首先,用纯C++写一个进程内的COM已经搞定了,理论上只要把DLL导出的那几个接口变成主动注册类厂(CoRegisterClassObject)就应该OK了,现在的情况是客户端在创建接口的时候COM库也正确把进程拉起来,类厂也注册成功了,但是创建接口老是返回E_NOINTERFACE。我调试运行COM进程发现我我已经正确返回了对应接口的指针了,也返回了S_OK,但是客户端那边就是返回E_NOINTERFACE,搞了几天了。。麻烦高手帮看看。
我在网上找了半天,都是说用纯C++实现的进程内的例子,没有一个说进程外的,如果哪位有写好的发给我参考更是感谢,不要给我ATL和MFC之类框架代码。先谢谢了。下面是接口定义import "unknwn.idl";[object, uuid(e1893788-31b1-4a20-8e5a-c765c8272e75)]
interface ICalc : IUnknown
{
    HRESULT Add(int a, int b, int* result);
    HRESULT Sub(int a, int b, int* result);
}[    
    uuid(3ab7610a-a3eb-4101-822e-0557aef8de14),
    version(1.0),
    helpstring("Math Library")
]
library MathLib
{
    importlib("stdole32.tlb");
    [uuid(4994511e-a14d-4192-a1ff-9c4259c95bcd)]
    coclass Math
    {
        interface ICalc;
    }
}
下面是类实现部分的头文件:extern const IID IID_ICalc;    
class ICalc : public IUnknown
{
public:    
    virtual HRESULT __stdcall Add( int a, int b, int *result) = 0;    
    virtual HRESULT __stdcall Sub( int a, int b, int *result) = 0;    
};class Calc : public ICalc
{
protected:
    long m_lRef;
public:
    Calc();
    virtual HRESULT __stdcall QueryInterface( REFIID riid, void **ppvObject );
    virtual ULONG   __stdcall AddRef( void);
    virtual ULONG   __stdcall Release( void);
    virtual HRESULT __stdcall Add( int a, int b, int *result);    
    virtual HRESULT __stdcall Sub( int a, int b, int *result);
};class Math : public IClassFactory
{
public:
    virtual HRESULT __stdcall QueryInterface( REFIID riid, void **ppvObject );
    virtual ULONG   __stdcall AddRef( void);
    virtual ULONG   __stdcall Release( void);
    virtual HRESULT __stdcall CreateInstance( IUnknown *pUnkOuter, REFIID riid, void **ppvObject);
    virtual HRESULT __stdcall LockServer( BOOL fLock);
};extern const IID LIBID_MathLib;
extern const CLSID CLSID_Math;
下面是类实现的部分代码:...HRESULT __stdcall Calc::QueryInterface( REFIID riid, void **ppvObject )
{
    printf("Calc::QueryInterface\n");
    if ( riid == IID_ICalc || riid == IID_IUnknown )
    {
        AddRef();
        *ppvObject = this;
        return S_OK;
    }    return E_NOINTERFACE;
}...HRESULT __stdcall Math::CreateInstance( IUnknown *pUnkOuter, REFIID riid, void **ppvObject )
{
    printf("Math::CreateInstance\n");
    Calc* p = new Calc();
    HRESULT hr = p->QueryInterface(riid, ppvObject);
    if ( SUCCEEDED(hr) )
    {
        return S_OK;
    }    delete p;
    p = NULL;
    return E_NOINTERFACE;
}...
HRESULT __stdcall Math::QueryInterface( REFIID riid, void **ppvObject )
{
    printf("Math::QueryInterface\n");
    if ( riid == IID_IClassFactory || riid == IID_IUnknown )
    {
        *ppvObject = this;
        AddRef();
        return S_OK;
    }
    return E_NOINTERFACE;
}...下面是类厂注册实现代码:CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);    Math oMath;        printf("begin register class object...");
    DWORD dwRegister = 0;
    HRESULT hr = CoRegisterClassObject(CLSID_Math, &oMath, CLSCTX_LOCAL_SERVER, REGCLS_MULTI_SEPARATE, &dwRegister );
    if (FAILED(hr))
    {
        printf("register failed, HR=%X\n", hr);
        getch();
        return 0;
    }    MSG msg;
    while (GetMessage(&msg,0,0,0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    
    CoRevokeClassObject(dwRegister);        ::CoUninitialize();

解决方案 »

  1.   

    atl app wizard就可以了为何非要折磨自己
      

  2.   

    这个不能算是折磨自己吧,就像先用WIN32 SDK写一个窗口后,然后再用MFC是一个道理。
      

  3.   

    自己写太麻烦了
    lz可以参考一下ATL的ComModule,如何实现一个Windows 服务程序
    也可以参考以下文字,如何实现Window服务
    http://www.vckbase.com/document/viewdoc/?id=1474
      

  4.   

    我是用纯C++实现一个Windows的COM进程外服务。并不是Windows的服务。
    为什么自己写一个简单的COM进程外服务麻烦?麻烦指点一下,谢谢
      

  5.   

    晕。。难道没有一位大哥亲自己动手用纯C++写过一个进程外服务?PS:如果有想研究一下,我可以提供我写的代码,代码质量不是很好,但是应该可以看的懂。
      

  6.   

    摆明是没有实现代理存根,上次已经有人指出这点了。不是没有人动手做纯C++的进程外服务器,而是毫无必要,自定义接口(ICalc)必须要有代理接口DLL,所以你的项目还必须增加一个代理DLL项目,这个DLL必须在客户机器上注册。在ATL向导中生成的代理项目根本无需手工打理,全自动的,如果要研究,建议用ATL生成一个测试项目,然后看看代理DLL中的内容,模仿它打造一个就可以了(如果能看懂的话)。再次建议,自己写列集散列的代码灰常灰常没必要,而且灰常繁琐,我也能写一个出来,问题是我写的代码无论是全面性、通用性、效率、性能等方面都不如向导生成的项目,网上见过一篇英文文章,它就解释了向导生成的代理DLL是最优的列集方案。本来没怎么关注这个贴,但为了避免楼主的研究走入极端,还是提醒一下比较好。还有,如果你不想实现代理,你可以只注册标准接口,比如IUnknown/IDispatch等,这些接口的代理由系统实现,但客户端使用就没那么方便了。
      

  7.   

    非常感谢jameshooo,终于搞定了,确实是因为没有代理DLL的问题。但是我有另一个问题,我试了以一下,实现了双接口的ATL进程外COM,可以没有代理DLL,
    但是没有实现双接口的ATL进程外COM如果没有代理DLL也会报找不到接口的错误。
    比较了一下,在加载实现了双接口的ATL进程外COM时,服务进程和客户进程有都会加载一个SXS.dll。
    而我自己实现IDispatch接口不实现代理DLL时,却根本没用。一样会报找不到接口的错误。能不能答一下。谢谢
      

  8.   

    如果你的组件是双接口,你只需要注册IDispatch(别注册ICalc)就可以直接被进程外调用。如果不是双接口,代理是不可避免的。
      

  9.   

    再次非常感谢你能回复。现在的现象是,我在客户端代码没有变化,都是直接获取ICalc接口,实现双接口且没有代理DLL的情况是可以正确获取ICalc对象。未实现双接口又无代理DLL情况下的获取出错。你可以试一下,代码很简单的。非常感谢。
      

  10.   

    支持楼主。
    最近在看atl开发指南,代理存根还没有认真看,能否讲一下为什么需要代理存根吗?
    谢谢
      

  11.   

    查资料看了下代理存根。有了基本的了解。还有一个疑问。
    是不是本地的话,代理存根只需要在com服务器的exe文件生成就可以了。
    对于远程服务器的话,服务器和客户端的机器都要有一份代理存根。
    是这样吗?
      

  12.   

    过了很长时间了,一个偶然的机会才发现问题原因。自己来解答一下。对于进程外的COM,需要在注册表中注册类型库和接口的列集器(代理存根)。列集器有下面几个情况:
    1. 可以自己实现,MIDL在编译IDL文件时已经生成了代码,编译一下即可,这样会多出一个DLL。
    2. 如果接口继承于IDispatch,那么可以使用通用列集器:CLSID:{00020424-0000-0000-C000-000000000046}。
    3. 如果接口是继承于IUnknown且成员的参数和返回值是Automation-compatible(具体请查MSDN),那么可以在IDL接口描述上添加oleautomation属性,这样也可以使用通用列集器