none
How to marshal a 'C' array of strings in c# (COM interop) RRS feed

  • Question

  • Hi, I have a slightly tricky COM interop problem...

     

    I have imported an unmanaged COM Dll (Added a reference in visual studio). This has correctly generated the required interop dll (using Tblimp, or whatever its called).

     

    One of the imported methods has the 'C' signature: "void AT91Boot_Scan(char *pDevList);"

    This is converted to c# "void AT91Boot_Scan(ref byte pDevList);"

     

    pDevList is an output parameter, 10 strings of >80 bytes according to the docs.

     

    Now I believe I need to disasssemble the genererated dll and change the method signature, which I can do.

     

    I *think* I need to change it to take a reference to an IntPtr (native int in il), which would create a c# signature of  "void AT91Boot_Scan(ref IntPtr pDevList);"

     

    I then need to allocate the memory, marshal it create and pass the IntPtr, and unmarshal the memory to a string.

     

    The trouble is, I do not know the correct way to accomplish all this, and everything I have tried so far has resulted in an access violation...

     

    Any suggestions/help appreciated.

     

    Thanks,

     

    Simon.

     

     

    //

    The working 'C' code is as follows:

    //

    unsigned char *pTest[10];

       

      // MANDATORY: Allocate memory for each string in the table
      for (i=0; i<10; i++)
       pTestIdea = (unsigned char *)malloc(100);

      // Scan all devices connected
      pAT91BootDLL->AT91Boot_Scan((unsigned char *)pTest);

      // Print all devices connected
      i= 0;
      while (pTestIdea != 0)
      {
       printf("Device %d = %s\n\r", i, pTestIdea);
       i++;
      }

     

    Monday, February 25, 2008 9:21 AM

Answers

  •  S_Banks wrote:

     

    So I probably need the MSIL equivalent of the code you provided, but so far have had no luck finding the correct syntax.

     

    That should be

     

    void AT91Boot_Scan(string[] marshal(lpstr[]) pDevList)

    Tuesday, February 26, 2008 6:59 AM
    Moderator

All replies

  • If you change the signature to be [MarshalAs(UnamangedType.LPArray, ArraySubType=UnmanagedType.LPStr)] string[], the CLR will do most of the work for you.

    Monday, February 25, 2008 1:57 PM
    Moderator
  • Thanks for your reply.

     

    I think this would work for P/Invoke, I would be able to use the MarshalAs attribute exactly as you describe on the method wrapper. I did try to access the dll via p/invoke in this way but it didnt work, "no entry point found".

     

    So I *think* I have to modify the COM runtime callable wrapper (generated by tlbimp) and correct the signature

     

    And the MarshalAs attribute is not well documented for MSIL. msil il

     

    So I probably need the MSIL equivalent of the code you provided, but so far have had no luck finding the correct syntax.

     

    Due to lack of COM interop experience I might be making this more difficult than it needs to be though Smile

    Monday, February 25, 2008 3:27 PM
  • The Object Browser should tell you how to call the methods in the dll.  Intellisense should also.

    Monday, February 25, 2008 10:11 PM
  •  S_Banks wrote:

     

    So I probably need the MSIL equivalent of the code you provided, but so far have had no luck finding the correct syntax.

     

    That should be

     

    void AT91Boot_Scan(string[] marshal(lpstr[]) pDevList)

    Tuesday, February 26, 2008 6:59 AM
    Moderator
  • That works perfectly, Thank you.

     

    I knew there must be an easy solution.  Smile

     

    For the record, the final code is:

     

    C                void AT91Boot_Scan(char *pDevList)

    MSIL           void AT91Boot_Scan([in][out] string[] marshal(lpstr[]) pDevList)

    C#

     

    String[] s = new string[10];

    for(int i = 0; i < s.Length; i++)

    {

    s[ i ] = new string(new char[255]);

    }

    dll.AT91Boot_Scan(s);

     

    Modifying COM Runtime Callable Wrappers (RCW) generated by TlbImp.exe.

     

    http://msdn2.microsoft.com/en-us/library/8zbc969t(VS.71).aspx

    Tuesday, February 26, 2008 8:31 AM
  •  S_Banks wrote:

    That works perfectly, Thank you.

     

    I knew there must be an easy solution. 

     

    For the record, the final code is:

     

    C                void AT91Boot_Scan(char *pDevList)

    MSIL           void AT91Boot_Scan([in][out] string[] marshal(lpstr[]) pDevList)

    C#

     

    String[] s = new string[10];

    for(int i = 0; i < s.Length; i++)

    {

    s[ i ] = new string(new char[255]);

    }

    dll.AT91Boot_Scan(s);

     

    Modifying COM Runtime Callable Wrappers (RCW) generated by TlbImp.exe.

     

    http://msdn2.microsoft.com/en-us/library/8zbc969t(VS.71).aspx

     

    Can someone help me a little more? i tried it like you wrote in the posts before. But i still get this Exception:

     

    Ausnahmefehler des Servers. (Ausnahme von HRESULT: 0x80010105 (RPC_E_SERVERFAULT))

     

    Thanks Simon

    Thursday, May 29, 2008 11:25 AM
  • Hi Mattias, I am trying to do the exact same thing as the poster above, interop with the same COM library as well.

    When I implement the solution just as S_Banks does, the first element of the string array that comes back from dll.AT91Boot_Scan(s) is a null reference. This is a problem, because I get a valid string result in the first element if I allocate unmanaged memory using Marshal.AllocHGlobal(), set up the array manually in unsafe code and pass a pointer with the MSIL edited thusly:

    void AT91Boot_Scan([in][out] int32* pDevList)

    I'd like to use the lpstr[] marshalling you describe, as it is so much cleaner and avoids unsafe code. Do you have any suggestions for why this might be happening?

    Thanks so much!

    Thursday, January 21, 2010 8:28 PM
  • S_Banks ,

    I would like to thank you for posting your findings and results in such great detail. I had the *Exact* same issue down to the last dot. I was able to follow your path and get to a result via your explainations and comments. Thank you.

    Mattias Sjögren ,
    I would also like to thank you for your support.



    Nigsch Simon ,
    I solved the issue I was having with that error. Make sure all of the work that is being done is done by the same thread. To consolidate this issue I made the GUI thread do the work by invoking each of the commands. It's inefficient but it seems that some functions (particularly the scan) do not like it when another thread accesses the COM object.


    • Proposed as answer by BlndLeadingDef Thursday, February 11, 2010 6:50 PM
    Wednesday, February 3, 2010 7:51 PM
  • Hi, guys,

    Thanks a lot for the posts. They saved me a lot of trouble.

    Friday, January 6, 2012 3:41 PM
  • Hello S Banks,

     

    I used this solution and it worked fine for Scanning devices. But now Im trying to use the AT91Boot_Open, read and write functions, and it have similar errors. Could you please advise how to fix this.

     

    Thanks in advance

    Tuesday, January 17, 2012 11:47 PM