Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Probleme beim einbinden einer DLL (https://www.delphipraxis.net/151407-probleme-beim-einbinden-einer-dll.html)

ryLIX 16. Mai 2010 23:57


Probleme beim einbinden einer DLL
 
Hi,
ich habe folgendes Problem.
Ich habe eine DLL eingebunden, der Code sieht so aus:
Delphi-Quellcode:
type TAPIRESULTFORMAT = (APIFORMAT_CHAR,
                           APIFORMAT_BYTE,
                           APIFORMAT_INTEGER,
                           APIFORMAT_WORD,
                           APIFORMAT_LONG,
                           APIFORMAT_DWORD,
                           APIFORMAT_TEXT,
                           APIFORMAT_BINARY,
                           APIFORMAT_REAL);

  function __apiEnd(): Boolean; stdcall; external 'api32.dll';
  function __apiInit(): Boolean; stdcall; external 'api32.dll';
  procedure __apiJob(ecu, job, para, result: PChar); stdcall; external 'api32.dll';
  function __apiResultBinary(buffer:Byte; bufferlen:Cardinal; buffersize:Cardinal; result:PChar; rset:Word): Boolean; stdcall; external 'api32.dll';
  function __apiResultByte(buffer:Byte;result:PChar; rset:Word): Boolean; stdcall; external 'api32.dll';
  function __apiResultChar(buffer:PChar; result:PChar; rset:Word): Boolean; stdcall; external 'api32.dll';
  function __apiResultDWord(buffer:Cardinal; result:PChar; rset:Word): Boolean; stdcall; external 'api32.dll';
  function __apiResultFormat(para:TAPIRESULTFORMAT; result:PChar; rset:Word): Boolean; stdcall; external 'api32.dll';
  function __apiResultInt(buffer:SmallInt; result:PChar; rset:Word): Boolean; stdcall; external 'api32.dll';
  function __apiResultLong(buffer:LongInt; result:PChar; rset:Word): Boolean; stdcall; external 'api32.dll';
  function __apiResultName(buffer:PChar; index:Word; rset:Word): Boolean; stdcall; external 'api32.dll';
  function __apiResultNumber(buffer:Word; rset:Word): Boolean; stdcall; external 'api32.dll';
  function __apiResultReal(buffer:Double; result:PChar; rset:Word): Boolean; stdcall; external 'api32.dll';
  function __apiResultSets(rsets:Word): Boolean; stdcall external 'api32.dll';
  function __apiResultText(buffer:PChar; result:PChar; rset:Word; format:PChar): Boolean; stdcall; external 'api32.dll';
  function __apiResultVar(variable:PChar): Boolean; stdcall; external 'api32.dll';
  function __apiResultWord(buffer:Word; result:PChar; rset:Word): Boolean; stdcall; external 'api32.dll';
Nun hab ich das Problem wenn ich das Programm starte bleibt es sofort im CPU Fenster hängen und wenn ich weiter laufen lasse bekomme ich diese Fehlermeldung:
Code:
Problemsignatur:
  Problemereignisname:   BEX
  Anwendungsname:   Project1.exe
  Anwendungsversion:   0.0.0.0
  Anwendungszeitstempel:   4bf0663d
  Fehlermodulname:   ntdll.dll
  Fehlermodulversion:   6.0.6002.18005
  Fehlermodulzeitstempel:   49e03821
  Ausnahmeoffset:   00009a3a
  Ausnahmecode:   c0000409
  Ausnahmedaten:   00000000
  Betriebsystemversion:   6.0.6002.2.2.0.256.1
  Gebietsschema-ID:   1031
  Zusatzinformation 1:   c024
  Zusatzinformation 2:   be9414e1f9cf32517da0d4512e5d2663
  Zusatzinformation 3:   c024
  Zusatzinformation 4:   be9414e1f9cf32517da0d4512e5d2663
Aber ich hab keine Ahnung warum bzw wo der Fehler liegt :(

daywalker9 17. Mai 2010 07:00

Re: Probleme beim einbinden einer DLL
 
Die DLL ist auch im Anwendungsverzeichnis oder in System32?

Bernhard Geyer 17. Mai 2010 08:03

Re: Probleme beim einbinden einer DLL
 
ersetze mal alle PChar durch PAnsiChar's. In D2009 ist der Datentyp PChar ein Unicodestring. Und ob deine API mit Unicode arbeitet ...

ryLIX 17. Mai 2010 15:57

Re: Probleme beim einbinden einer DLL
 
Dll ist im Anwendungsverzeichnis.

Das mit dem PChar teste ich gleich mal.

Tante Edit meint PAnsiChar bringt auf keinen Fall neue Fehler.
Ich hab nun noch ShareMem rausgeworfen und nun klappts auch.

Naja zumindestens sehe ich nun mal das Formular.
Aber wenn ich nun eine Api funktion aufrufe dann bekomme ich eine Zugriffsverletzung :(

Bei apiInit() wird bei dem C++ Header ein void Typ übergeben der nach Dr.Bobs Tutorial als Pointer übersetzt werden soll.
Wenn ich das richtig sehe muss hier ein Handle übergeben werden.

Jetzt bin ich mir aber nciht sicher welches Handle ich dort übergeben muss bzw wie ich dort an das richtige komme.

himitsu 17. Mai 2010 16:26

Re: Probleme beim einbinden einer DLL
 
Die DLL wird bestimmt gefunden, denn ohne dieses wäre die Anwendung gleich zu Beginn abgebrochen wurden, mit einer Meldung ala "DLL nicht gefunden".

[edit]
PS:
Delphi-Quellcode:
__apiResultBinary(buffer:Byte;
ist bestimmt ein PByte

ryLIX 17. Mai 2010 16:41

Re: Probleme beim einbinden einer DLL
 
Hab ich korrigiert.

In dem Header File sind eine der Parameter mit einem * gekennzeichnet.

z.B. :
Code:
apiGetConfig(const char *,char *);
Nun sollte man den const Parameter nicht weiter beachten habe ich gelesen in einem DLL Tutorial.
Aber der * wurde nicht weiter erähnt.

Bei Dr. Bobs Head Conversion wäre ein char = Char und char * = PChar
Also ist dann auch ein byte = Byte und byte * = PByte? :gruebel: :shock: :wiejetzt:

himitsu 17. Mai 2010 16:49

Re: Probleme beim einbinden einer DLL
 
typ = typ
typ * = pointer auf typ



Allerdings mußt du beachten, daß Char, PChar, String dynamische Typen sind,
bzw. es sind selber keine Typen, sondern Weiterleitungen zu anderen Typen.

Oftmals (in allen Delphi-Versionen bis 2007) entspricht dieses einem AnsiChar, PAnsiChar und AnsiString,
wärend Delphi 2009/2010 ja auf Unicode eingestellt ist ... somit wird dieses nach WideChar, PWideChar und WideString UnicodeString umgeleitet.

Blos weil dein Delphi jetzt auf Unicode eingestellt ist, ändert sich natürlich nicht gleich dei verwendete DLL.
(tja und nun rächt es sich, daß Viele bei der Deklaration von Schnittstellen keine festen Typen verwenden)

ryLIX 17. Mai 2010 18:07

Re: Probleme beim einbinden einer DLL
 
Ok das ist soweit klar.
Aber nun hab ich noch das Problem mit dem Handle.

Ich hab mir mal die .NET Version angeschaut die eigentlich auch nur ein Wrapper für die DLL ist die ich verwende.
Aber da werd ich aus der Zuweisung nicht schlau. :(

DeddyH 17. Mai 2010 19:21

Re: Probleme beim einbinden einer DLL
 
IIRC muss man bei einem void-Parameter gar nichts übergeben.

Christian Seehase 17. Mai 2010 21:24

Re: Probleme beim einbinden einer DLL
 
Moin Markus,

bei der Verwendung von Headern verwende ich immer const, auch bei Ausgabeparametern (in Delphi ist es allerdings üblich bei Ausgabeparametern var zu verwenden)

Begründung:
Bei C ist es üblich, dass bei Ausgabeparametern ein Pointer auf den Speicherbereich übergeben wird, in dem die Funktion den Wert ablegen kann. De Facto sind die Parameter also immer const.
Dadurch, dass ich diese Parameter mit const deklariere kann ich auch nil übergeben.
Gerade bei Funktionen der Windows-API kommt es häufig vor, dass man auch nil bei einem Ausgabeparameter angeben kann, wenn man den Rückgabewert nicht benötigt. Wird dieser dann, wie in Delphi üblich, als var deklariert, kann man dass nicht machen.

Ohne die Angabe const, var, out ist es ein Wert-Parameter, der als Kopie an die Funktion übergeben wird, so dass Änderungen des Wertes durch die Funktion nicht auf den Wert einer eventuell übergebenen Variablen durchschlagen.
Da dies bei C-Funktionen nicht vorkommen kann, macht es für mich auch keinen Sinn hier einen Wert-Parameter anzugeben.

Man kann es natürlich auch ohne const und ggf. mir var machen, aber das ist auch Geschmackssache.

ryLIX 17. Mai 2010 23:09

Re: Probleme beim einbinden einer DLL
 
Zitat:

Zitat von DeddyH
IIRC muss man bei einem void-Parameter gar nichts übergeben.

Dachte ich auch.

@Christian:
Danke für den Hinweis.

Ich hab jetzt grad mal beides probiert:
Nichts übergeben (Parameter komplett gestrichen) und Parameter auf const und nil übergeben.

Das einzige was sich verändert ist die Adresse wo die Zugriffsverletzung auftritt.

Wenn ich ohne Parameter aufrufe:

Zugriffsverletzung bei Adresse 1000105C in Modul 'api32.dll'. Schreiben von Adresse 0044B749

Wenn ich nil übergebe dann schlägt der Versuch beim schreiben auf die Adresse 00000000.

Ich der Dokumentation der API wird hier auch kein Parameter angegeben.
Deswegen würde ich auch darauf tippen das nichts übergeben wird.
Nur wie man sieht führt das auch nicht zum Erfolg.

Christian Seehase 17. Mai 2010 23:10

Re: Probleme beim einbinden einer DLL
 
Moin Markus,

bist Du sicher, dass die Aufrufkonvention stdcall richtig ist?
Es könnte, z.B., auch cdecl sein.

ryLIX 17. Mai 2010 23:18

Re: Probleme beim einbinden einer DLL
 
Zitat:

Zitat von Christian Seehase
Moin Markus,

bist Du sicher, dass die Aufrufkonvention stdcall richtig ist?
Es könnte, z.B., auch cdecl sein.

Der Header ist als extern "C" gekennzeichnet und die aufrufe laufen über FAR PASCAL.

Ich hab mal mit cdecl aufgerufen. Bringt aber auch keine Änderung.

Was mir aber noch aufgefallen ist in dem .NET Wrapper:
Delphi-Quellcode:
[DllImport('api32.dll', EntryPoint='__apiInit', CharSet=CharSet.Ansi)]
strict private class extern function __api32Init([Out] var A_0: Cardinal): boolean; static;
Hier ist der Parameter als OUT gekennzeichnet und vom Typ Cardinal.

Edit:
Um das vollständig zu halten hier noch der eigentliche Aufruf der importierten Funktion:
Delphi-Quellcode:
function API.apiInit: boolean;
begin
    Result := API.__api32Init(@(API.a))
end;
Delphi-Quellcode:
strict private class var a: Cardinal;

ryLIX 18. Mai 2010 17:42

Re: Probleme beim einbinden einer DLL
 
Ich bin noch mal ein wenig auf die Suche gegangen nach dem Fehler.
Dabei bin ich auch diese Seite gestoßen: http://rvelthuis.de/articles/article...ml#constparams

Dort heißt es:
Zitat:

One case that is really annoying is FAR PASCAL. You might expect this to be translated to far; pascal; but this is far from the truth. In 32-bit Windows, PASCAL seems to be #defined as __stdcall, and FAR as an empty macro (far is something that was necessary for segmented 16-bit DOS/Windows 3.x code, not for Windows 32-bit code). So to find out the correct calling convention may require some detective work. A good searching tool, like grep, can help you with this.
Ich hab nun zwei Header Files hier.
Eine Api.h und eine ApiDll.h, ich gehe nun mal davon aus das ich die ApiDLL.h beachten sollte da hier die Macros definiert sind.

Dort ist apiInit so definiert:
Code:
APIBOOL FAR PASCAL __apiInit(unsigned int far *handle);
:| Wie finde ich nun raus was ich zum aufruf der DLL verwenden muss?

Tante edit meint:
Oh es ist was passiert.
Ich hab den Beitrag mal weiter gelesen und bin auf diese Passage gestoßen:
Zitat:

Out parameters

If the header was translated with a MIDL compiler, you'll get hints like [in] or [out] etc. These are in fact quite helpful, since they tell you how the parameter will be used. An [in, out] parameter will probably be modified, so you should use var for these (please note my proviso above). An [out] parameter will be modified too, but can be uninitialized before it is passed. Normally, you could also use var here, but not if the parameter is a pointer to an interface type (please note, that these are translated as the type itself, since in Delphi these are implicit pointers types, just like objects). If you see an [out] or OUT comment or macro with an interface type, please declare these as out too. This is important for the reference counting mechanism.
Jetzt weiß ich ja aus dem Wrapper für .NEt das der Parameter dort als OUT gekennzeichnet ist.
Also hab ich nun folgendes probiert:
Delphi-Quellcode:
function __apiInit(Out handle:Pointer): Boolean; stdcall; external 'api32.dll';
Aufruf:
Delphi-Quellcode:
__apiInit(Handle) //Handle ist von Typ Pointer aber nicht initialisiert bzw belegt
Interessant ist nun das nichts passiert.
Also keine Fehlermeldung aber der Server den die Dll starten sollte startet auch nicht...

Edit²:
Ich sehe nun im CPU Fenster das die DLL geladen wird.
Ich hab nun auch herrausgefunden was ich als Parameter übergeben muss:
Zitat:

3. Applikations-Locking ueber Handle:
Die API-Schnittstellenfunktionen __apiInit/__apiInitExt vergeben als Ergebnis einen Handle vom Typ "unsigned int", welcher dann jeder anderen API-Schnittstellenfunktion als 1. Parameter uebergeben werden muss. Mit diesem Mechanismus des Applikations-Lockings kann nun von verschiedenen Prozessen auf EDIABAS zugegriffen werden, ohne API zwischenzeitlich zurueckzusetzen.
Voraussetzung hierfuer ist jedoch, dass die beteiligten Prozesse den Handle kennen.
Wie bekomme ich das korrekte Handle in Delphi?

ryLIX 20. Mai 2010 12:51

Re: Probleme beim einbinden einer DLL
 
*bump* :duck:
Keiner der mir helfen kann?

Stevie 20. Mai 2010 13:04

Re: Probleme beim einbinden einer DLL
 
Wenn Handle vom Typ unsigned int ist, müsste es doch in Delphi als Cardinal definiert werden und nicht als Pointer, oder?

ryLIX 20. Mai 2010 13:12

Re: Probleme beim einbinden einer DLL
 
Ich habe es auch schon mit dem Typ Cardinal versucht.
Seltsam ist das er in die DLL einsteigt und dort auch einiges läuft.
Was aber nur über das CPU Fenster sehen kann da ich keinen Quellcode zu der DLL habe.

Und ASM kann ich nicht. :(

Eigentlich müsste es so sein das nach dem Aufruf von apiInit der Komunikations Server startet.
Danach ist das ganze einsatzbereit und ich kann via apiJob Aufträge senden und die Ergebnisse via apiResult abrufen.

Nur leider startet der Komunikations Server nicht.


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:00 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz