Problem z DLL i stringami

0

Mam następujący problem:
Mam bibliotekę DLL, w której wyeksportowana jest funkcja tworząca obiekty pewnej klasy

(powiedzmy X) i zwracająca na nie interfejs, który jest przez nią implementowany.
W klasie tej (i interfejsie) jest dodatkowo następująca metoda:

procedure View(sg: TStringGrid);

Ma ona za zadanie wyświetlić informacje o obiekcie klasy X w obiekcie klasy TStringGrid.
W pliku *.exe, gdzie biblioteka ta jest dołączana znajduje się wywołanie tej metody

X1.View(Child.StringGrid1);

Niestety po jej wywołaniu pojawia się błąd. Przy uruchomieniu z poziomu pliku exe pojawia

się:


Debugger Exception Notification

Project xxx.exe raised exception class EInvalidPointer with message 'Invalid pointer

operation'. Process stopped. Use Step or Run to continue.

OK Help

natomiast przy uruchomieniu z poziomu dll


dll

Access violation at address 00401EEC in module 'xxx.exe'. Write of address 00000022.

OK

Gdy pozostwię tylko linie typu:

sg.FixedRows := 2;

a więc przekazujące liczby, błędy się nie pojawiają. Dopieru przy próbie typu

sg.Cells[0,0] := 'tekst';

Co może być powodem? Przekazywanie stringów?

0
ArtP napisał(a)

Co może być powodem? Przekazywanie stringów?

Najprawdopodobniej. Przeczytaj obfity komentarz, ktory Delphi dołacza automatycznie na początku DLL-a.

0

podaj chociaż pełną deklarację tej procedury z dllki i kod, w którym wywaołujesz ją z exeka. Podaj też listę uses, jaką masz w dll i exe (w pliku dpr)

0

Witam
Dzięki za informacje. Czytałem ten komentarz wiele razy i zawsze dołączałem ShareMem, ale tylko na początku unitu projektu.
Po dodaniu go do wszelkich pozostałych (tak na wszelki wypadek ;) ) zadziałało.

Pojawia się niestety inny błąd z którym postaram się uporać samodzielnie.

Pozdrawiam

0

Troszkę chaosu w tym pytaniu, po pierwsze skąd wziął się ten StringGrid, z DLL aplikacji, co dokładnei chcesz w nim umieścić jak wygląda wywołanie DLLa(najlepiej kod). Dla mnei trochę szalejesz po środowisku to wywala ci bzdury. Raczej nie przekazuje się rzeczy w ten sposób sg:TStringGrid dużo lepszym pomysłem jest psg:pointer, wówczas psg:=@SG. Przesyłasz adres twojego stringgriga a po drugiej stronie piszesz
var sg:TStringGrid;
sg:=TStringGrid(psg^);
masz przekazanie obiektu po adresie i przywiązanie do niego po 2 stronie.
Optymalnie jeśli masz w apliakcji swojego sg a przekazujesz do DLLa jego adres, tam go wypelniasz (ale wypelniasz od razu wlasciwego tego powolanego w aplikacji), nawet zwracaniem nie muszisz sie martwic, bo nie ma czego - wszystko juz jest bo DLL mając adress działał od razu na właściwym SG. Zamykasz DLLa odswieżasz okno (albo robi się to automatycznie) i masz co chciałeś (chyba).
Stringi też przekazuje się najlepiej w formie pchar-ów, nie ma sensu kopiować i przesyłać obiektów w tę i z powrotem skoro można przekazaćadres do obiektu. Stringi zwracam tylko i wyłacznie do obsługi błędów, zakładam w DLL kanał błędów i wszelkie błędy wyświetlają sie w oddzielnym miejscu (np. przeznaczonym do tego labelu) ale wówczas trzeba sporo namieszać.

0

Dawno tutaj nie zaglądałem...

Po pierwsze

Napisałeś:

Troszkę chaosu w tym pytaniu, po pierwsze skąd wziął się ten StringGrid, z DLL aplikacji, co dokładnei chcesz w nim umieścić jak wygląda wywołanie DLLa(najlepiej kod).

Chodziło mi o rozdzielenie modułów obliczeniowych (które umieściłem w bibliotece DLL) od aplikacji, która chce z nimi coś robić. W związku z tym DLL-ka eksportuje tylko jedną funkcję służącą do tworzenia obiektów i zwraca interfejs na obiekt, który stworzyła. Jej wywołanie wygląda następująco:

function CreateMyObject(ObjType: TObjectType): IMyObject; stdcall;

TObjectType jest typem obiektu, który chcę stworzyć, a IMyObject interfejsem na klasę bazową dla wszystkich obiektów w DLL-ce.
Po dołączeniu tej biblioteki do aplikacji mogę tworzyć obiekty w dll-ce np. tak:

...
var
  ob1: IObject1;
  ...
begin
  ob1 := CreateMyObject(Obiekt1) as IObject1;
  ...

, gdzie IObject1 jest interfejsem dziedziczącym z IMyObject
Skoro mam obiekt i mam jego interfejs to mogę wywoływać metody, które on implementuje np.:

  ...
  ob1.DoSomething;
  ...

Ponieważ zawartością tych obiektów są dane, które najłatwiej wyświetlić w formie tablicy ze stringami, czyli najlepiej w obiekcie typu TStringGrid, dlatego też jedną z metod interfejsu bazowego (IMyObject)jest następująca metoda:

procedure View(sg: TStringGrid); stdcall;

Dzięki niej obiekt może się sam wyświetlić w kontrolce TStringGrid.
W związku z tym w aplikacji (MDI) jest okno zawierające taki komponent o nazwie np. sg1. Okno to jest tworzone za każdym razem, kiedy użytkownik chce zobaczyć zawartość danego obiektu. Wówczas w odpowiednim miejscu programu następuje wywołanie tejże funkcji w następujący sposób:

  ...
  ob1.View(sg1);
  ...

, gdzie ob1 jest wcześniej wspomnianym interfejsem, a sg1 komponentem typu TStringGrid właśnie stworzonym w aplikacji, a nie w DLL-ce.

Ponieważ napisałeś:

Raczej nie przekazuje się rzeczy w ten sposób sg:TStringGrid dużo lepszym pomysłem jest psg:pointer, wówczas psg:=@SG. Przesyłasz adres twojego stringgriga a po drugiej stronie piszesz
var sg:TStringGrid;
sg:=TStringGrid(psg^);
masz przekazanie obiektu po adresie i przywiązanie do niego po 2 stronie.

Takie wywołanie jakiego użyłem, z tego co wiem nie powoduje przekazania obiektu do dll-ki (bo sg1 jest referencją na istniejący gdzieś w pamięci obiekt typu TStringGrid), tylko przekazanie adresu tego obiektu.
W związku z tym nie widzę sensu tego co napisałeś, a mianowicie konieczności konwertowania jeszcze tej referencji na wskaźnik, a potem odwrotnie, tj. wskaźnika na referencję. No i oczywiście nie muszę niczego rzutować, co przy programowaniu obiektowym nie uważam za dobry pomysł i staram się unikać, gdzie się tylko da.

Napisałeś też:

Optymalnie jeśli masz w apliakcji swojego sg a przekazujesz do DLLa jego adres, tam go wypelniasz (ale wypelniasz od razu wlasciwego tego powolanego w aplikacji), nawet zwracaniem nie muszisz sie martwic, bo nie ma czego - wszystko juz jest bo DLL mając adress działał od razu na właściwym SG. Zamykasz DLLa odswieżasz okno (albo robi się to automatycznie) i masz co chciałeś (chyba).

Efekt działania tego co zrobiłem jest dokładnie taki jak napisałeś. Tylko bez konieczności rzutowania wskaźników.

Po drugie

Napisałeś:

Stringi też przekazuje się najlepiej w formie pchar-ów, nie ma sensu kopiować i przesyłać obiektów w tę i z powrotem skoro można przekazaćadres do obiektu.

Jeśli przeczytałeś to co wcześniej napisałem to zapewne zauważyłeś, że nigdzie nie przekazuję żadnych obiektów, tylko referencje na nie (czyli adresy) bądź też interfejsy.
Poza tym nie wyobrażam sobie sensowniejszego zapisu wewnątrz procedury znajdującej się w Dll-ce typu:

procedure View(sg: TStringGrid); stdcall;

niż

sg.Cells[0,0] := 'tekst';

Mam nadzieję, że nikogo nie zanudziłem tym wywodem :)

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