views:

60

answers:

3

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?

+1  A: 

When you use the ref, you're not actually sending in the null, you're sending in a reference to where that null is stored, but when you're sending in without the ref you're sending in the actual value, which is null.

So:

With ref it's a reference to a null pointer.

Without it it's just a null pointer.

Edit: If I understand your question correctly, which I'm not 100% certain off...

ho1
+1  A: 

The non-null bit comes from inside the method - it is instantiating an object onto the reference you've provided. Using the ref keyword on a reference type will pass the callers reference to the object, not creating another reference to the object (which is what happens when you pass by reference normally). Try this:

static void Main()
{
    object foo = null;
    SetMyObject(ref foo);

    bool test = foo == null;
}

public static void SetMyObject(ref object foo)
{
    foo = new object();
}

The variable test will be false.

Adam
+1  A: 

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**

Hans Passant
So do you mean that they effectively pass the address of the local variable and that address which corresponds to `IUnknown**` in C++ terms is interpreted as `IUnknown*` by the callee so that the latter risks full-blown undefined behavior?
sharptooth
That's what I'm saying.
Hans Passant
That sounds horrible yet quite reasonable. Thank you.
sharptooth