Announcement

Collapse
No announcement yet.

Passing ASCIIZ Parameter to C Library Function

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

  • Passing ASCIIZ Parameter to C Library Function

    Dear Sirs,

    I am attempting to pass text between PB/Win 8.04 to the "Universal Library" from "Measurement Computing." For example, the declaration in the "C" header file for one of the functions which pass a pointer to character is as follows:

    int EXTCCONV cbLogGetAIChannelCount(char* filename, int* aiCount);

    1. Is ASCIIZ the most appropriate for passing Text to a function expecting a pointer-to-char, since text is null-terminated as required by C, and a pointer to ASCIIZ points directly to the beginning of the memory containing the ASCIIZ string, as I believe is required by a pointer-to-char in C.

    2. Should I pass the pointer BYREF or BYVAL? As the documentation for PB/Win 8.04 says "When you pass an ASCIIZ string to a routine, you are actually passing a pointer to the string's data."

    . a. What happens when we pass an ASCIIZ BYREF? Do we pass the array descriptor like when passing a numberic array, or do we just pass the pointer to the array?

    . b. What happens when we pass an ASCIIZ BYVAL? Do we copy the whole ASCIIZ string onto the parameter stack or just the pointer?



    Sincerely Yours,
    John Harvill
    Last edited by John Harvill; 31 Jan 2008, 11:18 AM.

  • #2
    Code:
    Declare Function cbLogGetAIChannelCount Lib "EXTCCONV.DLL" Alias "cbLogGetAIChannelCount" (ByRef filename As Asciiz, ByRef aiCount As Long) As Long
    1. DLL name might be different, I used the current name as a placeholder.

    2. A char* (pointer) can be BYREF ASCIIZ or BYVAL ASCIIZ PTR, or even BYVAL DWORD if you are using indirection (ie. for several data types). I used the first as it's the most simple method.

    3. BYVAL ASCIIZ * LEN does (I believe) pass all the data on the stack up to the length specified, making it the slowest and possibly rarest method.

    Hope this explanation helps!
    kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

    Comment


    • #3
      In addition you can pass a (ugh) literal ("myfile.txt") or the STRPTR of a dynamic string variable BYVAL.

      In the literal case it's because the compiler does what's required; in the second case it's because dynamic strings are always null-terminated, handled by the operating system.(yes this is documented).

      The key is understanding the "what".. which gives you a choice of "how."

      When a C function wants " * char" the "what" is "the address of a null-terminated string of characters."

      Therefore "BYVAL asciiz_var" at the point of call is incorrect.... because that passes the data itself, not the address of the data.
      Michael Mattias
      Tal Systems (retired)
      Port Washington WI USA
      [email protected]
      http://www.talsystems.com

      Comment


      • #4
        Kev and Michael,

        Thanks alot. These explanations help.

        Michael, when you say "STRPTR of a dynamic string variable BYVAL," are you refering to something like this example:

        When the C Header says:
        int C_Routine( char* TextMsgParameter );

        Then PowerBasic can interface as follows:

        DECLARE FUNCTION C_Routine LIB "x.lib" ALIAS "C_Routine" ( BYREF TextMsgVariable AS STRING ) AS LONG

        LOCAL TextMsg AS STRING
        TextMsg = "The Message I want to pass to the C-routine."
        Return_Value = C_Routine( BYVAL STRPTR( TextMsg ) )

        :thinking:
        Sincerley,
        John Harvill
        Last edited by John Harvill; 31 Jan 2008, 11:19 AM.

        Comment


        • #5
          Yes John, that is correct. Remember, when a C routine needs a character string, it wants the address of the 1st byte of the string. If the string is an asciiz fixed length string and Byref is the function declaration (the default unless you specifically specify BYVAL in the declare), then just placing the asciiz var in the function will cause the compiler to pass the address of the asciiz string to the C routine.

          If you use Byval Strptr(dynamic string), then that accomplishes the same thing. Anyway you can get the address of the first byte of the string to the C function will work. What you actually use for an arguement in the function will depend on whether Byref/Byval and the variable type.
          Fred
          "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

          Comment


          • #6
            There are two different things:

            DECLAREing the external function
            CALLing the DECLAREd external function

            As long as these are consistent with each other and with the library, you may do it any way you please. I'd do it one way, but there is no one 'correct' answer.

            That said......

            DECLAREing your C_Routine to accept the address of a dynamic string handle would be correct if the "C" header read " BSTR * TextMessageParameter" but it doesn't.

            And in your call, using a BYVAL override negates whatever you DECLAREd anyway (that's why it's called an override).

            I have five bucks says what you have now won't work.

            If you are lucky you will get an immediate GPF.

            If you are unlucky, you'll simply corrrupt your memory without a detectable error, resulting in a GPF later at a point in your code where you cannot find anything wrong, or you'll just get incorrect output.
            Michael Mattias
            Tal Systems (retired)
            Port Washington WI USA
            [email protected]
            http://www.talsystems.com

            Comment


            • #7
              Would the example work better if the parameter was declared ASCIIZ instead, as follows?

              When the C Header says:
              int C_Routine( char* TextMsgParameter );

              Then PowerBasic can interface as follows:

              DECLARE FUNCTION C_Routine LIB "x.lib" ALIAS "C_Routine" ( BYREF TextMsgVariable AS ASCIIZ ) AS LONG

              LOCAL TextMsg AS STRING
              TextMsg = "The Message I want to pass to the C-routine."
              Return_Value = C_Routine( BYVAL STRPTR( TextMsg ) )


              The PowerBasic documentation for CALL and passing a parameter BYVAL contains an example which appears to say we should declare the passed parameter as "BYREF ASCIIZ" even though we are calling the function BYVAL STRPTR( Dynamic_String_Variable ), in the following example:

              SUB TheSub(x AS ASCIIZ) ' Address of x expected
              ...
              DIM a$
              a$ = "Dynamic string data"
              CALL TheSub(BYVAL STRPTR(a$)) ' Pass data address
              :thinking:
              Sincerley,
              John Harvill

              Comment


              • #8
                Yes John, that would be better. In my reply a couple posts up I missed that the parameter was a String in the C interface declare....

                DECLARE FUNCTION C_Routine LIB "x.lib" ALIAS "C_Routine" ( BYREF TextMsgVariable AS STRING ) AS LONG

                That isn't, as far as I know, even possible. C doesn't even have a String class. Actually, C doesn't really even have a concept of 'String'. All C knows are sequences of bytes. The C functions in string.h deal with what we term strings by specifying a starting byte address and a null terminator.

                Interfacing with C from PowerBASIC goes easiest by incorporating Asciiz strings in your code. You kill two birds with one stone. First, by their simple declaration such as...

                Local szString As Asciiz * 64

                you've performed a simple memory allocation. In the case above you've got 63 bytes that you can do anything with that you like and won't generate any GPFs.

                Second, all you need to do in terms of use as a parameter is drop it right in the function cuz interfaces to C routines usually (if not always) specify Byref Asciiz.

                What's easier?

                SetWindowText(hWnd,szString)

                or...

                SetWindowText(hWnd,Byval Strptr(strSomeDynamicString))

                Further, the rich PowerBASIC assortment of powerful string functions mostly work fine with asciiz strings.
                Fred
                "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

                Comment


                • #9
                  Fred, I see in "Win32API.bas" that PowerBasic declares the function you mention using a parameter type of ASCIIZ as follows:

                  DECLARE FUNCTION SetWindowText LIB "USER32.DLL" ALIAS "SetWindowTextA" (BYVAL hWnd AS DWORD, lpString AS ASCIIZ) AS LONG

                  The ASCIIZ parameter is passed BYREF by default. The parameter could just as well been declared with an explicit BYREF as follows:

                  DECLARE FUNCTION SetWindowText LIB "USER32.DLL" ALIAS "SetWindowTextA" (BYVAL hWnd AS DWORD, BYREF lpString AS ASCIIZ) AS LONG

                  Passing "strings" to "C" functions is becoming clear to me; both how to declare and how to pass the string data, which is processed in the "C" routine as a null-terminated array of characters. Since the "C" routine processes the string data as an array, we need to pass a pointer to the first element of the array. Therefore, we should declare the parameter as BYREF ACSIIZ when declaring the function, and then call the function in a manner which passes the pointer-to-array, using either the simple syntax Variable_ASCIIZ or more involved BYVAL STRPTR( dynamic_string ), whichever is most convenient for your programming style.

                  Thanks alot. This discussion has been very helpful for me.

                  Sincerely,
                  John Harvill

                  Comment


                  • #10
                    One thing some folks might want to check out is the situation with Visual Basic declares one might encounter in the process of attempting to utilize various vendor's libraries. If my memory serves me right VB 1-6 doesn't have an Asciiz fixed length string type which mimics C character arrays. I do believe declares for C functions in that language do specify 'String' type, but that may very well be fixed length string types, i.e., String * 16 for example.
                    If that is the case VB probably makes sure it 'does the right thing' when it encounters that situation. To be honest, I've never attempted to use PowerBASIC's fixed length string type, i.e., String*16, for example. It could be easily checked though. If those sorts or strings work the way Asciiz strings work though I can't see any compelling reason to use them as you've lost the capacity for dynamic sting memory allocation beyond the fixed length.
                    Fred
                    "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

                    Comment


                    • #11
                      Any time you do "BYVAL whatever" at the the point of call, the DECLARE becomes immaterial for that parameter.

                      While this gives you tremendous flexibility, the cost is the compiler can no longer check your call versus the DECLARE statement and give you a "parameter mismatch" error.

                      In this application, I'd DECLARE the function for PB use with "AS ASCIIZ".

                      I'd call it by assiging the required value to a variable of type ASCIIZ, and not use a point-of-call override.

                      One reason I'd do this is to make sure I got a "parameter mismatch" error if I had - as I have done many times, counted parameters wrong at the point of call and put the ASCIIZ in the wrong place. (Can't do that with only one parameter f course, but habits are habits). In this case the compiler would tell me I had a parameter mismatch BEFORE I ran the program... and knowing about a parameter mismatch is always a good thing to know as early as possible.


                      MCM
                      Michael Mattias
                      Tal Systems (retired)
                      Port Washington WI USA
                      [email protected]
                      http://www.talsystems.com

                      Comment


                      • #12
                        In terms of using Byref szString or Byval Strptr(strDynamic) it just depends on which is easier at the moment. Lets say you were doing some string manipulations with a dynamic string and you wish to put the string in the title caption to a window (hWnd) that has a title bar. This would then be easiest...

                        SetWindowText(hWnd,Byval Strptr(strDynStr))

                        Why take the trouble to declare an Asciiz string, assign the dynamic string to it, then use just szStr in the function call? That would be more work.

                        However, what if you need to get text out of a text box for use in your procedure and you already have a dynamic string declared such as...

                        Local strData As String

                        Can you do this...


                        GetWindowText(hTextBox,Byval Strptr(strData))

                        That is an excellent idea if you want a nice fat GPF!

                        Dynamic strings only hold valid memory when a string is assigned to them through an equal sign or a Let statement. That will cause the PowerBASIC string machinery to be put into action to allocate memory for the string and to cause it to be copied to the valid location. Putting Byval Strptr(strData)
                        in a GetWindowText() function call won't cause PowerBASIC to allocate memory to hold the string, and when the Api attempts to put the characters in a location that it is assumming (and trusting on you to have made valid) has been successfully allocated, that will generate a GPF in the best scenerio and corrupt memory in the worst. For GetWindowText() calls you'll want to allocate memory (easiest by declaring an asciiz string) for the string and put that variable in the GetWindowText() call like so...

                        Local szMem As Asciiz*64

                        GetWindowText(hWnd,szMem,64)

                        Then, if you want to play around with it in a dynamic string assign it to one...

                        strNewString=szMem.

                        Hope this helps
                        Fred
                        "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

                        Comment


                        • #13
                          Question: Is the general convention (or general practice) that the calling routine (ie. my PowerBasic program) is responsible for allocating the memory for a "String" character array before passing it as a parameter to a routine written in C/C++? Is this the understanding in the industry between users and writers of libraries written in C/C++?

                          Sincerely,
                          John Harvill

                          Comment


                          • #14
                            Yes. As a rule when you pass anything to a procedure to be 'filled' or 'modified' it is assumed there is sufficient memory allocated for the procedure to fill it safely. Generally when the procedure fills a string, a count of the available length is passed as an additional parameter.

                            However, a well written procedure will check (use IsBadWritePtr function to do so) and return an "insufficient buffer supplied" error when the buffer is too small for the data.
                            Michael Mattias
                            Tal Systems (retired)
                            Port Washington WI USA
                            [email protected]
                            http://www.talsystems.com

                            Comment


                            • #15
                              Okey. Thankyou.

                              Sincerely Yours,
                              John Havill

                              Comment

                              Working...
                              X