konwersja dll z x32 na x64

0

mam dll która zawsze była kompilowana jako 32bitowa
Teraz próbuję ją uruchomić na 64 bit.
I co ciekawe.
Jak uruchamiam dll z mojego exeka z Dephi (x64) to program wylicza w takim jednym miejscy wskaźniki 32bitowe.a jak uruchamiam z exeka kompilowanego w .NET (też x64) to w tym samym miejscu już mam adresy 64bitowe

wcześniej było tak:


PValTablesInfo = ^TValTablesInfo;
  TValTablesInfo = packed record
    Addr1,Addr2:Pointer;
    Size:integer;
    DynArr:boolean;
  end;


New(x);
  x^.Addr1:=Addr1;
  x^.Addr2:=Addr2;
  x^.Size:=Round(Size/4);
  x^.DynArr:=DynArr;
  dataset.FieldByName(FieldName).Tag:=Integer(x);

Generalnie do pola w datasecie podłączam sobie jakiś rekord którego później gdzieś używam na zamknięciu tego dataseta było tak:

for i:=0 to dataset.FieldCount-1 do
      if (dataset.Fields[i].Tag<>0) then
      begin
      x:=Ptr(dataset.Fields[i].Tag);
      Dispose(x);
      dataset.Fields[i].Tag:=0;
    end;

I to zawsze działało.

natomiast teraz jak przerabiam to na wersję x64 to zamieniłem linię
dataset.FieldByName(FieldName).Tag:=Integer(x);
na
dataset.FieldByName(FieldName).Tag:=Int64(x);

później wysypywał się na
x:=Ptr(dataset.Fields[i].Tag)
więc zmieniłem na
x:=Addr(dataset.Fields[i].Tag);

i tu wszystko wydaje się ok, ale niestety na Dispose(x) wywala Invalid Pointer Operation.

Nie wiem dlaczego.
Poradzicie coś?

0

Może coś pomoże https://docwiki.embarcadero.com/RADStudio/Sydney/en/Converting_32-bit_Delphi_Applications_to_64-bit_Windows
Ogólnie musisz rzutować pointer na liczbę? Czy do taga nie da się czasem zapisać czegokolwiek? Nie możesz tam od razu zapisać po prostu pointer? Podejrzane się wydaje też

x^.Size:=Round(Size/4);

czemu Size / 4? Do czego tego potem używasz? Jeśli to rozmiar wskaźnika to pewnie powinno być Size / 8

1

A jesteś pewien ze tam powinno byc "Addr(...)" ?
ja bym zrobił tak:

x:=pointer(dataset.Fields[i].Tag)
0

Nawet jeżeli jest to kwestia problemów w kodzie to czemu dllka odpalona w delphi x64 jest ciągle ok? Przecież powinny wystąpić podobne problemy

0

@Michał Częczek: można wiedzieć po co wrzucasz adresy wskaźników do bazy? Przecież to nie ma prawa działać.

PS: Addr to intrinsic i służy do zwracenia adresu, tak samo jak operator @.

0
Adamek Adam napisał(a):

A jesteś pewien ze tam powinno byc "Addr(...)" ?
ja bym zrobił tak:

x:=pointer(dataset.Fields[i].Tag)

Można i tak, ale używając Addr lub @ mam dostęp do rekordu, którego adres jest w tym tagu, więc to działa i raczej nie jest przyczyną problemu ze zwalnianiem pamięci. Ale oczywiście przetestuję wszystkie sugestie.

2
My Razem napisał(a):

natomiast teraz jak przerabiam to na wersję x64 to zamieniłem linię
dataset.FieldByName(FieldName).Tag:=Integer(x);
na
dataset.FieldByName(FieldName).Tag:=Int64(x);

Raczej powinno się pisać kod tak by się kompilował w x64 i x86 bez potrzeby zmieniania za każdym razem. Tutaj powinno chyba być NativeInt(x).

A jeśli coś ci nie działa w .NET to pokaż jak to importujesz w .NET, bo tam jest mnóstwo niuansów.

No i przechowywanie wskaźników pomiędzy uruchomieniami to jest WTF. Zwłaszcza że Microsoft stosuje Address_space_layout_randomization czyli celowe mieszanie wskaźników pomiędzy jednym a drugim uruchomieniem programu tak by utrudniać exploity - raz ustalone wskaźniki się za drugim razem już nie zgadzają. Być może to właśnie zaobserwowałeś.

0

Nie powinno być problemu z wrzuceniem wskaźnika jako liczby do bazy (do rzutowania jest PtrInt), a później odczytania go i zwolnienia bloku pamięci spod adresu, który dana liczba wskazuje. Przykład:

{$MODE OBJFPC}{$LONGSTRINGS ON}
{$OPTIMIZATION OFF}

type
  PFoo = ^TFoo;
  TFoo = record
    Tag:  Integer;
    Name: String;
  end;
var
  FooAddress: PFoo;
  IntAddress: PtrInt;
begin
  New(FooAddress);
  FooAddress^.Tag  := 16;
  FooAddress^.Name := 'Foo';

  WriteLn('before:');
  WriteLn('  Tag:  ', FooAddress^.Tag);
  WriteLn('  Name: ', FooAddress^.Name, LineEnding);

  IntAddress := PtrInt(FooAddress);
  FooAddress := nil;

  WriteLn('int: 0x', HexStr(IntAddress, SizeOf(IntAddress) * 2));
  WriteLn('foo: 0x', HexStr(PtrInt(FooAddress), SizeOf(FooAddress) * 2), LineEnding);

  FooAddress := Pointer(IntAddress);

  WriteLn('after:');
  WriteLn('  Tag:  ', FooAddress^.Tag);
  WriteLn('  Name: ', FooAddress^.Name, LineEnding);

  Dispose(FooAddress);
end.

Przykładowe wyjście:

before:
  Tag:  16
  Name: Foo

int: 0x0000000001562830
foo: 0x0000000000000000

after:
  Tag:  16
  Name: Foo

Tak jak @Azarien wspomniał, przy każdym uruchomieniu programu, layout pamięci się zmienia, więc takie triki będą działać jedynie w trakcie danej sesji, a po jej wygaśnięciu (zamknięciu programu), adres znajdujący się w bazie staje się nieaktualny i nie można na nim polegać.

Rzutowanie adresu na liczbę wykorzystuje się np. w funkcjach z Win32 API, do przesyłania wskaźników w parametrach komunikatów np. w funkcji SendMessage (bo te są liczbowe, typu LONG_PTR, czyli PtrInt) wewnątrz programu i tylko w obrębie danej sesji. Przechowywanie tych adresów w formie liczb i wykorzystywanie pomiędzy sesjami jest po prostu niepoprawne i nie ma prawa działać w żadnym nowoczesnym systemie operacyjnym.

Dokładnie to samo można również robić z referencjami obiektów, bo te są zwykłymi wskaźnikami.

0

Wszystkie odpowiedzi skupiają się na wątpliwym przechowywaniu wskaźników w bazie. Niezależnie jak bardzo jest to podejrzane, to nie jest źródłem problemu.
Wersja 32 bitową dllki działa poprawnie zarówno wołana w dotnecie jaki i w aplikacji delhpi. Wersja 64 bitową nie działa tylko w dotnecie x64 a w Delphi x64 działa co wskazuje, że przyczyna problemu jest inna.
Jeśli chodzi o dotneta to dllka tam jest wołana za pomocą P/Invoke (DllImport).

0

Jedno mnie zastanawia:

My Razem napisał(a):

Jak uruchamiam dll z mojego exeka z Dephi (x64) to program wylicza w takim jednym miejscy wskaźniki 32bitowe.

Co to znaczy „wylicza wskaźniki 32-bitowe” i jak to możliwe, że są 32-bitowe? Nie może tak robić, jeśli DLL jest 64-bitowe. A jeśli jest 32-bitowe, to nie dałbyś rady użyć tej biblioteki z poziomu aplikacji 64-bitowej — bitness musi być jednakowy po obu stronach.

0

Wyświetl sobie w disassemblerze tą dll, sprawdź sobie w debuggerze jakie są adresy do tych bibliotek załadowanych w pamięci, wyświetl sobie mapę pamięci i zobacz czy są tam te same co w disassemblerze, te instrukcje z dll tzn. czy to ta sama, to samo w C#, prześledź jaki problem, może pod zły adres wskakuje, bo gdzieś indziej relatywny adres został źle obliczony.

Ja kiedyś jak debugowałem sobie własny kernel, to linkier gdzieś przesunął obiekty w pamięci, potem ja je ładowałem jeszcze w jakieś inne miejsce to na końcu gdzieś jakieś relatywne przesunięcie się zgubiło i takim debugowaniem błąd znaleziony w parę minut, a patrząc w kod nie miało się pojęcia gdzie jest błąd.

Nie wiem czemu tak dużo ludzi boi się tych debuggerów, a to najskuteczniejsza broń w walce z błędami.

0

Wszyscy chyba źle zrozumieliście że ja przechowuję te wskaźniki w bazie. Przecież tak nie jest. Ja je tylko dołączam do danego pola w datasecie, a nie w bazie.
Na początku do dataseta dodaję wskaźnik, a po jego zamknięciu zwalniam go.
Więc to wszystko dzieje się w ramach jednego wątku.

2

No nie, dobrze zrozumieliśmy, tylko zanim się rozpędzisz i napiszesz sporo kodu wykorzystującego takie cuda, lepiej jest od razu przestrzec Cię, abyś czegoś takiego nie używał, bo to nie jest zbyt bezpieczne, a w dodatku zależne od technologii. Nie miej więc za złe innym, że wyjaśniają dlaczego to zły pomysł. ;)

A wracając do meritum — w Pascalu możesz sobie robić takie rzeczy, bo zarządzanie pamięcią dynamicznie alokowanych leży po stronie programisty. Co zaalokowałeś, musisz sam zwolnić. To prosta zasada, łatwa do ogarnięcia i dająca mnóstwo możliwości i wolności.

Natomiast .NET wykorzystuje GC — jeśli coś stworzysz, przerobisz wskaźnik/referencję na liczbę, a potem przestaniesz używać wskaźnika/referencji, to GC najpewniej zwolni blok (bo już nie jest używany) i Twoja liczba stanie się bezwartościowa. IMO to jest powodem jednoczesnego działania programu w Delphi i niedziałania w .NET, ale nie używam technologii z GC, więc musisz to sobie sam wybadać debuggerem.

1
My Razem napisał(a):

Wszyscy chyba źle zrozumieliście że ja przechowuję te wskaźniki w bazie. Przecież tak nie jest. Ja je tylko dołączam do danego pola w datasecie, a nie w bazie.
Na początku do dataseta dodaję wskaźnik, a po jego zamknięciu zwalniam go.
Więc to wszystko dzieje się w ramach jednego wątku.

O!
Całkiem brazylijski serial się robi, zaledwie w odc. 2731 wiemy kto jest czyim bratem, choć nadal nie wiemy, czy tego samego ojca/matki

W normalnym języku normalnie użytym bym napisał klasę z pointerem/referencją, ją umieścił w kolekcji ... żadnego rzutowania adresu na integer, co więcej adres w większości języków by był bezpieczny co do typu (tak, wiem, Delphi też posiada te możliwosci, tylko ... myślę sobie ... dlaczego nie sa używane???)

Ale tak to jest, jak zna się tylko zmienne typu TEdit i tablice TDataset (sorry, ale za dużo tego widziałem, również w całkiem wysoko płatnej komercji)

aha, moje streszczenie serialu: jesz zupę widelcem, i stwierdzasz że jedna (krupnik /32b) przynajmniej częściowo daje się jeść, a druga(barszczyk /64b) już nie.

furious programming napisał(a):

No nie, dobrze zrozumieliśmy, tylko zanim się rozpędzisz i napiszesz sporo kodu wykorzystującego takie cuda, lepiej jest od razu przestrzec Cię, abyś czegoś takiego nie używał, bo to nie jest zbyt bezpieczne, a w dodatku zależne od technologii. Nie miej więc za złe innym, że wyjaśniają dlaczego to zły pomysł. ;)

+1
Chore, chore, chore. Nie pierwsze, nie ostatnie, gdzie programista tej platformy sam sobie sterem, żeglarzem i okrętem.

1
ZrobieDobrze napisał(a):

Chore, chore, chore. Nie pierwsze, nie ostatnie, gdzie programista tej platformy sam sobie sterem, żeglarzem i okrętem.

Nie wiem o którą „platformę” Ci chodzi, ale konieczność/możliwość dbania o większą ilość aspektów związanych z działaniem kodu, nie jest problemem, a zaletą. To nie tylko daje znacznie większą pulę możliwości, ale też uczy dobrych nawyków, w tym myślenia przy pisaniu kodu oraz dbania o każdy jego szczegół.

Pascale, tak samo jak C, są bardzo proste do zrozumienia — możesz nie umieć zbyt dobrze programować, ale patrząc na ich kod widzi się co się dzieje. A kiedy bierze się np. Javę, nie ma się pojęcia jak kod faktycznie działa, bo jest kupą abstrakcji i cukru składniowego — do tego jeszcze GC zarządza pamięcią, więc jest jeszcze trudniej zrozumieć co się dzieje. Nie znając języka, niewiele się zrozumie.

0
furious programming napisał(a):
ZrobieDobrze napisał(a):

Chore, chore, chore. Nie pierwsze, nie ostatnie, gdzie programista tej platformy sam sobie sterem, żeglarzem i okrętem.

Nie wiem o którą „platformę” Ci chodzi, ale konieczność/możliwość dbania o większą ilość aspektów związanych z działaniem kodu, nie jest problemem, a zaletą. To nie tylko daje znacznie większą pulę możliwości, ale też uczy dobrych nawyków, w tym myślenia przy pisaniu kodu oraz dbania o każdy jego szczegół.

Pascale, tak samo jak C, są bardzo proste do zrozumienia — możesz nie umieć zbyt dobrze programować, ale patrząc na ich kod widzi się co się dzieje. A kiedy bierze się np. Javę, nie ma się pojęcia jak kod faktycznie działa, bo jest kupą abstrakcji i cukru składniowego — do tego jeszcze GC zarządza pamięcią, więc jest jeszcze trudniej zrozumieć co się dzieje. Nie znając języka, niewiele się zrozumie.

Klasyczny proceduralny Pascal jest jest jednym z genialniejszych języków, zresztą nie jedyny ładny język Niklausa Wirtha.
Problemy są dwa:
a) masakra bez trzymanki jaka się dokonał przy wynajdywaniu Object Pascala. Jakieś podejmowanie zagadnień których nie nie potrzebował o galaktykę przed realnym rynkiem, na pograniczu obiektowego / modularnego. Twierdzisz, z e Java jest trudna??? to jaki jest Obect Pascal ???. Problem w Javie nie jest w rdzeniu języka, tylko w dalszym rozwoju (programowanie przez adnotacje, przerośnięte kontenery)
b) dlaczego w real-life w tym ponoć dobrym ekosystemie, komerycjni programiści powołują TEdit zamiast zmienną, zmieną wyłącznie globalną (fabryka tak daje, wiec widocznie tak ma być). Senior prokektu BC++ w którym miałem wspomóc ... KASOWAŁ zmienne lokalne, bo ich nie rozumiał. Odkopiowywał przed wejściem globalną w innej globalnej, żeby ją przywrócić, ale nie był w stanie mentalnie inaczej (facet z naprawde wysokiej półki finansowej)

Może ja mam pecha większego od innych, ale spomiędzy biorących pieniądze za klikanie w Borlandach / Embecadero spotkałem 3/4 autorów koszmarów.
Odpowiem Ci Twoimi słowami: w ogóle nie pojawia się w nich pytanie o zrozumienie języka *). Nie mówię o jednym id... na jakiego bym trafił, ale 2/3-3/4.
W moim ujęciu "ekosystemu języka" zarazem język urabia swoich wyznawców, ale i język jest urabiany pod statystycznie dominującego wyznawcę.
Obrońcy PHP by chcieli przekazać, że v7/v8 radykalnie odnawia styl języka, chciałbym w to wierzyć.
Ale tu jest tylko kontunuacja (z poszerznie runtimów),
... i coraz mniejsza ilość finansujących to musi finansować.

*) jakbym sam miał biegle zrozumieć Object Pascal, i zdać interview o 3 w nocy, to chyba bym się zastrzelił wcześniej. Dlaczego cholera nie mam żadnych problemów z Javą?

1
ZrobieDobrze napisał(a):

a) masakra bez trzymanki jaka się dokonał przy wynajdywaniu Object Pascala.

Co to jest „masakra bez trzymanki” w kontekście języka programowania? Masz na myśli ręczne zarządzanie pamięcią (brak GC)? No wybacz, ale pamiętanie o zwalnianiu zaalokowanych bloków pamięci jest tak samo naturalne, jak wycieranie zadka po zrobieniu kupy. Jeśli ktoś o tak fundamentalnych czynnościach nie pamięta, to będzie miał… przesrane. :D

Jakieś podejmowanie zagadnień których nie nie potrzebował o galaktykę przed realnym rynkiem, na pograniczu obiektowego / modularnego.

Pascal nie powołał do życia programowania obiektowego (modularnego też nie). Wprowadzenie wsparcia tych paradygmatów było naturalną ścieżką rozwoju języków programowania i odpowiedzią na potrzeby rynku.

Twierdzisz, z e Java jest trudna???

Nie napisałem, że jest trudna, a że często kod pisany w takich językach jak np. Java nie wygląda jak składnia języka programowania, a jak lista magicznych zaklęć Harrego Pottera, których zrozumienie jest niemal niemożliwe bez wertowania dokumentacji.

Problem w Javie nie jest w rdzeniu języka, tylko w dalszym rozwoju (programowanie przez adnotacje, przerośnięte kontenery)

Dokładnie to samo napisałeś w kontekście Pascala.

b) dlaczego w real-life w tym ponoć dobrym ekosystemie, komerycjni programiści powołują TEdit zamiast zmienną, zmieną wyłącznie globalną (fabryka tak daje, wiec widocznie tak ma być). Senior prokektu BC++ w którym miałem wspomóc ... KASOWAŁ zmienne lokalne, bo ich nie rozumiał. Odkopiowywał przed wejściem globalną w innej globalnej, żeby ją przywrócić, ale nie był w stanie mentalnie inaczej (facet z naprawde wysokiej półki finansowej)

Nie ma to jak argumentować swoje stanowisko dowodami anegdotycznymi.

Jeśli będziesz oceniać technologię przez pryzmat jakości kodu produkowanego przez najgorszych ”specjalistów”, to nigdy nie dostrzeżesz prawdziwej jej jakości. To tak jakbyś oceniał jakość samochodu tylko i wyłącznie po stylu jazdy jego użytkownika (w dodatku bardzo kiepskiego użytkownika) — no bez sensu.

Kiedyś też byłem zajawiony na obiektowość, klasy, interfejsy, był hype na generyki, przeładowywanie operatorów, makra i inne coda na kiju. Ale to wszystko bullshit — wiele tych wspaniałych ficzerów tylko mnoży abstrakcje i utrudnia zrozumienie przepływu sterowania. Kilka miesięcy temu zacząłem pracę nad projektem swojej gry, wróciłem do programowania czysto strukturalno-proceduralnego i czuję się jak nowo narodzony — jakbym odkrył programowanie na nowo. Tylko proste funkcje, strukturki i wskaźniki — nic więcej mi do szczęścia nie potrzeba. ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1