Delphi *.dll in VB einbinden

  • VB.NET

Es gibt 39 Antworten in diesem Thema. Der letzte Beitrag () ist von Gonger96.

    Delphi *.dll in VB einbinden

    Hallo,

    habe heute eine erneute Frage, da ich mal wieder mit einem Problem nicht zurecht komme.

    Ich habe hier eine DLL welche mit Delphi 2 geschrieben wurde und folgenden Inhalt hat:

    Spoiler anzeigen

    Quellcode

    1. PROGRAM Dlldemo;
    2. (* TC in 2'97. Delphi2.
    3. This program demonstrates the usage of the interface dll RAC.DLL,
    4. 32 bit version).
    5. IMPORTANT: This project has to be compiled as an console application. Use
    6. Project|Options (linker page) to instruct your compiler.
    7. *)
    8. (* Compiler settings used *)
    9. (*$DebugInfo On*)
    10. (*$LocalSymbols On*)
    11. (*$ReferenceInfo On*)
    12. (*$Align Off*) (* No word align *)
    13. (*$Optimization On*)
    14. (*$SafeDivide On*)
    15. (*$StackFrames Off*)
    16. (*$IOChecks Off*)
    17. (*$RangeChecks Off*)
    18. (*$StackChecks On*)
    19. (*$OverflowChecks Off*)
    20. (*$VarStringChecks Off*)
    21. (*$OpenStrings On*)
    22. (*$TypedAddress Off*)
    23. (*$BoolEval Off*)
    24. (*$ExtendedSyntax On*)
    25. (*$WriteableConst Off*)
    26. (*$LongStrings On*)
    27. USES
    28. SysUtils;
    29. PROCEDURE pGetVersionNr(
    30. var AVersionNr : word); pascal; external 'rac.dll';
    31. PROCEDURE pCheckDatabaseFormat(
    32. ADriveInfo : pointer;
    33. var AResult : word;
    34. var AErrorCode : SmallInt); pascal; external 'rac.dll';
    35. PROCEDURE pGetCallInfo(DriveInfo: pointer; ACall: pointer; AInfo: pointer;
    36. var AErrorCode: SmallInt); pascal; external 'rac.dll';
    37. VAR
    38. Call : ARRAY[0..6] OF char;
    39. Info : ARRAY[0..500] OF char;
    40. DriveInfo : ARRAY[0..80] OF char;
    41. ErrorCode : SmallInt;
    42. i : SmallInt;
    43. VersionNr : word;
    44. CheckResult : word;
    45. BEGIN
    46. sGetVersionNr(VersionNr);
    47. writeln('DLL VersionNr: ', VersionNr);
    48. write('DriveInfo: ');
    49. readln(DriveInfo);
    50. sCheckDataBaseFormat(@DriveInfo, CheckResult, ErrorCode);
    51. IF (ErrorCode = 0) AND (CheckResult = 0) THEN begin
    52. writeln('CheckDatabaseFormat: ok.');
    53. end
    54. ELSE begin
    55. IF ErrorCode <> 0 THEN begin
    56. writeln('ErrorCode: ', ErrorCode);
    57. end
    58. ELSE begin
    59. writeln('CheckDatabaseFormat: DLL does not match with CD');
    60. end;
    61. end;
    62. write('Call: ');
    63. readln(Call);
    64. writeln('Call: >' + Call + '<');
    65. sGetCallInfo(@DriveInfo, @Call, @Info, ErrorCode);
    66. IF ErrorCode = 0 THEN begin
    67. writeln('ok.');
    68. i := 0;
    69. WHILE Info[i] <> #0 DO begin
    70. IF byte(Info[i]) < 32 THEN begin
    71. write(byte(Info[i]), ': >');
    72. end
    73. ELSE begin
    74. write(Info[i]);
    75. end;
    76. inc(i);
    77. IF byte(Info[i]) < 32 THEN begin
    78. writeln('<');
    79. end;
    80. end;
    81. writeln;
    82. end
    83. ELSE begin
    84. writeln('ErrorCode: ', ErrorCode);
    85. end;
    86. write('Press Enter to quit...');
    87. readln;
    88. end.


    Nun habe ich in Visual Basic versucht, jene Dll wie folgt einzubinden:

    VB.NET-Quellcode

    1. Private Declare Sub sGetVersionNr Lib "E:\win32\rac" (ByRef AVersionNr As Integer)
    2. Private Declare Sub sCheckDatabaseFormat Lib "E:\win32\rac" (ByVal ADriveInfo As String, ByRef AResult As Integer, ByRef AErrorCode As Integer)
    3. Private Declare Sub sGetCallInfo Lib "E:\win32\rac" (ByVal ADriveInfo As String, ByVal ACall As String, ByVal AInfo As String, ByRef AErrorCode As Integer)


    aber wenn ich mir dann etwas anzeigen lassen möchte, bekomme ich mit z.B.:

    VB.NET-Quellcode

    1. MessageBox.Show(sGetVersionNr(AVersionNr:=), "Test", _
    2. MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk)


    Erscheint in der MessageBox dann

    Quellcode

    1. {sGetVersionNr(AVersionNr:=(0))}


    Und VB meldet, das da noch ein Ausdruck fehlt.

    das gleiche auch mit "sCheckDatabaseFormat" und "sGetCallInfo".

    Könnte mir jemand bitte weiter helfen, stehe da auf dem Schlauch.

    Danke schön.

    Sanweb schrieb:

    Und VB meldet, das da noch ein Ausdruck fehlt.
    Was meldet denn VB noch so?
    Was für eine Speichereiheit ist ein Delphi-word?
    Int16 oder Int32?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Wäre es nicht sinnvoller einfach das bisschen Delphi zu übersetzen? Außerdem steht dort: This project has to be compiled as an console application. Wo soll das ne DLL sein?
    Andere Frage, musst du nicht eine Referenz mit reingeben ?
    So ?

    VB.NET-Quellcode

    1. Dim refint As Integer = 0
    2. sGetVersionNr(refint)
    3. MessageBox.Show(refint.ToString)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Gonger96“ ()

    RodFromGermany schrieb:

    Sanweb schrieb:

    Und VB meldet, das da noch ein Ausdruck fehlt.
    Was meldet denn VB noch so?
    Was für eine Speichereiheit ist ein Delphi-word?
    Int16 oder Int32?


    Sorry, ich vergaß noch die Doku meinem ersten Beitrag beizufügen:
    Spoiler anzeigen
    TECHNICAL INFORMATION about RAC.DLL


    RAC.DLL is the 32 bit version (for windows 95, NT, and up) of the
    DLL for accessing the Database on CD-ROM.


    THE DLL

    RAC.DLL (the DLL) grants access to the callbook on CD ROM.
    The input the DLL needs is
    * where to find the CD ROM and
    * the call you are looking for.
    The output the DLL delivers is the complete entry that belongs to the call.
    If the call could not be found, an error is returned.

    Basically, the DLL exports three types of routines:
    xGetVersionNr, xCheckDatabaseFormat and xGetCallInfo,
    where "x" stands for one of the letters "c", "p", "r" or "s".
    This results in a total of twelve routines exported by the DLL.

    Each version of xGetVersionNr etc. performs the same task, the difference
    is only the calling convention used by the routine:

    First letter Calling convention
    "c" cdecl
    "p" pascal
    "r" register
    "s" stdcall

    The calling convention determines the method used for passing parameters
    to procedures and functions.

    Calling conventions differs in three areas:
    Order of passing parameters
    Responsibility for removing parameters from the stack ("cleanup")
    Use of registers for passing parameters

    The register and pascal conventions pass parameters from left to right,
    that is the leftmost parameter is evaluated and passed first and the
    rightmost parameter is evaluated and passed last. The cdecl and stdcall
    conventions pass parameters from right to left. For all conventions except
    cdecl, the procedure or function removes parameters from the stack upon
    returning. With the cdecl convention, the caller must remove parameters
    from the stack when the call returns. The register convention uses up to
    three CPU registers to pass parameters, whereas the other conventions always
    pass all parameters on the stack.

    The calling conventions are summarized in the following table.

    Directive Order Cleanup Registers
    register Left-to-right Function Yes
    pascal Left-to-right Function No
    cdecl Right-to-left Caller No
    stdcall Right-to-left Function No

    The register convention is by far the most efficient, since it often
    avoids the creation of a stack frame. The pascal and cdecl conventions
    are mostly useful for calling routines in dynamic-link libraries written
    in C, C++, or other languages. The stdcall convention is used for calling
    Windows API routines.


    Some programming languages or programs granting access to DLLs may not offer
    to choose a calling convention. In such a case, try to find out the used
    method (cdecl will be a good guess to start with).

    Examples:
    To use the cdecl calling convention routines, use
    cGetVersionNr,
    cGetCallInfo and
    cCheckDatabaseFormat.

    To use the pascal calling convention routines, use
    pGetVersionNr,
    pGetCallInfo and
    pCheckDatabaseFormat.



    Notes:
    * In the text below, all routines are referenced like "xGetVersionNr". Replace
    the "x" by one of the letters "c", "p", "r" or "s", as descripted above.

    * Word means 16 bit unsigned integer, SmallInt means 16 bit signed integer.


    PROCEDURE xGetVersionNr(
    var AVersionNr : word);

    Returns in AVersionNr the current version number for the DLL. For this
    DLL, the number is 8.


    PROCEDURE xGetCallInfo(
    ADriveInfo : pointer;
    ACall : pointer;
    AInfo : pointer;
    var AErrorCode : SmallInt);

    Returns in AInfo the information for the call given in ACall.

    ADriveInfo, ACall and AInfo are pointers to zero-terminated strings. These
    strings has to exist at the moment of calling xGetCallInfo. The calling
    program is responsible for creating them. AInfo must be long enough to
    comfort xGetCallInfo (at least 400 characters).
    Note: "Length of AInfo" refers to the length of the string AInfo points at.
    ADriveInfo and ACall are treated in the same manner for short.

    In ADriveInfo the procedure expects the path to the CD ROM drive. Use
    "G:\"
    if "G:" designates the CD ROM drive with the callbook CD ROM.
    Keep in mind that this information is a *must* and the calling program
    has to know it.
    Note: If the active directory on drive G: is not the root, ADriveInfo = "G:"
    will lead to an error 3. So always use "G:\".
    The calling program has to ensure that the length of ADriveInfo does not
    exceed 80 characters.

    ACall contains the call you are looking for, all letters in lower case,
    no additional spaces etc. The calling program has to ensure that ACall is
    not longer than 15 characters. However, there is no call longer than 6
    characters in the database.

    In AErrorCode, xGetCallInfo delivers you an information about the success
    of the search. For a successful search (ACall have been found) it is
    zero. All other values indicate an error. Some meanings of AErrorCode:

    -5: Internal error
    -4: Decode error
    -3: Wrong CD: DLL does not match the CD inserted- ask for a new DLL
    -2: ACall or AInfo having strange values (i.e. nil, or ACall is empty)
    2: File not found
    3: Path not found
    5: File access denied
    1101: ACall is not in the database.
    32000: Error allocating memory

    xGetCallInfo delivers the data of a successful search in AInfo. For
    AErrorCode <> 0, it is undefined.
    In AInfo, the information is structured in fields.

    An ascii null indicates the end of the record. The field deliminators
    are always added, so an empty field should look like this: ...#7#8...
    (#7 means ascii character 7).


    PROCEDURE xCheckDatabaseFormat(
    ADriveInfo : pointer;
    var AResult : word;
    var AErrorCode : SmallInt); export;

    Resturns in AResult, whether this DLL will work with the CD ROM specified
    in ADriveInfo.

    ADriveInfo and AErrorCode are used in the same manner as in xGetCallInfo.
    Like in xGetCallInfo, AResult is undefined for AErrorCode <> 0.
    List of error codes is given in the documentation of xGetCall.

    Values the routines return in AResult:
    0: ok.
    1: Wrong database format detected. Ask for a new DLL. Usage of
    xGetCallInfo will then lead to an error.



    PROGRAM DLLDemo

    The program DLLDemo demonstrates the usage of the DLL and the
    structure of AInfo. The sourcecode (Delphi 2.0) of DLLDemo (DLLDemo.dpr)
    is on the CD, too.


    Mangafreak1995 schrieb:

    Wäre es nicht sinnvoller einfach das bisschen Delphi zu übersetzen? Außerdem steht dort: This project has to be compiled as an console application. Wo soll das ne DLL sein?

    Wäre nicht sinnvoll, da das "bischen" Delphi nur eine Demo ist und nicht den Originalinhalt der eigentlichen *.dll beinhaltet. Siehe diesem Beitrag beigefügte Doku ganz am Ende.

    Die CD beinhaltet eine DBISAM 1.0 Datennbank, deren Inhalt verschlüsselt ist (bereits mit einem DBISAM Viewer Tool heraus gefunden) und man mittels der beigefügten DLL und einer selbst erstellten Applikation auf darin enthaltene Datensätze zugreifen kann, ohne die komplette Datenbank auslesen zu können. Beispiel: Für "Call" gibt man einen Namen ein und bekommt nur den entsprechenden Datensatz zu diesem Namen heraus.

    Meine Applikation steht schon, nur kann ich nicht auf die Datenbank mittels der dll zugreifen, da ich es nicht leider aus der Doku nicht ganz verstehe, wie ich darauf zugreifen soll.

    Wie ich es bisher verstanden habe, muss zuerst "sDriveInfo" abgerufen werden um zu prüfen, in welchem Laufwerk die CD ist. Danach mit "sGetVersionNr" & "sCheckDatabaseFormat" anwenden, ob die Revision der auf der CD befindlichen DLL mit der von der CD befindlichen Datenbank übereinstimmt. Wenn ich dann also Die Variable für das Laufwerk und die Variable des CD-Check habe, kann ich wohl dann mit der zusätzlichen Varibale "sGetCallInfo" die Datenbank endlich abfragen.

    Gonger96 schrieb:

    Dim refint As Integer = 0
    sGetVersionNr(refint)
    MessageBox.Show(refint.ToString)


    Und das hat schon funktioniert, da bekomme ich schon mal als Zahl "9" angezeigt, ohne jegliche Fehlermeldung von VB ... :thumbsup: Mal weiter vorran tasten ... ;(

    En großes Danke an alle, die sich die Zeit für mein Problem nehmen.
    Lesen, verstehen, ggf. nachfragen, aber bitte beantworten:

    RodFromGermany schrieb:

    Was für eine Speichereiheit ist ein Delphi-word?
    Int16 oder Int32?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    VB.NET-Quellcode

    1. Private Declare Sub sGetCallInfo Lib "E:\win32\rac" (ByVal ADriveInfo As String, ByVal ACall As String, ByVal AInfo As String, ByRef AErrorCode As Integer)

    Das widerspricht sich. Get besagt man bekommt etwas, ein Sub gibt aber keinen Wert zurück also praktisch eine Funkction die void zurück gibt. Hier kannst du nichts zurückbekommen. ByRef besagt du gibst die Variable als Referenz rein, sodass die Funktion sie verändern kann. Nutz eine Funktion die einen Wert zurückgibt und spar dir die Referenz. Es sei denn du arbeitest mit Zeigern, ich hab so 0 bis garkeine Ahnung von Delphi also kann ich nicht gut beurteilen wie das da so läuft.

    Sanweb schrieb:

    Kann ich Dir nicht beantworten, da ich es nicht weiß.

    Artentus schrieb:

    Hab grad eben nachgeschaut. Scheint ein UInt16 zu sein.
    Das musst Du in Deiner .NET-Deklaration entsprechend berücksichtigen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @Gonger96
    Das ist schon wichtig. In .Net geht das, weil die CLR dir das automatisch konvertiert. In nativen Sprachen und damit auch mit Interop in .Net werden die Argumente aber direkt auf den Stack geschoben und geanu so auch wieder ausgelesen. Wenn dann ein Datentyp falsch ist entsteht ein sog. Stack-Imbalance.
    War mir eben nicht ganz sicher ob ich mich da richtig ausgedrückt habe, aber ...

    Die Delphi DLL stammt nicht von mir, sondern war mit der Dokumentation mitsamt der Datenbank auf der CD drauf. Ich brech mir nur einen damit ab, um mittels der auf der CD befindichen DLL auf die Datenbank auf der CD zugreifen zu können mit der von mir erstellten VB2012 Applikation.

    @Gonger96
    Daher kann ich Dir leider auch nicht eine Delphi Funktion aus der DLL benennen, sondern muss auf das oben genannte Delphi Beispiel verweisen, welche mit auf der CD war.
    @Artentus
    Das war mir bis jetzt nicht bewusst. Ich nehm eig. immer die passenden Typen, vielleicht ists mir deswegen nie aufgefallen. Danke wieder etwas gelernt ^^

    @Sanweb
    Was läuft denn noch nicht so wie gewünscht ?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Gonger96“ ()

    Also habe soeben mal meine erste oben genannte Deklaration umgestellt von

    VB.NET-Quellcode

    1. Private Declare Sub sGetVersionNr Lib "E:\win32\rac" (ByRef AVersionNr As Integer)

    nach

    VB.NET-Quellcode

    1. Imports System.Runtime.InteropServices
    2. <DllImport("E:\win32\rac", SetLastError:=True, CharSet:=CharSet.Auto)> _
    3. Private Shared Function sGetVersionNr(ByRef AVersionNr As Integer) As Integer
    4. End Function

    da das Declare zuletzt in VB 6 benutzt wurde. Hier scheint noch kein fehler zu existieren, da ich bei beiden den Wert "9" bekomme.

    Stelle nun gerade

    VB.NET-Quellcode

    1. Private Declare Sub sCheckDatabaseFormat Lib "E:\win32\rac" (ByVal ADriveInfo As String, ByRef AResult As Integer, ByRef AErrorCode As Integer)

    nach (hoffentlich nun richtig?)

    VB.NET-Quellcode

    1. <DllImport("E:win32\rac", SetLastError:=True, CharSet:=CharSet.Auto)> _
    2. Private Shared Function sCheckDatabaseFormat(ByRef ADriveInfo As String, ByVal AResult As Integer, ByVal AErrorCode As Integer) As String
    3. End Function

    um, und versuche damit mal ein Ergebniss zu erzielen, damit "sCheckDatabaseFormat" mir etwas ausspuckt.

    Try & Error Methode halt ...
    Unter .Net basiert Declare Sub/Function auf dem Interops DllImport es ist also egal welches du nutzt, nur DllImport bietet viele Möglichkeiten. Die Sache ist die du musst wissen was der return-Typ ist und welche Parameter rein kommen. Wenn sich hier jmd. mit Delphi auskennt kann er dir das hoffentlich sagen.
    Die Werttyp Int16 stellt ganze Zahlen mit Vorzeichen mit Werten zwischen -32768 und +32767 dar.
    Der Werttyp UInt16 stellt ganze Zahlen ohne Vorzeichen mit Werten zwischen 0 und 65535 dar.

    Also Int16 hat Vorzeichen integriert, was UInt16 nicht hat.
    Das ist sonst das Gleiche, nur das die Spanne dann eben anders ist, wegen dem "-".