none
Checking a NULL pointer for an [out] parameter RRS feed

  • Question

  • I have been looking all day but have yet to find the answer. So I will write this tonight and hope someone can give me a quick answer. It is one of those things once you learn it you never forget..

    I want to pass from C++ to C# two pointers. One to a WSTR and another to a DWORD. I want to be able to pass NULL for either and test to see if I passed NULL within the C# code.

    What I was planning to use was the following signature:

    void f([MarshalAs(UnmanagedType.LPWStr, SizeParamIndex=1)] out string Text, out ulong Count);

    I figured I would simply test Text and Count for equality wit null (e.g., if(Text==null) ), but discovered that the C# compiler won't let me use the values without first assigning to them. So I am obviously not using the correct signature.

    Any help would be most appreciated, either in the way of a link to a topic that can explain the rules for this type of problem and any type of data, or some examples for LPWSTR and ulong types at the least.

    Thursday, February 10, 2011 4:44 AM

Answers

  • Hello Greg,

     

    1. >> The C# method is called from C++, not from C#...

    1.1 Called from C++ as in the form of a delegate ? Or is it in the form of a COM method ?

     

    2. The discussion below assumes that the C# function "f" (mentioned in the OP) is a delegate that has been exported out to unmanaged C++ code.

    2.1 Since it is desireable to test whether the C++ unmanaged code has passed NULL value(s) for either "Text" or "Count", I suggest modifying the signature of "f" to the following :

    public delegate void fDelegate(IntPtr strReceiver, IntPtr ulCountReceiver);

    Both "strReceiver" and "ulCountReceiver" are pointers to unmanaged memory and they are both "in" parameters. Being set as "in" parameters is good enough because all an "fDelegate" function needs are pointer values.

    2.2 A function instance of "fDelegate" may then check for NULL pointers in either "strReceiver" and "ulCountReceiver" and take action accordingly.

    2.3 Here is a sample code for a function instance of fDelegate :

            static public void fDelegateFunction(IntPtr strReceiver, IntPtr ulCountReceiver)
            {
                string strToSet = "From Managed Code";
                ulong ulStrLen = (ulong)(strToSet.Length);
                if (strReceiver != IntPtr.Zero)
                {
                    char[] chArray = strToSet.ToCharArray();
                    Marshal.Copy(chArray, 0, strReceiver, (int)ulStrLen);
                }

                if (ulCountReceiver != IntPtr.Zero)
                {
                    Marshal.WriteInt64(ulCountReceiver, (long)ulStrLen);
                }

                return;
            }

    2.4 For example, if the C++ unmanaged code is as follows :

    g_fDelegate(NULL, NULL);

    Then in the fDelegateFunction() C# code, "strReceiver" equals IntPtr.Zero and so no characters from the managed string "strToSet" is copied to unmanaged memory.

    Similarly, "ulCountReceiver" would equal IntPtr.Zero and so the length of the managed string is not copied to unmanaged memory.

    2.5 The following would be a non-trivial call to the "f" function :

    C++ :

    typedef void (__stdcall *fDelegate)(/*[in]*/ LPTSTR s, /*[in]*/ unsigned __int64* pulCountReceiver);

    void __stdcall CallfDelegate()
    {
      if (g_fDelegate)
      {
        wchar_t    wszString[256];
        unsigned __int64 ui64Value = 0;
       
        memset ((void*)wszString, 0, sizeof(wszString));
       
        g_fDelegate((LPTSTR)wszString, &ui64Value);
                     ...
                     ...
                     ...
      }
    }

    A wide character memory buffer and an unsigned __int64 value are declared. Pointers to these are passed to the delegate function call.

    Note that the nature of fDelegateFunction() is such that each character from the managed string is copied to "strReceiver". This excludes the terminating NULL character. Hence inside the CallfDelegate() function, we have specifically set all wide characters inside "wszString" to NULL values, i.e. :

    memset ((void*)wszString, 0, sizeof(wszString));

    What gets passed to the managed fDelegateFunction() are actual unmanaged memory pointers.

    2.6 fDelegateFunction() uses Marshal class functions to do the copying of data to unmanaged memory.

     

    Hope the above will be helpful.

    - Bio.

     

    • Marked as answer by Greg Ofiesh Saturday, February 12, 2011 3:10 AM
    Friday, February 11, 2011 7:12 PM

All replies

  • You need to use ref, not out.

    EDIT: Typed that too fast.  To be honest, I am not sure if ref will work.  Try it out and let me know. :-)


    MCP
    Thursday, February 10, 2011 4:53 AM
  • Hello Greg,

    1. >> I figured I would simply test Text and Count for equality wit null (e.g., if(Text==null) ), but discovered that the C# compiler won't let me use the values without first assigning to them. So I am obviously not using the correct signature.

    1.1 You can initialize "Text" to null before passing it as a parameter to the "f" function, e.g. :

                string Text = null;
                ulong Count = 0;

                if (Text == null)
                {
                    f(out Text, out Count);
                }

    2. Another issue which is of great concern is how the "f" function would internally allocate memory for the "out" string.

    2.1 Basically, since the "out string" Text has been attributed such that it is to be marshaled as LPWStr, internally within the "f" function, you should use the CoTaskMemAlloc() API to allocate memory for the Unicode string to be returned to the "out" (first) parameter.

    2.2 Otherwise, it is possible to get a memory leak or a crash.

    2.3 If "f" had been declared in the following way :

    void f([MarshalAs(UnmanagedType.BStr)] out string Text, out ulong Count);

    Then you must use SysAllocString() to allocate for the BSTR to be returned to the "out" (first) parameter.

    2.4 Note that in both cases, UnmanagedType.LPWStr and UnmanagedType.BStr, you do not need the SizeParamIndex field.

    2.4.1 For UnmanagedType.LPWStr, the returned Unicode string is NULL terminated and so the marshaler will know how to copy the returned unmanaged string (from the CoTaskMemAlloc() allocated memory) to the "Text" C# string.

    2.4.2 For UnmanagedType.BStr, the returned BSTR has size intrinsically included and so the marshaler will also know how to copy the returned unmanaged string (from the SysAllocString() allocated memory) to the "Text" C# string.

    2.5 Note that in both cases, after the returned unmanaged string has been copied to the "Text" C# string, the marshaler will free the unmanaged string using the appropriate free'ing function, CoTaskMemFree() or SysFreeString(). 

    2.6 For more information on interop memory allocation issues, refer to :

    "Marshaling between Managed and Unmanaged Code" by Yi Zhang and Xiaoying Guo

    http://msdn.microsoft.com/en-us/magazine/cc164193.aspx#S6

    Refer to the section "Memory Ownership".

     

    - Bio.

     

    • Proposed as answer by Cookie Luo Friday, February 11, 2011 8:32 AM
    • Unproposed as answer by Greg Ofiesh Saturday, February 12, 2011 3:10 AM
    Thursday, February 10, 2011 6:06 PM
  • WebJose, At first I thought that you were right, but then changing out ulong to ref ulong prevented me from testing for null because null is not a value that ulong can ever be set to. So I began to use ref IntPtr, which is not even clear that this is correct to test for the reference being a NULL pointer. Using ref IntPtr also produces the problem of having int, uint, long, ulong all typed to IntPtr, which just seems wrong on its face. I am not convinced this is the correct way to go about receiving int pointers for out parameters.

    Bio, I did not know that LPWSTR does not require a size param, that is good to know. I assume that the C++ caller handles memory management as you describe. The C# method is called from C++, not from C#.

    Back to my question. How do I define the method signature of a C# method so that I can check in C# for a NULL pointer passed from C++? The article you pointed to does not explicitly discuss this question.

    Using NULL pointers to indicate that no OUT value is requested by the caller is so common in Windows APIs that people must have already come across this issue, thus there must already be an article from Microsoft discussing this point somewhere. At least I would think...

     

     

    Thursday, February 10, 2011 8:15 PM
  • I came across an article on stack overflow that said that you can wrap ulong with a nullable wrapper (beginning with .NET 2.0). The short hand is "ulong?".  My signature now looks like the following:

    void f(StringBuilder Text, ref ulong? Count);

    Then I came across another post that said what I thought - that the pointer is NULL, not the data. Not any closer...

     

    Any thoughts would be appreciated...

    Thursday, February 10, 2011 8:57 PM
  • Hello Greg,

     

    1. >> The C# method is called from C++, not from C#...

    1.1 Called from C++ as in the form of a delegate ? Or is it in the form of a COM method ?

     

    2. The discussion below assumes that the C# function "f" (mentioned in the OP) is a delegate that has been exported out to unmanaged C++ code.

    2.1 Since it is desireable to test whether the C++ unmanaged code has passed NULL value(s) for either "Text" or "Count", I suggest modifying the signature of "f" to the following :

    public delegate void fDelegate(IntPtr strReceiver, IntPtr ulCountReceiver);

    Both "strReceiver" and "ulCountReceiver" are pointers to unmanaged memory and they are both "in" parameters. Being set as "in" parameters is good enough because all an "fDelegate" function needs are pointer values.

    2.2 A function instance of "fDelegate" may then check for NULL pointers in either "strReceiver" and "ulCountReceiver" and take action accordingly.

    2.3 Here is a sample code for a function instance of fDelegate :

            static public void fDelegateFunction(IntPtr strReceiver, IntPtr ulCountReceiver)
            {
                string strToSet = "From Managed Code";
                ulong ulStrLen = (ulong)(strToSet.Length);
                if (strReceiver != IntPtr.Zero)
                {
                    char[] chArray = strToSet.ToCharArray();
                    Marshal.Copy(chArray, 0, strReceiver, (int)ulStrLen);
                }

                if (ulCountReceiver != IntPtr.Zero)
                {
                    Marshal.WriteInt64(ulCountReceiver, (long)ulStrLen);
                }

                return;
            }

    2.4 For example, if the C++ unmanaged code is as follows :

    g_fDelegate(NULL, NULL);

    Then in the fDelegateFunction() C# code, "strReceiver" equals IntPtr.Zero and so no characters from the managed string "strToSet" is copied to unmanaged memory.

    Similarly, "ulCountReceiver" would equal IntPtr.Zero and so the length of the managed string is not copied to unmanaged memory.

    2.5 The following would be a non-trivial call to the "f" function :

    C++ :

    typedef void (__stdcall *fDelegate)(/*[in]*/ LPTSTR s, /*[in]*/ unsigned __int64* pulCountReceiver);

    void __stdcall CallfDelegate()
    {
      if (g_fDelegate)
      {
        wchar_t    wszString[256];
        unsigned __int64 ui64Value = 0;
       
        memset ((void*)wszString, 0, sizeof(wszString));
       
        g_fDelegate((LPTSTR)wszString, &ui64Value);
                     ...
                     ...
                     ...
      }
    }

    A wide character memory buffer and an unsigned __int64 value are declared. Pointers to these are passed to the delegate function call.

    Note that the nature of fDelegateFunction() is such that each character from the managed string is copied to "strReceiver". This excludes the terminating NULL character. Hence inside the CallfDelegate() function, we have specifically set all wide characters inside "wszString" to NULL values, i.e. :

    memset ((void*)wszString, 0, sizeof(wszString));

    What gets passed to the managed fDelegateFunction() are actual unmanaged memory pointers.

    2.6 fDelegateFunction() uses Marshal class functions to do the copying of data to unmanaged memory.

     

    Hope the above will be helpful.

    - Bio.

     

    • Marked as answer by Greg Ofiesh Saturday, February 12, 2011 3:10 AM
    Friday, February 11, 2011 7:12 PM
  • Bio, C# passes its interface to C++ and C++ calls the interface methods as COM methods.

    I was looking at the Marshal object last night. I was thinking this would be a good path to follow, but I need to learn more about it. Thanks for the samples. They should help me move forward. If I can get it working, I will mark your post as the answer.

    Also, from the marshaling point of view, would your use of Marshal be different if it were COM as opposed to PInvoke delegate function?

     

    UPDATE: Oh wow... Just found that LONG in C++ means 32 bits and long in C# means 64 bits... Fancy that...

     

    So it turns out that using Marshal does the trick and provides me with all the access to the pointer values that I need. But it is strange to me that this is the necessary way to code in C# when all along people are telling me to use String and StringBuilder, LPArray, etc. Using Marshal is not intuitive nor is it eloquent. I almost think I should write a C++ unmanaged wrapper around the COM object, then wrap that with C#. It seems the code would look more intuitive and a little more eloquent.

    Is Marshal used for both PInvoke and for COM marshaling? Or is there a difference?

    Friday, February 11, 2011 7:27 PM
  • Hello Greg Ofiesh,

    1. >> ...from the marshaling point of view, would your use of Marshal be different if it were COM as opposed to PInvoke delegate function?

    1.1 It all depends on the declaration of the COM method as viewed by the C++ code. This will determine the nature of the string buffer that the C++ code and the C# code will exchange.

    1.2 How have you declared the "f" C# method ? I assume that you have also run "regasm.exe" after compiling your C# class library (for the C# class that contains the "f" method) with the /tlb flag (thus producing a .tlb file).

    1.3 Open the .tlb file using OLE/COM Object Viewer. How is the "f" method declared ?

     

    2. >> ... Just found that LONG in C++ means 32 bits and long in C# means 64 bits...

    2.1 The 64bit long type in C++ is __int64. The equivalent of the C++ long type in C# is Int32.

     

    3. >> Is Marshal used for both PInvoke and for COM marshaling? Or is there a difference?

    The Marshal class is used whenever managed code has to work with unmanaged memory e.g. memory data exchange. Its usage is not confined to either PInvoke or COM.

     

    - Bio.

     

    Sunday, February 13, 2011 6:06 AM
  • Actually, I do not run regasm.exe. I assume that I would need to use regasm.exe if I am publishing the C# code as a COM server, which I am not. The C# code is the COM client and it has an interface that it hands the C++ code. The C++ code in turn calls back into the C# interface. The C++ code is a COM server. The interface is defined by Microsoft (Text Services Framework). My test code is only an example of marshaling data so that I can get the actual method to work properly. Is there any other purpose for regasm.exe that I should be aware of?
    Monday, February 14, 2011 8:10 PM
  • Hello Greg,

    I am not familiar with the Text Services Framework, unfortunately, so I guess I can't comment much further. All the best to your project.

    - Bio.

     

    Tuesday, February 15, 2011 5:16 PM