Announcement

Collapse
No announcement yet.

Send a char string from C to a PB DLL

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

  • Send a char string from C to a PB DLL

    Ive got a C program that has variable-length string data that is located in a variable that has this declaration:
    Code:
    char *buf;
    How can I send the data from buf to a PB DLL?

    This is what Im currently doing (which is failing):
    Code:
    [i]// Load the library ...[/i]
    hLib = LoadLibrary( "PB.DLL" );
     if ( hLib == NULL )
     {
         printf("Unable to get handle to pb.dll");
         return FALSE;
     }
     lpCallDll = (LPFNCALLDLL*)GetProcAddress(hLib, "ExportedSub");
     if ( lpCallDll == NULL )
     {
        printf("Unable to locate ExportedSub in pb.dll");
        FreeLibrary( hLib );
        return FALSE;
     }
    [i]Call the DLL sub...[/i]
    lpCallDll( buf );
    Here is my exported sub in a DLL:
    Code:
    SUB ExportedSub CDECL ALIAS "ExportedSub" (BufData AS ASCIIZ) EXPORT
    ON ERROR RESUME NEXT
     OPEN "C:\c2pb.log" FOR APPEND AS #1
      PRINT #1, TIME$ & ": DATA=" & BufData
     CLOSE #1
    END SUB
    There are a couple of related samples i found in POFFS but Ive been going around in loops for hours with this now and I know that anyone fluent in C++ could probably do it in a few minutes!


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

  • #2
    Try this:
    Code:
    SUB ExportedSub CDECL ALIAS "ExportedSub" (byval lpBufData as dword) EXPORT
    
        local lpz as asciiz pointer : lpz = lpBufData
    
        ON ERROR RESUME NEXT 
        OPEN "C:\c2pb.log" FOR APPEND AS #1  
        PRINT #1, TIME$ & ": DATA=" & @lpz 
        CLOSE #1    
    
    end sub
    Regards
    Peter

    ------------------
    [email protected]
    [email protected]
    www.dreammodel.dk

    Comment


    • #3
      Thanks very much Peter, it makes sense pulling it in as a DWORD, but my C declarations aren't compatible , what changes do I need to make?


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

      Comment


      • #4
        Ive cut both the C and PB programs down. Both compile, but its still GPF'ing on run due to some type incompatibility somewhere ... :/
        Code:
        [b]'PBDLL.BAS[/b]
        #COMPILE DLL "pbdll.dll"
        #INCLUDE "win32api.inc"
         
        FUNCTION LibMain(BYVAL hInstance   AS LONG, _
                         BYVAL fwdReason   AS LONG, _
                         BYVAL lpvReserved AS LONG) EXPORT AS LONG
          SELECT CASE fwdReason
            CASE %DLL_PROCESS_ATTACH
               OPEN "C:\testdll.log" FOR OUTPUT AS #3
               LibMain = 1   'success!
              EXIT FUNCTION
            CASE %DLL_PROCESS_DETACH
               CLOSE #3
               LibMain = 1   'success!
              EXIT FUNCTION
            CASE %DLL_THREAD_ATTACH
               LibMain = 1   'success!
              EXIT FUNCTION
            CASE %DLL_THREAD_DETACH
               LibMain = 1   'success!
            EXIT FUNCTION
           END SELECT
        END FUNCTION
         
        SUB ExpSub CDECL ALIAS "ExpSub" (PacketData AS DWORD) EXPORT
        ON ERROR RESUME NEXT
        local lpz as asciiz pointer : lpz = PacketData
        PRINT #3, TIME$ & ": DATA=" & @lpz
        END SUB
        Code:
        [b]// TESTDLL.C[/b]
        #include "stdafx.h"
        #include <windows.h>
         
        typedef void __stdcall LPFNCALLDLL( char * strBuffer );
         
        HMODULE		hLib;
        LPFNCALLDLL*	lpCallDll;
         
        int main(int argc, char* argv[])
        {
         printf("Loaded... \n");
         hLib = LoadLibrary( "PBDLL.DLL" );
         if ( hLib == NULL )
         {
        	printf("Unable to get handle to PBDLL.DLL");
        	return FALSE;
         }
         lpCallDll = (LPFNCALLDLL*)GetProcAddress(hLib, "ExpSub");
         if ( lpCallDll == NULL )
         {
        	printf("Unable to locate ExpSub in PBDLL.DLL");
        	FreeLibrary( hLib );
        	return FALSE;
         }
         char	*buf;
         buf = "Test";
         lpCallDll( buf );
         return TRUE;
        }

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

        Comment


        • #5
          Wayne,

          Try to remove __stdcall (or remove CDECL). Then it should work.
          And I think your original ExpSub is OK.

          Regards
          Peter

          P.S. I can't test. I dont have VC++ on this machine.


          ------------------
          [email protected]
          [email protected]
          www.dreammodel.dk

          Comment


          • #6
            Wayne,

            Give this function a whirl, if the value you are getting back from
            the C function in the DLL is the ADDRESS of a zero terminated string
            this should do the job OK.

            Regards,

            [email protected]

            Code:
              '##########################################################################
              
              FUNCTION char(ByVal lpszText as LONG) as STRING
              
                ' ----------------------------------------
                ' converts a zero terminated string at an
                ' address to a basic dynamic string.
                ' ----------------------------------------
              
                  #REGISTER NONE
              
                  LOCAL sl  as LONG
                  LOCAL dst as LONG
              
                  ! mov esi, lpszText
                Start:
                  ! mov al, [esi]
                  ! inc esi
                  ! cmp al, 0             ; scan string for zero
                  ! jne Start
              
                  ! sub esi, lpszText     ; calculate length
                  ! dec esi               ; don't count the zero
                  ! mov sl, esi           ; len in esi
              
                  bas$ = space$(sl)       ' allocate basic string
              
                  ! mov esi, lpsztext
                  ! mov edi, bas$
                  ! mov ecx, sl
              
                  ! rep movsb
              
                  FUNCTION = bas$
              
              END FUNCTION
              
              '##########################################################################
            ------------------
            hutch at movsd dot com
            The MASM Forum - SLL Modules and PB Libraries

            http://www.masm32.com/board/index.php?board=69.0

            Comment


            • #7
              Hi Wayne,

              My C is very rusty, but I think the problem may be in your creation of the test buffer,
              near the end of your C code. You appear to be assigning into space that hasn't been
              reserved -
              Code:
                char *buf; 
                buf = "Test";
              I think you need to either declare and initialise in one go -
              Code:
                char *buf = "Test"
              or reserve space then assign, something like -
              Code:
                char buf[10];
                strcpy( buf, "Test" );
              If I'm going off at a tangent here, just shoot me

              Paul
              Zippety Software, Home of the Lynx Project Explorer
              http://www.zippety.net
              My e-mail

              Comment


              • #8
                *sigh* Im just using a 'blank' sub now and it's still GPFing immediately after calling it ...
                Code:
                SUB ExpSub ALIAS "ExpSub" (PacketData AS DWORD) EXPORT
                END SUB
                Still hammering away


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

                Comment


                • #9
                  Since you are passing the pointer value itsefl from C, rather than the address of the pointer, try this:
                  Code:
                  SUB ExpSub ALIAS "ExpSub" ([b]BYVAL[/b] PacketData AS DWORD) EXPORT

                  ------------------
                  Lance
                  PowerBASIC Support
                  mailto:[email protected][email protected]</A>
                  Lance
                  mailto:[email protected]

                  Comment


                  • #10
                    To throw another spanner in the works, the data in buf can contain chr$(0) which would result in the string being chopped off prematurely
                    a full day wasted trying to parse a binary string from a c++ exe to a pb dll is too long and i can hardly keep my eyes open, ill try www.rentacoder.com in the morning if there are no more leads here


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

                    Comment


                    • #11
                      Wayne,

                      Try this in the C/C++ end:

                      Code:
                      #include "stdafx.h"
                      #include <windows.h>
                       
                      typedef void (CALLBACK* LPFNCALLDLL)( char*);
                       
                      HMODULE		hLib;
                      LPFNCALLDLL	lpCallDll;
                       
                      int main(int argc, char* argv[])
                      {
                       printf("Loaded... \n");
                       hLib = LoadLibrary( "PBDLL.DLL" );
                       if ( hLib == NULL )
                       {
                      	printf("Unable to get handle to PBDLL.DLL");
                      	return FALSE;
                       }
                       lpCallDll = (LPFNCALLDLL)GetProcAddress(hLib, "ExpSub");
                       if ( lpCallDll == NULL )
                       {
                      	printf("Unable to locate ExpSub in PBDLL.DLL");
                      	FreeLibrary( hLib );
                      	return FALSE;
                       }
                       char buf[];
                       buf = "Test";
                       lpCallDll( buf );
                       return TRUE;
                      }
                      Since you are calling a fn in a DLL, the function pointer in C/C++
                      has to be a CALLBACK* type. This should take care of that end!!

                      Cheers,
                      Cecil

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

                      Comment


                      • #12
                        Thanks Cecil, but MSVC6 gives me these errors with your code:
                        Code:
                        VCTest.cpp(25) : error C2133: 'buf' : unknown size
                        VCTest.cpp(26) : error C2440: '=' : cannot convert from 'char [5]' to 'char []'
                        ?

                        Also, the thing is... the data coming in could be 50 bytes or it could be 50000, its variable length so i dont want to explicitly say char [x]... also, the string needs to handle CHR$(0)'s (eg, "This is a \0 test") without chopping any of the string off, so seemingly ill need to pass a length value as well (but ive never had any problems parsing numeric values, just these damned strings! )


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

                        Comment


                        • #13
                          Hi Wayne

                          Lance is right the reason your latest iteration was failing
                          was because you were using BYREF DWORD in your PBDLL SUB instead
                          of the correct BYVAL DWORD. Here's a brief overview:

                          You can use the (CALLBACK* lpfncalldll) as Cecil suggested which amounts to
                          (FAR PASCAL* lpfncalldll) but these both map to __stdcall on
                          x86 platforms. The only difference is in the syntax you'd use
                          as in:

                          Code:
                          // Using __stdcall
                          typedef void __stdcall LPFNCALLDLL( char * );
                          
                          LPFNCALLDLL *lpCallDll;
                          
                          lpCallDll = (LPFNCALLDLL*)GetProcAddress( hLib, "ExpSub" );
                          
                          // Using FAR PASCAL
                          typedef void (FAR PASCAL* LPFNCALLDLL)( char * ):
                          
                          LPFNCALLDLL lpCallDll
                          
                          lpCallDll = (LPFNCALLDLL)GetProcAddress( hLib, "ExpSub" );
                          In the original PBDLL code you posted you were using an ASCIIZ
                          which is fine however your function was CDECL whereas the C
                          code was using the STDCALL convention to access your DLL.

                          I see that your present PBDLL SUB does not use CDECL which
                          means that it defaults to STDCALL which matches your C declaration.
                          Both these signatures are correct for receiving data correctly
                          from the C application:

                          Code:
                          SUB ExpSub ALIAS "ExpSub" (BYVAL PacketData AS DWORD) EXPORT
                              LOCAL lpz AS ASCIIZ PTR
                              
                              lpz = PacketData
                              PRINT #3, TIME$ & ": DATA=" & @lpz
                          END SUB
                          
                          'OR
                          
                          SUB ExpSub ALIAS "ExpSub" ( szData AS ASCIIZ ) EXPORT
                          
                              PRINT #3, TIME$ & ": DATA=" & szData
                          
                          END SUB
                          They both amount to the same thing - although the ASCIIZ is
                          more comfortable...

                          Cheers

                          Florent

                          PS: You can use your original c code as in:

                          Code:
                          #include <stdio.h>
                          #include <windows.h>
                            
                          typedef void __stdcall LPFNCALLDLL( char * );
                            
                          int main(int argc, char* argv[] ) {
                          	/* Note that HMODULE and LPFNCALLDLL are local to
                          	 * main here. Placing them outside of a function
                          	 * makes them global - maybe that's your intention,
                          	 * just pointing it out... */
                          	HMODULE		hLib;
                          	LPFNCALLDLL *lpCallDll;
                          	char *buffer;
                           	
                          	printf("Loaded... \n");
                          	hLib = LoadLibrary( "PBDLL.DLL" );
                          	if ( !hLib  ) {
                          		printf("Unable to get handle to PBDLL.DLL" );
                          		return FALSE;
                          	}
                           
                          	lpCallDll = (LPFNCALLDLL*)GetProcAddress( hLib, "ExpSub" );
                          	if ( !lpCallDll ) {
                          		printf("Unable to locate ExpSub in PBDLL.DLL");
                          		FreeLibrary( hLib );
                          		return FALSE;
                          	}
                           	
                          	buffer = "Test";
                          	lpCallDll( buffer );
                           
                          	return TRUE;
                          }
                          ------------------


                          [This message has been edited by Florent Heyworth (edited August 01, 2001).]

                          Comment


                          • #14
                            Florent, ok im 99% of the way there now! (Id just like to take this quick break to thank everyone for their patience and testing )
                            Only one problem left - if the data sent is "Test1 /0 Test2", "Test1 " is all that gets read due to the nullchar in the middle, is there any way of getting around that?

                            Here's the code I've got now (and backed up!) that finally works (apart from the nullchar problem, as demonstrated in this source):
                            Code:
                            #COMPILE DLL "pbdll.dll"
                            #INCLUDE "win32api.inc"
                            FUNCTION LibMain(BYVAL hInstance   AS LONG, _
                                             BYVAL fwdReason   AS LONG, _
                                             BYVAL lpvReserved AS LONG) EXPORT AS LONG
                              SELECT CASE fwdReason
                                CASE %DLL_PROCESS_ATTACH
                                   OPEN "C:\damnc.log" FOR OUTPUT AS #3
                                   LibMain = 1   'success!
                                  EXIT FUNCTION
                                CASE %DLL_PROCESS_DETACH
                                   CLOSE #3
                                   LibMain = 1   'success!
                                  EXIT FUNCTION
                                CASE %DLL_THREAD_ATTACH
                                   LibMain = 1   'success!
                                  EXIT FUNCTION
                                CASE %DLL_THREAD_DETACH
                                   LibMain = 1   'success!
                                EXIT FUNCTION
                               END SELECT
                            END FUNCTION
                             
                            SUB ExpSub ALIAS "ExpSub" ( szData AS ASCIIZ ) EXPORT
                                PRINT #3, TIME$ & ": DATA=" & szData
                            END SUB
                            Code:
                            #include "stdafx.h"
                            #include <windows.h> 
                            typedef void __stdcall LPFNCALLDLL( char * );
                            LPFNCALLDLL *lpCallDll;
                            HMODULE		hLib;
                             
                            int main(int argc, char* argv[])
                            {
                             printf("Loaded... \n");
                             hLib = LoadLibrary( "PBDLL.DLL" );
                             if ( hLib == NULL )
                             {
                            	printf("Unable to get handle to PBDLL.DLL");
                            	return FALSE;
                             }
                             lpCallDll = (LPFNCALLDLL*)GetProcAddress( hLib, "ExpSub" );
                             if ( lpCallDll == NULL )
                             {
                            	printf("Unable to locate ExpSub in PBDLL.DLL");
                            	FreeLibrary( hLib );
                            	return FALSE;
                             }
                             char *buf;
                             strcpy (buf , "Test1 \0 Test2");  // only "Test1 " is parsed due to nullchar
                             lpCallDll( buf );
                             printf ("Done");
                             return TRUE;
                            }

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

                            Comment


                            • #15
                              It seems you want to pass a NUL-terminated string that may contain NUL characters...
                              this is not honestly a good match. If the string needs to be able to contain NULs, a
                              better bet would be to recieve it as a pointer and pass a length count along with it.
                              Your SUB can then process the result into a PowerBASIC string. On the PB end, that
                              might look like this:

                              Code:
                              SUB ExpSub ALIAS "ExpSub" (BYVAL lpszData AS DWORD, BYVAL cbData AS LONG ) EXPORT
                                  LOCAL sData AS STRING
                                  sData = PEEK$(lpszData, cbData)
                                  PRINT #3, TIME$ & ": DATA=" & sData
                              END SUB
                              You'd need to adjust the C end to pass along the length count, also.


                              ------------------
                              Tom Hanlin
                              PowerBASIC Staff

                              Comment


                              • #16
                                Wayne,

                                I forgot to use the strcpy fn to initialize the string array.
                                You can use an unsized array or pointer, makes no difference.

                                As far as the imbedded nuls, on the PB side you'll need to
                                parse the string for that!!!!

                                Cheers,
                                Cecil

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

                                Comment


                                • #17
                                  WOOOOOHOOOOO! Tom, that iced the cake just nicely!
                                  It's 3:30am Perth time, Ive been trying to parse this thing since about 11am yesterday, you have no idea how relieved (and tired!) i am
                                  You guys are sensational, if I could buy you all a drink I would! (but its the thought that counts )
                                  Cecil, btw im not sure how you initialise a string with strcpy(), Ive used it to copy strings before (eg. strcpy(buf, "test") , but never to initialise... ? just if you get a chance
                                  Ok, goodnight all, its been a very long day


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

                                  Comment


                                  • #18
                                    Wayne,

                                    Well, you just did that in the C code. You passed the string
                                    constant "Test1 \0 Test2" to the buf variable. Agreed, this is
                                    NOT the conventional way of assigning the string, but yesterday
                                    was one of those days when you feel 'Brain Dead'. Know what I
                                    mean??????

                                    In C, the two most conventional ways of initializing a string
                                    with a string constant is as follows:

                                    1. char buf[] = "Test1 \0 Test2"
                                    2. char* buf = "Test1 \0 Test2"

                                    As you can see, method 1. uses an unsized array, and method 2.
                                    uses a pointer. The compiler computes the size and assigns
                                    memory space as required in either case.

                                    In C, the nul-terminated string is actually treated as a pointer
                                    by the compiler no matter how you assign it.

                                    A more appropriate type convention for strings in C is the TCHAR.
                                    This is more applicable for portable code especially when
                                    UNICODE is defined in the project.

                                    Florent is correct, of course, in the function pointer call,
                                    however, I prefer assigning it as a pointer from the get-go.
                                    This makes the type casts more readable. For instance, while
                                    reading code "(LPFNCALLDLL*)" immediately translates in my mind
                                    as "a pointer to a pointer" when that is not the case from
                                    the way you typdefed the call.

                                    Is that clear as mud???????

                                    One more thing, if you are creating a PB DLL for the "C" community,
                                    run-time linking is NOT the way to go. Supply your DLL with
                                    load-time linking. This requires a bit more work on the PB end
                                    but since you have VC++6, no prob. Peter Stephensen and I did
                                    a gig on this toward the end of last year or the first of this
                                    year. Don't have the link address, but it's on this BBS.

                                    Cheers,
                                    Cecil


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


                                    [This message has been edited by Cecil Williams (edited August 02, 2001).]

                                    Comment


                                    • #19
                                      have a look at http://www.powerbasic.com/support/pb...ad.php?t=19859

                                      regards
                                      peter

                                      ------------------
                                      [email protected]
                                      [email protected]
                                      www.dreammodel.dk

                                      Comment

                                      Working...
                                      X