Search code examples
c#windowscominteropcom-interop

Strange consequences of "ref" keyword in C# to COM interop


Consider an excerpt from code that can be found here:

namespace WinSearchFile
{
    public class Parser
    {
        [DllImport("query.dll", CharSet = CharSet.Unicode)] 
        private extern static int LoadIFilter (string pwcsPath, ref IUnknown pUnkOuter, ref IFilter ppIUnk); 

        [ComImport, Guid("00000000-0000-0000-C000-000000000046")] 
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
        private interface IUnknown 
        { 
            [PreserveSig] 
            IntPtr QueryInterface( ref Guid riid, out IntPtr pVoid ); 
            [PreserveSig] 
            IntPtr AddRef(); 
            [PreserveSig] 
            IntPtr Release(); 
        } 

        private static IFilter loadIFilter(string filename)
        {
            IUnknown iunk = null; 
            IFilter filter = null;

            // Try to load the corresponding IFilter 
            int resultLoad = LoadIFilter( filename, ref iunk, ref filter ); 
            if (resultLoad != (int)IFilterReturnCodes.S_OK) 
            { 
                return null;
            } 
            return filter;
        }
  }

Parser::loadIFilter() in that code basically calls LoadIFilter() function. The latter looks up the registry, finds which class id corresponds to the specified file extension, instantiates a corresponding COM class (calls CoCreateInstance()) and calls IPersistFile::Load() from it.

Now the problem is that the signature for LoadIFilter() is the following:

HRESULT __stdcall LoadIFilter( PCWSTR pwcsPath, __in IUnknown *pUnkOuter, __out void **ppIUnk );

so the second parameter is IUnknown* of the aggregating object. If the COM class for the extension of interest doesn't support aggregation and the IUnknown* passed is not null CoCreateInstance() returns CLASS_E_NOAGGREGATION and so does LoadIFilter().

If I remove the ref keyword from the pUnkOuter parameter in the declaration and at the site of LoadIFilter() call the function is called with null IUnknown*. If I retain the ref keyword the function is called with non-null IUnknown* and returns CLASS_E_NOAGGREGATION for classes that don't support aggregation.

My question is - why is non-null IUnknown* passed when the keyword is retained? IUnknown iunk local variable is initialized to null so where does non-null IUnknown* come from in the invoked unmanaged code?


Solution

  • Using ref is simply wrong, your IUnknown is already passed as a pointer since it is an interface. Passing ref would be equivalent to IUnknown**