我编写了一个进程外COM组件svr.exe。 
在一个局域网内,我把svr.exe部署在我的计算机上。 
又编写了一个客户程序callSvr.exe,部署在其他的计算机上。 现在,callsvr可以正常的访问到svr.exe中的方法。 
接下来我需要给svr.exe添加事件回调,让它可以把事件通知给客户程序。 
事件回调的方法是学习vckbase网站上的文章做的,其网址如下: 
http://www.vckbase.com/document/viewdoc/?id=1525 我在svr.exe中定义了一个接口方法SetCBFun,客户程序可以调用此函数来设置“事件回调函数”。 
在客户程序中,调用如下代码来设置回调函数的地址: 
    hr = pBackupAdmin->SetCBFun(&m_sink); 其中,m_sink是CSink m_sink对象变量。CSink是客户程序中自定义的一个类,它派生于ICallBack。而ICallBack是我在svr.exe中手工定义的一个接口,具体的代码我是参照vckbase网站提供的方法写的。 现在的问题是:我在调用pBackupAdmin->SetCBFun函数时出错,我跟踪调试进入SetCBFun函数中,进入了一个扩展名为tli的文件(是系统自动生成的)。在这个文件的SetCBFun函数中,当执行完HRESULT _hr = raw_SetCBFun(pCallBack)后,_hr等于0xc0000005。我用Error Lookup查找,也无法得知是什么错误,而继续执行下一条语句时就蹦异常了: 
if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this)); 我也跟踪调试了vckbase网站的代码,而人家的代码就不出错,直接执行到COM组件服务的SetCBFun函数中了。我想,很可能是因为我的COM组件是远程调用的,在经过代理proxy.dll时出错了。是不是跨进程传输数据时哪里的数据没有兼容好啊? 请高手帮忙解释一下。多谢了。另外,我这里用的方法不是“事件连接点”的方法。难道必须使用“事件连接点”才可以? 

解决方案 »

  1.   

    连接点本来就是一种回调机制.
    SetCBFun应该传递两个参数吧,你怎么只有一个啊,
    一个是地址,另外一个是会话COOKID
      

  2.   

    会话COOKID要不要无所谓,楼主的问题是在远程调用时出错,应该是代理和存根在传输ICallBack接口时,出现问题。
      

  3.   

    tttyd太理解我了。vcbase网站中的那个例子中的“会话COOKID”可以不要,因为那个例子中考虑有多个事件会话的情况。
    而我现在是“在远程调用时,代理和存根在传输ICallBack接口时”出现了异常。
    猜想是代理在解析远程进程外组件对象的接口时出现的问题。
    不知道猜的对不对,而且也不知道如何解决。
      

  4.   

    COM组件代码:ICallBack接口的定义
    interface ICallBack : IUnknown
    { [id(1), helpstring("method CBFun_InState")] HRESULT CBFun_InState([in] LONG nInID, [in] LONG nDecoderID, [in] LONG nInIndex, [in] LONG nState);
    };组件对象类中,SetCBFun函数的实现
    STDMETHODIMP CAlarmSrv::SetCBFun(ICallBack *pCallBack)
    {
    if( NULL == pCallBack ) // 居然给我一个空指针?!
    return E_INVALIDARG;
    m_pCallBack = pCallBack; // 保存 return S_OK;
    }客户端-----调用COM组件的代码:
        // 创建远程COM组件
        hr = CoCreateInstanceEx(clsid, NULL, CLSCTX_REMOTE_SERVER, &servInf, 1, &qi);
        if (FAILED(hr))
        {
    AfxMessageBox("CoCreateInstanceEx失败");
            return FALSE;;
        }
        if (FAILED(qi.hr))
        {
    AfxMessageBox("COM库连接失败");
            return FALSE;;
        }
        // 获得组件的指针
        IArmSrv* pBackupAdmin = (IArmSrv*)qi.pItf;    // Retrieve first interface pointer.    // 调用组件的接口方法
        hr = pBackupAdmin->Init();
        if (hr != S_OK)
        {
            AfxMessageBox("调用接口方法失败");
            return;
        }
        // 我的程序可以执行到这里,之前的代码都没有发生错误。    // 设置回调事件函数
        hr = pBackupAdmin->SetCBFun(&m_sink);// 程序执行到这一行就异常了
        pBackupAdmin->Release();
      

  5.   

    我以前做COM+的好像也遇到了这个问题,从代码上也查不出什么问题来,是多个COM+服务一个Client的。后来好像在什么地方另加了一个进程,对付过去了,有点忘了。
      

  6.   

    m_sink是CSink类的代码,也没什么,就是抄袭vcbase网站的。如下:sink.h// Sink.h: interface for the CSink class.
    //
    //////////////////////////////////////////////////////////////////////#if !defined(AFX_SINK_H__C77F3B61_D82C_4C16_A9C9_21FD64ED0423__INCLUDED_)
    #define AFX_SINK_H__C77F3B61_D82C_4C16_A9C9_21FD64ED0423__INCLUDED_#if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000class CSink : public ICallBack  
    {
    public:
    CSink();
    virtual ~CSink();
        
    // STDMETHOD(xx) 是宏,等价于 long __stdcall xx
    STDMETHOD(QueryInterface)(const struct _GUID &iid,void ** ppv);
    ULONG __stdcall CSink::AddRef(void);
    ULONG __stdcall CSink::Release(void);
    STDMETHOD(raw_CBFun)(long,long,long,long);};#endif // !defined(AFX_SINK_H__C77F3B61_D82C_4C16_A9C9_21FD64ED0423__INCLUDED_)
    sink.cpp// Sink.cpp: implementation of the CSink class.
    //
    //////////////////////////////////////////////////////////////////////#include "stdafx.h"
    #include "CallLocalOpcSrv.h"
    #include "Sink.h"#ifdef _DEBUG
    #undef THIS_FILE
    static char THIS_FILE[]=__FILE__;
    #define new DEBUG_NEW
    #endif//////////////////////////////////////////////////////////////////////
    // Construction/Destruction
    //////////////////////////////////////////////////////////////////////CSink::CSink()
    {}CSink::~CSink()
    {}// STDMETHODIMP 是宏,等价于 long __stdcall
    STDMETHODIMP CSink::QueryInterface(const struct _GUID &iid,void ** ppv)
    {
    *ppv=this;
    return S_OK;
    }ULONG __stdcall CSink::AddRef(void)
    { return 1; } // 做个假的就可以,因为反正这个对象在程序结束前是不会退出的ULONG __stdcall CSink::Release(void)
    { return 0; } // 做个假的就可以,因为反正这个对象在程序结束前是不会退出的STDMETHODIMP CSink::raw_CBFun(long nInID, long nDecoderID,
                                        long nInIndex, long nState)
    {
    AfxMessageBox("收到远程组件的消息。");
    return S_OK;
    }
      

  7.   

    你的应该是raw_CBFun_InState,
    而不是raw_CBFun
      

  8.   


    sorry,应该是raw_CBFun_InState,我在拷贝代码时,为了保护公司的信息,删除了一些字符。
    因此,在删除sink类的信息时多删除了。这都让你看出来了,真细心。呵呵。原来的代码就是raw_CBFun_InState。
    这一点没问题的。
      

  9.   

    你用本机调用也会出错吗?使用CreateInstance创建对象.
      

  10.   

    http://msdn.microsoft.com/zh-cn/library/3ts2dxc8(VS.80).aspx
      

  11.   

    我用本机调用也会出错,但不是使用CreateInstance创建对象。而是CoCreateInstanceEx。
      

  12.   


    您给的示例不知道是怎么回事,没有说明,打开项目后也无法运行,总之,看不懂。
    而且,我也没有找到回调事件的定义。如果只是“演示如何远程调用 COM 对象”的编程,那我的程序就可以实现。
    现在的问题是“远程回调事件”的应用。
      

  13.   

    等了五天了。一直没有答案。高手都不在了?qimiao77也没招了?
      

  14.   

    我是按照vckbase网站上的文章做的。
    也说不清区别呢。
      

  15.   


    yyunffu可能误解我的意思了。我现在是在本地用远程调用的方式来调用我的回调函数。
      

  16.   


    唉,16楼说的本机调用是指:
    我有一个客户端程序要调用我自己写的COM组件,这个客户端程序假设放在192.168.0.101上。
    我在把“本来应该放在其他计算机上的COM组件放在了192.168.0.101上”
    我说的本机是192.168.0.101上,但调用方式还是远程调用的方式。不知我说清楚没有。