none
C# program passes an uint[] which contains a string pointer to a C++ dll RRS feed

  • Question

  • I know there is a lot of discussions regarding passing pointer from C# program to a third party C++ dll. But my case is a special one.

    I have a third party C++ DLL and I call a function in it by this:

    [DllImport(@"ThirdParty.DLL", EntryPoint = "?ThirdPartyEntryPoint", CallingConvention = CallingConvention.Cdecl)]
    public static extern Int16 ThirdParty_command(uint p1, ushort p2, uint[] data);

    Now the tricky thing here is that one of the elements in "uint[] data" is a string pointer which points to a string in C++ (type: char* ). I tried this but it doesn't work:

    String name = "myName";
    fixed (char* nameAddress = name)
    {
       
    uint[] data = { 0x00, 0x01, 0x02,(uint) nameAddress };
       
    Int16 result = ThirdParty_command(0, 0, data);
    }
    It gives error "Too many parameters". I can use ThirdParty_command for other purpose without problem (if "data" doesn't contans a pointer)

    qiuwei

    Thursday, May 31, 2012 10:36 AM

Answers

  • What type variable are the first two zero parameters?.  C language calling conventions stores the passed parameter list as consecutive bytes on the stack, and arrays are stored as consecutive bytes in the memory (or on the stack).

    I believe you code isn't working because the parameter list need to be in static memory space.  The dll won't recognize a private pointer variable that is local.  Try ths code below

    [DllImport(@"ThirdParty.DLL",
    EntryPoint = "?ThirdPartyEntryPoint",
    CallingConvention = CallingConvention.Cdecl)]
    public static extern Int16 ThirdParty_command(uint p1, ushort p2, uint[] data);

    static extern IntPtr buffer;
    static extern uint[] data = { 0x00, 0x01, 0x02,(uint) nameAddress };

    String name = "myName";
    fixed (char* nameAddress = name)
    {   
       buffer = data; 
       Int16 result = ThirdParty_command(0, 0, buffer);
    }


    jdweng

    Friday, June 1, 2012 12:12 AM
  • I created a small C# and a C++ project to test passing an uint[] array which contains a pointer pointing to a string. I use "IntPtr ptr = Marshal.StringToCoTaskMemAnsi(myString)" to get the pointer and store it in the array. the C++ side can retrieve the string without any problem. So It could be the third party dll issue

    C# code:

    [DllImport(@"DD.dll", EntryPoint = "?TestPointerInArray@PointerTest@PointerDLL@@SANGGQAK@Z", CallingConvention = CallingConvention.Cdecl)]
            public static extern byte PointerTest(uint address,
                                                      ushort datasize, uint[] cmddata);

            static void Main(string[] args)
            {
                    string name = "myName";
                    IntPtr ptr = Marshal.StringToCoTaskMemAnsi(name);
                    uint[] myarray = { 0x01, 0x02, 0x03, (uint)ptr };
                    double result = PointerTest(0, 4, myarray);

            }

    DD.cpp code:

    double PointerTest::TestPointerInArray(UINT16_T address,
                                                         UINT16_T count ,UINT32_T cmd_data[])
        {
            char* mystring = (char*)cmd_data[3];
            return 1.0;//return a dummy value.
        }


    qiuwei

    Thursday, June 7, 2012 12:37 AM

All replies

  • What gives that error, does it happen at runtime? Because this code compiles fine.

    One potential problem is that .NET's strings are Unicode so .NET's char * is not the same thing as C++'s char *. Try this code instead:

                String name = "myName";
                IntPtr ptr = Marshal.StringToCoTaskMemAnsi(name);
                uint[] data = { 0x00, 0x01, 0x02, (uint)ptr };
                Int16 result = ThirdParty_command(0, 0, data);
                Marshal.FreeCoTaskMem(ptr);

    Thursday, May 31, 2012 10:56 AM
    Moderator
  • It would depend which version of visual studio you were running.  Visual Studio 2010 they made it more user friendly by eliminating so of the C# errors.

    Parameter arre pushed onto the stack in the reverse order from the parameter list.  you need to cast the 0's to the type (uint).  the problem is you only can pass one paramater so try the code below.

    String name = "myName";
    fixed (char* nameAddress = name)
    {
       
    uint[] data = { 0x00, 0x01, 0x02,(uint) nameAddress };
       
    Int16 result = ThirdParty_command(data);
    }

    Or change the following

    IntPtr ptr = Marshal.StringToCoTaskMemAnsi(a, b, name);



    jdweng

    Thursday, May 31, 2012 2:53 PM
  • I thought about that as well. Unicode takes 2 bytes, char in C++ takes one byte. I tried your code but it still doesn't work.

    If "data" is constructed properly, "result" returned from the call will give "0". if not, it gives other error code. Now I am getting the error description "Too many parameters". My feeling is that "ThirdParty.DLL" doesn't recognize the string pointer. I am pretty sure 0x00,0x01,0x02 are in right format (Because I can use these with other data)


    qiuwei

    Thursday, May 31, 2012 11:38 PM
  • Joel;

         Thank you for you reply. But I have to call the4 method as "ThirdParty_command(0,0,data)";

        I tried "Marshal.StringToCoTaskMemAns", it doesn't work.


    qiuwei

    Thursday, May 31, 2012 11:42 PM
  • What type variable are the first two zero parameters?.  C language calling conventions stores the passed parameter list as consecutive bytes on the stack, and arrays are stored as consecutive bytes in the memory (or on the stack).

    I believe you code isn't working because the parameter list need to be in static memory space.  The dll won't recognize a private pointer variable that is local.  Try ths code below

    [DllImport(@"ThirdParty.DLL",
    EntryPoint = "?ThirdPartyEntryPoint",
    CallingConvention = CallingConvention.Cdecl)]
    public static extern Int16 ThirdParty_command(uint p1, ushort p2, uint[] data);

    static extern IntPtr buffer;
    static extern uint[] data = { 0x00, 0x01, 0x02,(uint) nameAddress };

    String name = "myName";
    fixed (char* nameAddress = name)
    {   
       buffer = data; 
       Int16 result = ThirdParty_command(0, 0, buffer);
    }


    jdweng

    Friday, June 1, 2012 12:12 AM
  • Hmm, how is that function declared in C++? Is "data" a pointer to unsigned int and you have documentation telling you what to put in that array?
    Friday, June 1, 2012 7:00 AM
    Moderator
  • Thanks Joel. I was sick today.

    The code doesn't compile. But it could be a reason.

    I am trying the suggestion you gave.


    qiuwei

    Friday, June 1, 2012 8:37 AM
  • First variable is "uint", the second one is "ushort".

    qiuwei

    Friday, June 1, 2012 8:39 AM
  • ThirdParty_command(UINT16_T para1,UINT16_T datasize, UINT32_T data[]);

    where "UINT16_T" is "unsigned short"

    "UINT32_T" is "unsigned long"

    "datasize" is the length of data[].

    "data[]" is not a pointer, it stores some commands and data which are passed to the C++ dll.

    The document says if I want to pass a string to the C++ dll, I have to store the pointer of the string in the data array.

    As I said in my question, I can use this method in other places(no pointer in the "data[]" array) without any problem.

    Thank you very much for your time to look at this.


    qiuwei


    • Edited by Qiuwei Friday, June 1, 2012 8:55 AM
    Friday, June 1, 2012 8:54 AM
  • Try this instead.  I'm not sure if short is 32 and int is 64.  when not sure make the code specific.

    public static extern Int16 ThirdParty_command(int16 p1, int16 p2, int32[] data);


    jdweng

    Friday, June 1, 2012 9:15 AM
  • I tried. I still doesn't work.

    But this time it gives different error: wrong data type.


    qiuwei

    Friday, June 1, 2012 9:45 AM
  • "Wrong data type"?

    That's weird, there's should be no difference between those 2 calls, at least not with the parameter values (0) you have shown in your first example.

    Friday, June 1, 2012 10:44 AM
    Moderator
  • Sorry, my bad , the error detail is "Undefined data type"


    qiuwei



    • Edited by Qiuwei Friday, June 1, 2012 11:53 AM
    Friday, June 1, 2012 11:35 AM
  • Was the error a compiler error or a run time error?  do you have tthe dll listed aas a refernce item in your project.  If you do, try remove the reference.

    jdweng

    Friday, June 1, 2012 12:29 PM
  • This is a run time error. It is not a referenced item.

    qiuwei

    Friday, June 1, 2012 11:09 PM
  • That is great.  The dll is giving the error.  Not you have to make sure the data you are paaing meets the dlls requirements.

    ThirdParty_command(UINT16_T para1,UINT16_T datasize, UINT32_T data[]);

    1)  The first parameter I  suspect is a command number and right now you are using 0.  check to make sure this is correct

    2) The 2nd parameter is datasize which normally in this situation has to be the number of bytes in the 3rd parameter.  Again you have zero

    3) Make sure the characters you are sending are correct format.  If you are sending characters dll often want the '\0' as the last character (null terminated) and the datasize sometimes want the null included in the datasize.  dlls sometimes want to see a CR and or LF and or CRLF\0.


    jdweng

    Saturday, June 2, 2012 5:02 AM
  •   The first parameter I  suspect is a command number and right now you are using 0.  check to make sure this is correct

    --->The first parameter is my device address which is "0" here.  I am 100% sure this is correct. I have used this lots of times in other places to talk to my device.

    The 2nd parameter is datasize which normally in this situation has to be the number of bytes in the 3rd parameter.  Again you have zero

    -->The second parameter is "(ushort)(data.Length)" (Sorry I used "0" because I didn't think it might be a problem, so I just gave it a random figure when I posted this question)

    "data[]" stores commands and data (like some a string needs to be displayed on the device)

    You are right, dll asks for a "null-terminated"string which I have already  tried like "myName\0", byte[] nameString = {0x65, 0x63, 0x00}; "\0myName"; byte[] nameString = {0x00, 0x65, 0x63};sbyte[] nameString = {0x65, 0x63, 0x00};...anything I can think about with my current knowledge (obviously not enough for this case)

    My guess here is that the "thirparty.dll" cannot "see" the string using the pointer passed in for a reason.

    Thank you Joel for your time!


    qiuwei

    Saturday, June 2, 2012 9:00 AM
  • The string simply has to be in Static memory space.  That is why I said in my earlier posting

    static extern uint[] data = { 0x00, 0x01, 0x02,(uint) nameAddress };


    jdweng

    Saturday, June 2, 2012 10:20 AM
  • I did try that. It doesn't work.

    qiuwei

    Saturday, June 2, 2012 11:36 AM
  • You could always build a simple C++ library and then test your code with the library.  I would create a c++ class project with one function and create a dll.  Then add a reference in your C# project to the c++ project dll by browsing for the dll file as a reference.  You can then single step into the C++ code from C# and check to make sure everything works properly.

    jdweng

    Saturday, June 2, 2012 12:07 PM
  • Mmm..Yep. This is the last option.

    Time to invest time on C++.

    Thanks Joel.


    qiuwei

    Tuesday, June 5, 2012 12:02 AM
  • I created a small C# and a C++ project to test passing an uint[] array which contains a pointer pointing to a string. I use "IntPtr ptr = Marshal.StringToCoTaskMemAnsi(myString)" to get the pointer and store it in the array. the C++ side can retrieve the string without any problem. So It could be the third party dll issue

    C# code:

    [DllImport(@"DD.dll", EntryPoint = "?TestPointerInArray@PointerTest@PointerDLL@@SANGGQAK@Z", CallingConvention = CallingConvention.Cdecl)]
            public static extern byte PointerTest(uint address,
                                                      ushort datasize, uint[] cmddata);

            static void Main(string[] args)
            {
                    string name = "myName";
                    IntPtr ptr = Marshal.StringToCoTaskMemAnsi(name);
                    uint[] myarray = { 0x01, 0x02, 0x03, (uint)ptr };
                    double result = PointerTest(0, 4, myarray);

            }

    DD.cpp code:

    double PointerTest::TestPointerInArray(UINT16_T address,
                                                         UINT16_T count ,UINT32_T cmd_data[])
        {
            char* mystring = (char*)cmd_data[3];
            return 1.0;//return a dummy value.
        }


    qiuwei

    Thursday, June 7, 2012 12:37 AM