You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#中手动调用Marshal.QueryInterface()?

手动调用Marshal.QueryInterface()的细节解析

首先明确:虽然实际开发中直接强制转换COM接口类型(CLR会隐式调用QueryInterface)是更简洁安全的方式,但为了学习手动调用的原理,搞清楚这些细节完全有必要。

针对你的疑问逐一解答:

1. pUnk的来源:Marshal.GetIUnknownForObject(my_ISomething)完全可行

Marshal.GetIUnknownForObject()的作用就是将托管的COM接口实例转换为底层的IUnknown指针(以IntPtr形式返回),这完全符合QueryInterface第一个参数对IUnknown指针的要求。

需要注意:通过这个方法获取的IntPtr会增加COM对象的引用计数,所以**必须在使用完后调用Marshal.Release(pUnk)**来释放引用,避免内存泄漏。

3. 将ppv转换回ISomething2接口

QueryInterface成功返回的ppv是目标接口的原始指针,要转换为托管的接口实例,有两种常用方式:

  • 方式一:使用Marshal.GetObjectForIUnknown(ppv)获取通用对象,再强制转换为目标接口类型
  • 方式二:使用Marshal.CreateWrapperOfType(ppv, typeof(ISomething2))直接创建对应类型的包装对象

同样要注意:QueryInterface返回的ppv也会增加对象引用计数,所以转换为托管对象后,必须调用Marshal.Release(ppv)来释放这个原始指针(托管对象自身会在GC回收时自动释放对应的COM引用)。

完整代码示例

// 假设my_ISomething是已经获取到的有效COM接口实例
IntPtr pUnk = Marshal.GetIUnknownForObject(my_ISomething);
try
{
    // 获取目标接口ISomething2的GUID
    Guid iidISomething2 = typeof(ISomething2).GUID;
    
    // 调用QueryInterface,HRESULT返回0表示成功
    if (Marshal.QueryInterface(pUnk, ref iidISomething2, out IntPtr ppv) == 0)
    {
        try
        {
            // 转换为托管接口实例
            ISomething2 something2 = (ISomething2)Marshal.GetObjectForIUnknown(ppv);
            
            // 在这里使用something2接口的方法
            something2.DoSomething();
        }
        finally
        {
            // 释放QueryInterface返回的指针
            Marshal.Release(ppv);
        }
    }
}
finally
{
    // 释放GetIUnknownForObject获取的IUnknown指针
    Marshal.Release(pUnk);
}

额外注意事项

  • Marshal.QueryInterface的返回值是HRESULT,除了0(S_OK),还可能返回E_NOINTERFACE(找不到目标接口)等错误码,实际测试时可以根据返回值判断调用结果;
  • 手动调用时必须严格遵循COM的引用计数规则,每个AddRef(包括GetIUnknownForObject和QueryInterface)都要对应一个Release;
  • 隐式强制转换的本质是CLR帮你完成了从获取IUnknown指针、调用QueryInterface、转换为托管对象到自动管理引用的全部流程,这也是为什么实际开发中更推荐这种方式。

内容的提问来源于stack exchange,提问作者Nick Gris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 05:12:37