【问题标题】:Deriving COM interfaces in .NET在 .NET 中派生 COM 接口
【发布时间】:2010-09-16 18:28:23
【问题描述】:

由于我无法控制的公司限制,我有以下情况:

定义以下接口的 COM 库(没有 CoClass,只有接口):

[
    object,
    uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx),
    dual,
    nonextensible,
    helpstring("IService Interface"),
    pointer_default(unique)
]
IService : IDispatch
{
  HRESULT DoSomething();
}

[
    object,
    uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx),
    dual,
    nonextensible,
    helpstring("IProvider Interface"),
    pointer_default(unique)
]
IServiceProvider : IDispatch
{
  HRESULT Init( IDispatch *sink, VARIANT_BOOL * result );
  HRESULT GetService( LONG serviceIndicator, IService ** result );
};


[
    uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx),
    version(1.0),
]
library ServiceLibrary
{
    importlib("stdole2.tlb");

   interface IService;
   interface IServiceProvider;
};

我有一个 COM(用 C++ 编写),它实现了这两个接口并为我们的应用程序提供了上述服务。我认为一切都很好。

我正在尝试在 .NET (C#) 中构建新的 IProviderIService

我为 COM 库构建了一个主互操作程序集,并实现了以下 C#:

[ComVisible( true )]
[Guid( "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" )]
public interface INewService : IService
{
   //  adds a couple new properties
}

[ComVisible( true )]
[Guid( "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" )]
public class NewService : INewService
{
   //  implement interface
}

[ComVisible( true )]
[Guid( "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" )]
public interface INewProvider : IServiceProvider
{
   //  adds nothing, just implements
}

[ComVisible( true )]
[Guid( "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" )]
public class NewProvider : INewProvider
{
   //  implement interface
}

当我尝试将其放入现有运行时时,我能够从 COM (C++) 创建 NewProvider 对象,并为 IServiceProvider 创建 QueryInterface。当我尝试调用 IServiceProvider 上的方法时,会抛出 System.ExecutionEngineException

我唯一能找到的另一件事是,通过查看由#import 创建的 .tlh 文件,它显示了旧 COM IExistingProvider 类正确地表明它是从 IServiceProvider 派生的。但是 .NET 类显示了 IDispatch 的基础。我不确定这是否是一个标志、指示、有用的东西。

【问题讨论】:

  • 当你说你已经“构建”了一个主互操作程序集时,你的意思是从头开始吗?难道不能只是添加 COM 库作为参考吗?
  • 我从 TLBIMP.exe 构建了 PIA。您想查看它的示例命令行吗?我使用了 TLBIMP,创建了我自己的 Interop.Services.dll,然后引用它。我最近获得了“.NET 和 COM - 完整的互操作性指南”。
  • 您没有将库直接导入 Visual Studio 有什么原因吗?或者您没有使用 Visual Studio?

标签: c# c++ .net interface com


【解决方案1】:

您可能必须在您的类上指定其他属性才能使其正确编组。我会查看here 的可用属性,如果您还没有这样做的话,也许可以查看this tutorial

【讨论】:

  • 1+废话,codeprojects 文章应该是一本怪书。它的巨大!
【解决方案2】:

这可能是名称​​IServiceProvider 的问题。检查您是否尚未导入具有相同名称的接口。

当我使用您的 IDL 创建 COM 接口库,然后尝试从另一个客户端导入它时,我收到警告:

Warning 65  warning C4192: automatically excluding 'IServiceProvider' while importing type library 'ServiceLibrary.dll'

否则,您可以尝试将其重命名为 IServiceProvider2。这就是我所做的,一切正常。我正在使用 Visual Studio 2008。

如果此代码在您的机器上正常运行(在我的机器上运行良好),那么问题可能出在您的实现中。

IDL:

import "oaidl.idl";

[
    object,
    uuid(9219CC5B-31CC-4868-A1DE-E18300F73C43),
    dual,
    nonextensible,
    helpstring("IService Interface"),
    pointer_default(unique)
]
interface IService : IDispatch
{
  HRESULT DoSomething(void);
}

[
    object,
    uuid(9219CC5B-31CC-4868-A1DE-E18300F73C44),
    dual,
    nonextensible,
    helpstring("IProvider Interface"),
    pointer_default(unique)
]
interface IServiceProvider2 : IDispatch
{
  HRESULT Init( IDispatch *sink, VARIANT_BOOL * result );
  HRESULT GetService( LONG serviceIndicator, IService ** result );
};

[
    uuid(9219CC5B-31CC-4868-A1DE-E18300F73C45),
    version(1.0),
]
library ServiceLibrary
{
    importlib("stdole2.tlb");

    interface IService;
    interface IServiceProvider2;
};

C#:

using System.Runtime.InteropServices;
using System.Windows.Forms;
using ServiceLibrary;
using IServiceProvider=ServiceLibrary.IServiceProvider2;

namespace COMInterfaceTester
{
    [ComVisible(true)]
    [Guid("2B9D06B9-EB59-435e-B3FF-B891C63108B2")]
    public interface INewService : IService
    {
        string ServiceName { get; }
    }

    [ComVisible(true)]
    [Guid("2B9D06B9-EB59-435e-B3FF-B891C63108B3")]
    public class NewService : INewService
    {
        public string _name;

        public NewService(string name)
        {
            _name = name;
        }

        //  implement interface
        #region IService Members

        public void DoSomething()
        {
            MessageBox.Show("NewService.DoSomething");
        }

        #endregion

        public string ServiceName
        {
            get { return _name; }
        }
    }

    [ComVisible(true)]
    [Guid("2B9D06B9-EB59-435e-B3FF-B891C63108B4")]
    public interface INewProvider : IServiceProvider
    {
        //  adds nothing, just implements
    }

    [ComVisible(true)]
    [Guid("2B9D06B9-EB59-435e-B3FF-B891C63108B5")]
    public class NewProvider : INewProvider
    {
        //  implement interface
        public void Init(object sink, ref bool result)
        {
            MessageBox.Show("NewProvider.Init");
        }

        public void GetService(int serviceIndicator, ref IService result)
        {
            result = new NewService("FooBar");
            MessageBox.Show("NewProvider.GetService");
        }
    }
}    

C++ 客户端:

#include "stdafx.h"
#include <iostream>
#include <atlbase.h>
#import "COMInterfaceTester.tlb" raw_interfaces_only
#import "ServiceLibrary.dll" raw_interfaces_only

using std::cout;

int _tmain(int argc, _TCHAR* argv[])
{
    CoInitialize(NULL);   //Initialize all COM Components
    COMInterfaceTester::INewProviderPtr pNewProvider(__uuidof(COMInterfaceTester::NewProvider));
    ServiceLibrary::IServiceProvider2 *pNewProviderPtr;

    HRESULT hr = pNewProvider.QueryInterface(__uuidof(ServiceLibrary::IServiceProvider2), (void**)&pNewProviderPtr);

    if(SUCCEEDED(hr))
    {       
        VARIANT_BOOL result = VARIANT_FALSE;
        int *p = NULL;

        hr = pNewProviderPtr->Init((IDispatch*)p, &result);

        if (FAILED(hr))
        {
            cout << "Failed to call Init";
        }

        ServiceLibrary::IService *pService = NULL;
        hr = pNewProviderPtr->GetService(0, &pService);

        if (FAILED(hr))
        {
            cout << "Failed to call GetService";
        }
        else
        {
            COMInterfaceTester::INewService* pNewService = NULL;
            hr = pService->QueryInterface(__uuidof(COMInterfaceTester::INewService), (void**)&pNewService);

            if (SUCCEEDED(hr))
            {
                CComBSTR serviceName;
                pNewService->get_ServiceName(&serviceName); 

                if (serviceName == "FooBar")
                {
                    pService->DoSomething();
                }
                else
                    cout << "Unexpected service";

                pNewService->Release();

            }

            pService->Release();
        }

        pNewProviderPtr->Release();
    }
    else
        cout << "Failed to query for IServiceProvider2";

    pNewProvider.Release();
    CoUninitialize ();   //DeInitialize all COM Components

}

【讨论】:

  • IServiceProvider 实际上是在 System 命名空间中定义的——这可能是命名冲突的来源。
猜你喜欢
  • 2010-09-07
  • 2012-04-08
  • 2010-10-20
  • 2013-11-12
  • 2010-12-12
  • 1970-01-01
  • 2012-12-07
  • 2019-07-15
  • 1970-01-01
相关资源
最近更新 更多