CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 8 of 8
  1. #1
    Join Date
    Jun 2005
    Posts
    7

    Angry DllImport problems with c-style char arrays

    Hi guys,

    I have function written in C

    extern "C" __declspec(dllexport) char * receiveSTATUS()
    {
    ....
    ....
    }

    that returns a pointer to a string of characters. This function is in a dll file (pfserver.dll) which i have imported into a C# .NET application using the DLL import attribute like...


    [DllImport("pfserver.dll")]
    public static extern string receiveSTATUS();

    so i presume the c-style char array gets marshalled into a string in C#. The function is called in the C# app like...

    string str = receiveSTATUS();

    This method works for the most part but sometimes the string str (received in the C# app) has some garbage values in the beginnig few places e.g.

    string to be returned "192.168.1.10|-"
    string received "!,##!,#.1.10|-"

    (the #'s are garbage characters that dont have a print representation and get printed as rectangles)

    Most of the time this works... i.e I get the correct string back into the C# app but sometimes this weird garbage gets returned. I have no idea how to fix this. Maybe i need to do some marshalling using MarshalAs ?

    Thanks for your help in advance

    Z
    Last edited by zubinr; July 28th, 2005 at 03:33 PM.

  2. #2
    Join Date
    Jan 2002
    Location
    Scaro, UK
    Posts
    5,940

    Re: DllImport problems with c-style char arrays

    Firstly, I seriously hope that you're not expecting C# to delete the memory of the string. Because it won't. You'll have a memory leak problem if you're expecting this.

    Secondly you shouldn't be passing pointers to strings back like this : you should first pass the length, let C# allocated the memory and then fill that memory from the dll e.g.

    Code:
    void __stdcall GetString(LPSTR szString, int *pnLength)
    {
        CString sString = "Hello";
    
        if (szString == NULL)
        {
            *pnLength = sString.GetLength();
        }
        else
        {
            const int nToCopy = min(*pnLength, sString.GetLength());
            ::strncpy(szString, sString, nToCopy);
            *pnLength = nToCopy);
        }
    }
    
    // C#
    class Interop
    {
        [DllImport("MyDll")]
        static private unsafe extern void GetString(byte *pbChars, ref int nLength);
    
        static public unsafe string GetString()
        {
            int nLength = 0;
    
            GetString(null, ref nLength);
    
            byte [] abChars = new byte[nLength];
    
            fixed (byte *pbChars = abChars)
            {
                    GetString(pbChars, ref nLength);
            }
    
            return System.Text.Encoding.ASCII.GetString(abChars);
         }
    }
    Thirdly, I've already discussed this very issue at length in the following thread, with a possible alternative which I haven't tested.

    http://www.codeguru.com/forum/showpo...9&postcount=47

    Darwen.
    Last edited by darwen; July 28th, 2005 at 11:09 PM.
    www.pinvoker.com - PInvoker - the .NET PInvoke Interface Exporter for C++ Dlls.

  3. #3
    Join Date
    Jun 2005
    Posts
    7

    Re: DllImport problems with c-style char arrays

    hey darwen,

    Thank you very much for all the info. I am going to try it right away. I didnt realize the memory leak problem. I have never used C# Interop (for that matter any C#) before. Im learning.

    Thanks,
    Z

  4. #4
    Join Date
    Jun 2005
    Posts
    7

    Re: DllImport problems with c-style char arrays

    Hi,

    So this method works well but, in my case my C - function which returns the string, sends a UDP broadcast message, fills up a string and then returns the string. Since this function will be called twice from C# (first time to get the length, second time to fill the memory). i see two issues...

    1. Too much delay i.e my dll function takes about 2 seconds to complete... thats 4 seconds of delay if it is called twice.

    2. Everytime the function is called a different string could be generated depending on the network parameters.

    I tried using the two methods in your other post about sending an IntPtr and then MarshalAs.PtrToStr... but that results in the same garbage characters being added to my returned string. The funny thing is the string is clean in the DLL (if i print it out to the console) but gets corrupted when i print it in C#.

    Any ideas?

    Thank you,
    Z

  5. #5
    Join Date
    Jan 2002
    Location
    Scaro, UK
    Posts
    5,940

    Re: DllImport problems with c-style char arrays

    Using an IntPtr is exactly the same method as using a byte * - only I find the latter much more readable.

    1. Too much delay i.e my dll function takes about 2 seconds to complete... thats 4 seconds of delay if it is called twice.
    I would recommend you look at the code in the method. You should be able to copy well over 1Gb of RAM in 2 seconds, so 50k or whatever length you're copying should take next to no time.

    I can transfer a bitmap image (720x480x4) in about 2ms, and that's only on a 2.8GHz machine with 256Mb of RAM.

    You could always have a seperate method which gets the length, caches the string, and then the call to copy the memory into the C# buffer releases the memory afterwards.

    However 2 seconds is a very, very, very long time in PC terms so it looks likely you're doing something wrong in the dll. I'd have a look at the dll code and see if you can't speed it up. And still use my method : it's major advantage is the fact that you can pass code pages into the encoding, so it could be made multi-lingual if necessary.

    In fact, that's probably exactly what is happening depending on the language you've got installed on your OS.

    Darwen.
    www.pinvoker.com - PInvoker - the .NET PInvoke Interface Exporter for C++ Dlls.

  6. #6
    Join Date
    Jun 2005
    Posts
    7

    Re: DllImport problems with c-style char arrays

    Hi Darwen,

    Actually the 2 second delay is not the time taken to copy the string. As I wrote in my previous post the function in the DLL sends a broadcast mesasge on UDP port 2000 and waits for replies (with receive timeout 2 seconds) and then builds a string with the IP addresses of the machines found on the network and returns that string.

    I'm runing WXP SP2 on a 3.06 GHz machine with 512 mb ram. It wont take this long to copy a string even if I did it in the most inefficient way possible!!!

    So the function (by design) will take 2 seconds to complete. Calling it twice will result in waiting for 2 seconds again to build the string. And also then if a machine goes down in the meantime, the second call to the function would not return the same string.

    I really appreciate your help though, your method gets rid of the garbage characters but i cant apply it to my requirement. I tried it with a static string. and it works properly. Anyway i will try to think up another way of passing the length of the string before the actual string without calling the function twice. If you have any ideas please let me know... If i find a work around i will post here too!

    Thank you,
    Z

  7. #7
    Join Date
    Jan 2002
    Location
    Scaro, UK
    Posts
    5,940

    Re: DllImport problems with c-style char arrays

    Like I said, why don't you cache the string like this :

    Code:
    void __stdcall GetString(LPSTR szString, int *pnLength)
    {
        static CString sString;
    
        if (szString == NULL)
        {
            sString = GetStringFromUdp();
            *pnLength = sString.GetLength();
        }
        else
        {
            const int nToCopy = min(*pnLength, sString.GetLength());
            ::strncpy(szString, sString, nToCopy);
            *pnLength = nToCopy;
        }
    }
    This only does the UDP call once.

    Not particularly nice though, it'll screw up if you ever wanted to make your app multithreaded : I'd prefer methods of the following kind :

    Code:
    void * __stdcall InitialiseCallResult()
    {
        CString *psString = new CString;
        *psString = GetFromUdp();
        return psString;
    }
    
    void __stdcall GetCallResult(void *pString, LPSTR szString, int *pnLength)
    {
        CString *psString = (CString *)pString;
    
        if (szString == NULL)
        {
            *pnLength = psString->GetLength();
        }
        else
        {
            const int nToCopy = min(*pnLength, psString->GetLength());
            ::strncpy(szString, *psString, nToCopy);
            *pnLength = nToCopy;
        }
    }
    
    void __stdcall ReleaseCallResult(void *pString)
    {
        CString *psString = (CString *)pString;
        delete psString;
    }
    
    // C#
    // C#
    public class Interop
    {
       [DllImport("MyDll.dll")]
       static private extern IntPtr InitialiseCallResult();
    
       [DllImport("MyDll.dll")]
       static private unsafe extern void GetCallResult(IntPtr pHandle, byte *pbData, ref int pnLength);
    
       [DllImport("MyDll.dll")]
       static private extern void ReleaseCallResult();
    
       static public string GetString()
       {
          IntPtr pHandle = InitialiseCallResult();
    
          int nLength = 0;
    
          GetCallResult(pHandle, null, ref nLength);
    
          byte [] abData = new byte[nLength];
    
          fixed (byte *pbData = abData)
          {
             GetCallResult(pHandle, pbData, ref nLength);
          }
    
          ReleaseCallResult(pHandle);
       }
    }
    Of course the real nice way of doing it is to use COM - but the above will suffice I would suggest.

    Darwen.
    Last edited by darwen; July 29th, 2005 at 03:49 PM.
    www.pinvoker.com - PInvoker - the .NET PInvoke Interface Exporter for C++ Dlls.

  8. #8
    Join Date
    Mar 2010
    Posts
    1

    Re: DllImport problems with c-style char arrays

    Hi folks,
    I've got there a problem, too.

    my function in the MFC-Dll returns a allocated BYTE array (BYTE* buffer).
    I wrote a Wrapper for this dll an can know call the class-methods in my C# program.
    But only one function makes problems --> function which returns the BYTE-buffer.
    After reading this thread, I tried to adjust the function call in the following way:

    Wrapper function:
    Code:
    void getBitmap(long *length, BYTE **imageBuffer)
    {
    	return _class->getBitmap(length, imageBuffer);
    }
    C# App:
    Code:
    public unsafe byte[] getBitmap()
    		{
    			int bufferSize = 0;
    
    			m_myWrappedMFCClass.getBitmap(ref bufferSize, null);
    
    			byte[] abChars = new byte[bufferSize];
    
    			fixed (byte* pbChars = abChars)
    			{
    				m_myWrappedMFCClass.getBitmap(ref bufferSize, pbChars);
    			}
    
    			return abChars;
    		}
    I get the following compiler errors for both function calls:
    - The best overloaded method match for 'MyWrapperNamespace.ManagedClassAPI.getBitmap(int*, byte**)' has some invalid arguments
    - Argument '1': cannot convert from 'ref int' to 'int*'
    and for the last additionally:
    - Argument '2': cannot convert from 'byte*' to 'byte**'

    That's my first contact with dll-imports and above all with dll imports from MFC to .NET

    What am i doing wrong?

    Some pointer problems? It has been a while

    Thank your beforehand.

    greetz
    steph

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured