C#中“ref”关键字对COM互操作的奇怪后果

时间:2010-06-29 11:41:33

标签: c# windows com interop com-interop

考虑一下可以找到的代码摘录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()基本上调用LoadIFilter()函数。后者查找注册表,查找哪个类id对应于指定的文件扩展名,实例化相应的COM类(调用CoCreateInstance())并从中调用IPersistFile::Load()

现在问题是LoadIFilter()的签名如下:

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

所以第二个参数是聚合对象的IUnknown*。如果感兴趣的扩展的COM类不支持聚合并且传递的IUnknown*不为空CoCreateInstance()则返回CLASS_E_NOAGGREGATIONLoadIFilter()也是如此。

如果我从声明中的ref参数和pUnkOuter网站上删除了LoadIFilter()关键字,则调用该函数为null IUnknown*。如果我保留ref关键字,则使用非空IUnknown*调用该函数,并为不支持聚合的类返回CLASS_E_NOAGGREGATION

我的问题是 - 为什么在保留关键字时会传递非空IUnknown*IUnknown iunk局部变量初始化为null,因此非空IUnknown*来自调用的非托管代码?

3 个答案:

答案 0 :(得分:3)

当您使用ref时,实际上并未发送null,而是发送了null存储位置的引用,但是当您使用ref时在没有null发送实际值的情况下发送,{{1}}。

所以:

使用ref它是对空指针的引用。

没有它,它只是一个空指针。

编辑:如果我理解你的问题,我不是100%肯定的......

答案 1 :(得分:2)

使用ref是完全错误的,你的IUnknown已经作为指针传递,因为它是一个接口。传递ref将等同于IUnknown **

答案 2 :(得分:1)

非null位来自方法内部 - 它将对象实例化到您提供的引用上。在引用类型上使用ref关键字会将调用者引用传递给该对象,而不是创建对该对象的另一个引用(这是通过引用正常传递时会发生的情况)。试试这个:

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

    bool test = foo == null;
}

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

变量测试将为false。