Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing NULL to ref/out parameter of a COM interface method

How to pass NULL to a parameter of a COM interface method if it is defined like [In, Out] ref int pchEaten?

For example, consider the following interface:

[ComImport, Guid ("000214E6-0000-0000-C000-000000000046")]
[InterfaceType (ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellFolder
{
    void ParseDisplayName (
        [In] IntPtr hwnd,
        [In] IBindCtx pbc,
        [In, MarshalAs (UnmanagedType.LPWStr)] string pszDisplayName,
        [In, Out] ref uint pchEaten,
        [Out] out PIDLIST ppidl,
        [In, Out] ref SFGAO pdwAttributes);
    // ...
}

MSDN says the following about the pchEaten parameter: A pointer to a ULONG value that receives the number of characters of the display name that was parsed. If your application does not need this information, set pchEaten to NULL, and no value will be returned. The pdwAttributes parameter can also be set to NULL.    

However, when I call the ParseDisplayName method from C#, I see no way to pass null to a ref parameter.

If I were calling functions from a DLL, I could import a function multiple times: one with IntPtr parameter, one with proper signature, and choose the overload depending on wether I need to pass and receive the values. However, if I try importing the same method multiple times in COM, it won't work as the order of methods is crucial and function pointers will shift.

Question: How to make calling a COM method both with a value and NULL of an in/out parameter possible?

Note: This is just an example. I know I can create a dummy variable, pass it to a ref parameter and ignore the returned value. However, behavior of a method may depend on wether the value is NULL, non-null value may incur performance costs etc., so I'd like to avoid it.

like image 928
Athari Avatar asked Mar 23 '23 07:03

Athari


1 Answers

You can redefine the interface like this for example:

internal interface IShellFolder
{
    void ParseDisplayName (
        [In] IntPtr hwnd,
        [In] IBindCtx pbc,
        [In, MarshalAs (UnmanagedType.LPWStr)] string pszDisplayName,
        IntPtr pchEaten,
        [Out] out PIDLIST ppidl,
        [In, Out] ref SFGAO pdwAttributes);
    // ...
}

And call it like this if you want to pass null:

ParseDisplayName(...., IntPtr.Zero, ...);

Or like this if you want to pass a uint value:

IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(uint)));
uint i = 1234;
Marshal.StructureToPtr(i, p, false);
ParseDisplayName(...., p, ...);
i = (uint)Marshal.PtrToStructure(p, typeof(uint)); // read the value back
Marshal.FreeHGlobal(p);
like image 55
Simon Mourier Avatar answered Apr 06 '23 18:04

Simon Mourier