Announcement

Collapse
No announcement yet.

Calling a PB DLL from a Delphi EXE

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Calling a PB DLL from a Delphi EXE

    Hello,
    I'm working via email with a Delphi/Pascal programmer on a project.
    His Delphi program will be calling my PowerBasic DLL, but we're having
    a heck of a time getting a simple function to work! (We're both ignorant,
    I guess ) As a test, he'd like to call my DLL to perform a division
    on two LONGS (32 bit LONGS are called "integers" in Delphi) and return
    a SINGLE.

    His Delphi version of the DLL call is:

    function TestIntDiv(a,b: integer): single; stdcall; external 'DLLTEST.DLL';

    I'll show more of his DLL source if it would help, but it doesn't look
    like much of it has to do with this function (I could easily be wrong again
    though, of course.)


    Here's my PB DLL:
    Code:
    #COMPILE DLL
    
    FUNCTION TestIntDiv (a AS LONG,b AS LONG) EXPORT AS SINGLE
       MSGBOX "PowerBasic DLL Message: Input values are " + STR$(a)+" and " +STR$(b)
       FUNCTION=a/b 
    END FUNCTION
    I've tried this with INTEGER as well as LONG here, with the same
    error message:

    "The TESTDIVONLY.EXE file is linked to missing export DLLTEST.DLL
    -TestIntDiv."

    AARGH!! Anyone happen to know what may be wrong, or what this fellow
    and I should be looking for? I saw this same error message in a search
    through the forum, but it was for a PB EXE calling a Delphi DLL, rather
    than the other way around.

    Thanks a ton, I'd be lost without the help here on the PowerBasic forum!

    Todd Wasson




    ------------------
    Todd Wasson
    http://PerformanceSimulations.Com
    PowerBasic Racing Simulator (October 2007 clip - 15.1MB wmv file) http:http://www.performancesimulations.co...m-GenIV-12.wmv

  • #2
    Here's what your PB dll should look like ...

    Code:
    #COMPILE DLL
    FUNCTION TestIntDiv Alias "TestIntDiv" _
       (ByVal a AS LONG, ByVal b AS LONG) EXPORT AS SINGLE   
    
      MSGBOX "PowerBasic DLL Message: Input values are " + _
        STR$(a)+" and " +STR$(b)   
      FUNCTION=a/b 
    
    END FUNCTION
    The Delphi declare should look like this (you had it right):
    Code:
    function TestIntDiv(a,b: integer): single; stdcall; external 'DLLTEST.DLL';
    In short, delphi passes variables ByVal as a default (PB passes them ByRef). Also, your delphi app wasn't finding the entry point because you didn't specify the alias - it would have found TESTINTDIV, but then would have gpf'd because of the byval thing.
    Best Regards
    Don

    ------------------
    Don Dickinson
    www.greatwebdivide.com

    Comment


    • #3
      Don,

      Thank you! Fantastic! Such a quick reply too! I never would have
      figured that out on my own. Just curious, I'll be testing the DLL during
      development with a PB program. Should I (or can I) pass parameters and
      work things the same way? How so? Make sure to use ALIAS in the Function
      names with "BYVAL x SINGLE" for everything?

      Thanks again!

      Todd Wasson


      ------------------
      Todd Wasson
      http://PerformanceSimulations.Com
      PowerBasic Racing Simulator (October 2007 clip - 15.1MB wmv file) http:http://www.performancesimulations.co...m-GenIV-12.wmv

      Comment


      • #4
        Yes, you can. The one exception is when returning a string. Here's what I do ...

        Code:
        Global g_Return as String
        
        '- Call this one from PB or VB
        Function ReturnString Alias "ReturnString" _
            ( ByVal sString as String) Export as String
          Function = "This is the string: " + Trim$(sString)
        End Function
        
        '- Call this one from delphi
        Function ReturnStringZ Alias "ReturnStringZ" _
            ( incoming as Asciiz) Export as DWORD
          g_Return = ReturnString(Trim$(incoming)) + $NUL
          Function = StrPtr(g_Return)
        End Function
        
        // The delphi declaration
        function ReturnStringZ(zString : PChar) : PChar; stdcall; external 'mydll.dll';
        
        '- The vb declaration
        declare Function ReturnString Alias "ReturnString" _
            ( ByVal sString as String) as String
        Or something like that. I didn't test it, but I think it's ok.

        Best Regards,
        Don

        ------------------
        Don Dickinson
        www.greatwebdivide.com

        Comment


        • #5
          Also, I should point out that you can pass variable by reference as well. E.G.

          Code:
          #COMPILE DLL
          '
          '- When this returns, a will increase by one.
          FUNCTION TestIntDiv Alias "TestIntDiv" _   
            (a AS LONG, b AS LONG) EXPORT AS SINGLE
             a = a + 1
               MSGBOX "PowerBasic DLL Message: Input values are " + _    
               STR$(a)+" and " +STR$(b)     
            FUNCTION=a/b 
          END FUNCTION
          '
          'Te Delphi declare should look like this:
          '
          function TestIntDiv(var a: integer; var b : Integer): single; stdcall; external 'DLLTEST.DLL';
          Best Regards,
          Don


          ------------------
          Don Dickinson
          www.greatwebdivide.com

          Comment


          • #6
            Don or any other Delphi expert,

            I have a PB/DLL ver 6.0 DLL HX2000.DLL with a sub Get_Calculation:

            '**************
            SUB Get_Calculation(B1 AS BYTE, B2 AS BYTE, HeatexIn AS DOUBLE, sHeatexType AS ASCIIZ, B3 AS BYTE, B4 AS BYTE, HeatexOut AS DOUBLE) EXPORT

            DIM HeatexInArray(20) AS DOUBLE
            DIM HeatexOutArray(50) AS DOUBLE
            DIM i AS LONG
            DIM j AS LONG
            DIM VvxDataIn AS DOUBLE Pointer
            DIM VvxDataOut3 AS DOUBLE Pointer

            'more code

            VvxDataIn=VARPTR(HeatexIn)
            FOR i=0 TO 20
            HeatexInArray(i)[email protected][i]
            NEXT i


            'much more code

            VvxDataOut3=VARPTR(HeatexOut)
            FOR j=0 TO 50
            @VvxDataOut3[j] = HeatexOutArray(j)
            NEXT j

            'more code

            END SUB
            '***************

            How do I declare and call Get_Calculation from Delphi?


            Krister Olsson

            ------------------

            Comment


            • #7
              Old thread, by I have the same question as Krister

              Best Regards
              Fredrik

              Originally posted by Krister Olsson View Post
              Don or any other Delphi expert,

              I have a PB/DLL ver 6.0 DLL HX2000.DLL with a sub Get_Calculation:

              '**************
              SUB Get_Calculation(B1 AS BYTE, B2 AS BYTE, HeatexIn AS DOUBLE, sHeatexType AS ASCIIZ, B3 AS BYTE, B4 AS BYTE, HeatexOut AS DOUBLE) EXPORT

              DIM HeatexInArray(20) AS DOUBLE
              DIM HeatexOutArray(50) AS DOUBLE
              DIM i AS LONG
              DIM j AS LONG
              DIM VvxDataIn AS DOUBLE Pointer
              DIM VvxDataOut3 AS DOUBLE Pointer

              'more code

              VvxDataIn=VARPTR(HeatexIn)
              FOR i=0 TO 20
              HeatexInArray(i)[email protected][i]
              NEXT i


              'much more code

              VvxDataOut3=VARPTR(HeatexOut)
              FOR j=0 TO 50
              @VvxDataOut3[j] = HeatexOutArray(j)
              NEXT j

              'more code

              END SUB
              '***************

              How do I declare and call Get_Calculation from Delphi?


              Krister Olsson

              ------------------

              Comment


              • #8
                >Make sure to use ALIAS in the Function names with "BYVAL x SINGLE" for everything

                Um, the ALIAS in your PB Function header is used to specify the "export name" of the procedure, the name you use in your "using module's definition of that exported procedure"

                It has nothing to do with parameter or return data types.

                If you are going to be working "cross language" you may want to download and compile....

                Show exports and imports for PB/Win 6x, 7x (original by Torsten Reinow)

                You "open" your calling module and look in the "imports" ... that will be the name that module is looking for. You "open" your DLL module and check the "exports"... that gives you the name of your procedure as other programs must look for it.

                IMPORT and EXPORT names ARE case-sensitive.
                Last edited by Michael Mattias; 21 Apr 2012, 09:39 AM.
                Michael Mattias
                Tal Systems Inc. (retired)
                Racine WI USA
                [email protected]
                http://www.talsystems.com

                Comment


                • #9
                  No Delphi/Pascal code shown. Which compiler are you using?

                  If you are using a Windows unit you can use LoadLibrary/GetProcaddress.

                  Comment


                  • #10
                    Wow, I just an email reply from this thread I started more than 11 years ago... Thanks for the blast from the past. This was the very first line of code written as a test for what has since become this:

                    http://www.youtube.com/watch?v=RaMMa_X-PZ8

                    Sorry I can't help with your Delphi question. I'm sure one of these guys will help sort things out.
                    Todd Wasson
                    http://PerformanceSimulations.Com
                    PowerBasic Racing Simulator (October 2007 clip - 15.1MB wmv file) http:http://www.performancesimulations.co...m-GenIV-12.wmv

                    Comment


                    • #11
                      Sorry but may we have a real compilable version of the PB code to work with?

                      Do PB Doubles take up 21 bytes?
                      as in :
                      Code:
                      VvxDataIn=VARPTR(HeatexIn)
                       FOR i=0 TO 20
                       HeatexInArray(i)[email protected][i]
                       NEXT i

                      Comment


                      • #12
                        Delphi code (IDE XE2) trying to call DLL:
                        (see attached error message)

                        procedure TForm1.Button1Click(Sender: TObject);
                        type
                        Short = Byte;
                        pShort = ^Short;
                        TArrayIn = packed array [0..20] of Double;
                        TArrayOut = packed array [0..50] of Double;
                        var
                        s1, s2: Byte;
                        z1, z2: pShort;
                        RRIN: TArrayIn;
                        RROUT: TArrayOut;
                        RRName: PAnsiChar;
                        v1: Integer;
                        Hbar: Thandle;
                        // Declaration. ***
                        GET_CALCULATION: procedure(_s1, _s2: Byte; _RRIN: TArrayIn; _RRName: PAnsiCHar; _z1, _z2: pShort; var RROUT_: TarrayOut);stdcall;
                        begin
                        Hbar := LoadLibrary('C:\Heatex32.dll');
                        if Hbar > 0 then
                        begin
                        try
                        try
                        GET_CALCULATION := GetProcAddress(Hbar, PAnsiChar('GET_CALCULATION'));
                        if Assigned(GET_CALCULATION) then
                        begin
                        // ***
                        s1 := 0;
                        s2 := 0;
                        // ***
                        for v1 := Low(RRIN) to High(RRIN) do
                        begin
                        RRIN[v1] := 0;
                        end;
                        RRIN[1] := 2.5;
                        RRIN[2] := 0;
                        RRIN[3] := 20;
                        RRIN[4] := 0.006;
                        RRIN[5] := 2.5;
                        RRIN[6] := 0;
                        RRIN[7] := -10;
                        RRIN[8] := 0.0016;
                        RRIN[9] := 1.2;
                        RRIN[10] := 1;
                        RRIN[11] := 1001;
                        RRIN[12] := 0;
                        RRIN[13] := 101325;
                        RRIN[14] := 0;
                        RRIN[15] := 0;
                        RRIN[16] := 0;
                        RRIN[17] := 0;
                        // ***
                        RRName := PAnsiChar('H0750/6.0/E');
                        // ***
                        GetMem(z1, SizeOf(Byte));
                        z1^ := 0;
                        GetMem(z2, SizeOf(Byte));
                        z2^ := 0;
                        // ***
                        for v1 := Low(RROUT) to High(RROUT) do
                        begin
                        RROUT[v1] := 0;
                        end;
                        // Call procedure. ***
                        GET_CALCULATION(s1, s2, RRIN, RRName, z1, z2, RROUT);
                        // Show result. ***
                        ShowMessage(FloatToStr(RROUT[0]));
                        end;
                        except
                        RaiseLastOSError;
                        end;
                        finally
                        FreeLibrary(HBar);
                        end;
                        end
                        else
                        begin
                        MessageDlg('Error: Can't find Heatex32.dll', mtError, [mbOk], 0);
                        end
                        end;

                        end.



                        Originally posted by Chris Holbrook View Post
                        No Delphi/Pascal code shown. Which compiler are you using?

                        If you are using a Windows unit you can use LoadLibrary/GetProcaddress.
                        Attached Files

                        Comment


                        • #13
                          Making assumptions because I don't have PB/DLL V6 that the default is CDECL/STDCALL, and all parameters are passed BYREF.

                          So you want to supply pointers as arguments. I'm pretty sure that passing a byte is not the same as passing a pointer to a byte. Presumably VAR takes out a level of indirection too, so you have alternative methods of passing a pointer. Personally, I would use explicit pointers (pByte, pAnsiChar, etc), which would mean either declaring a pointer-to-your-double-array type or using a dword to pass the double array addresses. That may appear heavy-handed but it does show the next programmer to read your code exeactly what your intentions were!

                          If you have the PB/DLL or a PBWIn compiler you can create a DLL in minutes with SUBS/FUNCTIONS to test that each each individual argument type is passed correctly.

                          Comment


                          • #14
                            Is there any documentation on the interface for this DLL?
                            Any chance of seeing any code that DOES talk to it now?

                            Never mind. found

                            http://www.powerbasic.com/support/pb...d.php?p=151082

                            so will look at that for a while.

                            Comment


                            • #15
                              Ok, it's not pretty and am not sure which way sHeatexType is supposed to go.
                              and the arrays aren't in the 2004 thread. I based the DLL on what was in that thread.
                              and used Delphi v7 for the below code.
                              but the compiler is doing most of the work.

                              Code:
                              function GET_CALCULATION(var B1: byte; var B2: Byte; var HeatexIN: double; var sHeatexType: PAnsiCHar; var B3: byte; var B4: byte; var HeatexOut: double): integer; stdcall; external 'E:\pbcc\junk\Delphitst.DLL';
                              
                              
                              procedure TForm1.Button2Click(Sender: TObject);
                              type
                               Short = Byte;
                               pShort = ^Short;
                               TArrayIn = packed array [0..20] of Double;
                               TArrayOut = packed array [0..50] of Double;
                              var
                               B1, B2, B3, B4: byte;
                               HeatexIn: double; //TArrayIn;
                               HeatexOut: double; //TArrayOut;
                               sHeatexType: PAnsiChar;
                               begin
                              
                               B1 := 65;
                               B2 := 66;
                               B3 := 67;
                               B4 := 68;
                               sHeatexType := 'HeatexType';
                               HeatexIn := 1234.99;
                               HeatexOut := 4321.88;
                               edit4.Text := format('%d', [B1]);
                               edit5.Text := format('%d', [B2]);
                               edit6.Text := format('%d', [B3]);
                               edit7.Text := format('%d', [B4]);
                               edit8.Text := sHeatexType;
                               edit9.Text := format('%f', [HeatexIn]);
                               edit10.Text := format('%f', [HeatexOut]);
                              
                               GET_CALCULATION(B1, B2, HeatexIn, sHeatexType, B3, B4, HeatexOut);
                              
                               edit11.Text := format('%d', [B1]);
                               edit12.Text := format('%d', [B2]);
                               edit13.Text := format('%d', [B3]);
                               edit14.Text := format('%d', [B4]);
                               edit15.Text := sHeatexType;
                               edit16.Text := format('%f', [HeatexIn]);
                               edit17.Text := format('%f', [HeatexOut]);
                              
                              end;
                              Last edited by Paul D. Elliott; 23 Apr 2012, 06:59 AM.

                              Comment


                              • #16
                                >(see attached error message)

                                0xC0000005 = attempt to access unowned memory. Best guess you passed a paremeter as an address when a value was required or vice-versa; or passed a PB STRING datatype when the Deplhi says STRING but which actually means BYREF ASCIIZ.

                                Then again you may have a programming error independent of the data types.

                                As Mr. Holbrook suggests, FIRST make sure your datatypes are being passed correctly.
                                Michael Mattias
                                Tal Systems Inc. (retired)
                                Racine WI USA
                                [email protected]
                                http://www.talsystems.com

                                Comment


                                • #17
                                  fixed sHeaterType to go both ways if needed.
                                  made HeatexIn & HeatexOut to be arrays.
                                  fixed declaration so no return value expected ( same as Sub in PB ).

                                  Code:
                                    procedure GET_CALCULATION(var B1: byte; var B2: Byte; var HeatexIN: double; sHeatexType: pAnsiChar; var B3: byte; var B4: byte; var HeatexOut: double); stdcall; external 'E:\pbcc\junk\Delphitst.DLL';
                                  
                                  procedure TForm1.Button2Click(Sender: TObject);
                                  type
                                   Short = Byte;
                                   pShort = ^Short;
                                   TArrayIn = packed array [0..20] of Double;
                                   TArrayOut = packed array [0..50] of Double;
                                  var
                                   B1, B2, B3, B4: byte;
                                   HeatexIn: TArrayIn;
                                   HeatexOut: TArrayOut;
                                   sHeatexType: array [0..100] of char; //pAnsiChar;
                                   v1: integer;
                                   begin
                                  
                                   B1 := 65;
                                   B2 := 66;
                                   B3 := 67;
                                   B4 := 68;
                                   sHeatexType := 'HeatexType';
                                  
                                   For v1 := Low(HeatexIn) To High(HeatexIn) Do
                                    Begin
                                       HeatexIn[v1] := 0;
                                    End;
                                   HeatexIn[1] := 2.5;
                                   HeatexIn[2] := 0;
                                   HeatexIn[3] := 20;
                                   HeatexIn[4] := 0.006;
                                   HeatexIn[5] := 2.5;
                                   HeatexIn[6] := 0;
                                   HeatexIn[7] := -10;
                                   HeatexIn[8] := 0.0016;
                                   HeatexIn[9] := 1.2;
                                   HeatexIn[10] := 1;
                                   HeatexIn[11] := 1001;
                                   HeatexIn[12] := 0;
                                   HeatexIn[13] := 101325;
                                   HeatexIn[14] := 0;
                                   HeatexIn[15] := 0;
                                   HeatexIn[16] := 0;
                                   HeatexIn[17] := 0;
                                  
                                   For v1 := Low(HeatexOut) To High(HeatexOut) Do
                                     Begin
                                       HeatexOut[v1] := 0;
                                     End;
                                  
                                   HeatexIn[0] := 1234.99;
                                   HeatexOut[0] := 4321.88;
                                   edit4.Text := format('%d', [B1]);
                                   edit5.Text := format('%d', [B2]);
                                   edit6.Text := format('%d', [B3]);
                                   edit7.Text := format('%d', [B4]);
                                   edit8.Text := sHeatexType;
                                   edit9.Text := format('%f', [HeatexIn[0]]);
                                   edit10.Text := format('%f', [HeatexOut[0]]);
                                  
                                   GET_CALCULATION(B1, B2, HeatexIn[0], sHeatexType, B3, B4, HeatexOut[0]);
                                  
                                   edit11.Text := format('%d', [B1]);
                                   edit12.Text := format('%d', [B2]);
                                   edit13.Text := format('%d', [B3]);
                                   edit14.Text := format('%d', [B4]);
                                   edit15.Text := sHeatexType;
                                   edit16.Text := format('%f', [HeatexIn[0]]);
                                   edit17.Text := format('%f', [HeatexOut[0]]);
                                  
                                  end;

                                  Comment


                                  • #18
                                    @Paul, where is the DLL please? I did not find it.

                                    Comment


                                    • #19
                                      Chris,

                                      It gets complicated.

                                      I made my own DLL based on what was posted.
                                      I didn't post the whole Delphi program.
                                      If you use the code below you will need to modify the first line of the
                                      Delphi code above to reflect where you put the DLL.
                                      The Delphi code & this DLL are just using test data ( probably not what
                                      the real program would use ).

                                      Code:
                                      #Compile DLL
                                      
                                      Function TestIntDiv Alias "TestIntDiv" (ByVal a As Long, ByVal b As Long) Export As Single
                                      
                                        MsgBox "PowerBasic DLL Message: Input values are " + Str$(a)+" and " +Str$(b)
                                        Function=a/b
                                      
                                      End Function
                                      
                                      Sub GET_CALCULATION(ByRef B1 As Byte, ByRef B2 As Byte, ByRef HeatexIn As Double, ByRef sHeaterType As AsciiZ, ByRef B3 As Byte, ByRef B4 As Byte, ByRef HeatexOut As Double) Export
                                       Dim HeatexInArray(20) As Double
                                       Dim HeatexOutArray(50) As Double
                                       Dim i As Long
                                       Dim j As Long
                                       Dim VvxDataIn As Double Pointer
                                       Dim VvxDataOut As Double Pointer ' removed the 3 from the name
                                      
                                       'more code
                                      
                                       VvxDataIn=VarPtr(HeatexIn)
                                       For i=0 To 20
                                       HeatexInArray(i)[email protected][i]
                                       Next i
                                      
                                       For i = 1 To 20
                                          HeatexInArray(0) += HeatexInArray(i)
                                       Next
                                      
                                       For j = 0 To 50
                                          HeatexOutArray(j) = HeatexInArray(0) * j
                                       Next
                                       'much more code
                                      
                                       ' sorry forgot to move the whole array back
                                       VvxDataOut = VarPtr(HeatexOut)
                                       For j = 0 To 50
                                          @VvxDataOut[j] = HeatexOutArray(j)
                                       Next
                                      
                                         B1 = Asc("a")
                                         B2 = Asc("b")
                                         'HeatexIN = 1.00
                                         MsgBox "in pb dll " & sHeaterType
                                         sHeaterType = "abcdef"
                                         B3 = Asc("x")
                                         B4 = Asc("y")
                                         'HeatexOut = 2.00
                                      End Sub
                                      Last edited by Paul D. Elliott; 24 Apr 2012, 12:23 PM. Reason: sorry forgot to move the whole array back

                                      Comment


                                      • #20
                                        Fredrik,

                                        Did that answer your question?

                                        I don't have a version of Delphi later than v7.


                                        Chris,

                                        Sorry. I forgot to move the whole output array out.
                                        My program was only looking at 1st entry.
                                        Last edited by Paul D. Elliott; 24 Apr 2012, 12:19 PM.

                                        Comment

                                        Working...
                                        X