描述:
只是写一个大概欢迎提问!
ATL实现DCOM例程
MFC和ATL都可以实现COM,其中MFC是采用嵌套类的方式实现COM接口用接口映射表的方式提供多接口支持,而ATL采用多重继承的方式实现COM接口。在VC++6.0中提供了对ATL的支持,考虑到ATL的简单,我们下面用ATL来实现一个DCOM的程序。
1. 建立服务端
建立VC工程:
选ATL COM AppWizard 服务端名称为TestServ,选择NT Service单击ok完成。
加入COM对象:
在菜单中选择Insert->New ATL Object,在弹出的对话框中选择Simple object,名字中填入Test,属性中选择支持连接点(support connect point)确定。
实现连接点:
先编译工程,前一个步骤是自动生成了一个IDL文件对接口进行描述,这里对工程进行编译是让编译器对IDL进行编译生成必要的C++代码。在classwizard上选中CTest按右键,在弹出菜单中选择Implement Connectpoint,在弹出窗口中选中_ItestEvents按Ok,新生成一个空白的类CProxy_ITestEvents和接口ItestEvents。在接口中加入方法OnDataModified参数为 [In]BSTR bstr,然后在CProxy_ItestEvents中加入以下的代码:
void Fire_OnDataModified(BSTR bstr)
{
CComVariant varResult;
T* pT = static_cast<T*>(this);
CComVariant* pvars = new CComVariant[1];
int nConnectionIndex;
int nConnections = m_vec.GetSize();
for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
{
pT->Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
pT->Unlock();
IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);
if (pDispatch != NULL)
{
VariantClear(&varResult);
pvars[0] = bstr;
DISPPARAMS disp = { pvars, NULL, 1, 0 };
pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);
}
}
delete[] pvars;
return ;
}
加入接口方法:
在classwizard中选中ITest,按右键选中add a meathod,填入SendMsg,参数[in]BSTR bstr,按OK完成。
加入方法的处理代码:
在CITest中选择SendMsg,写入代码。
Fire_OnDataModified(bstr);
修改对象声明的实例个数:
在Test.h文件中的类声明中加入该语句:
DECLARE_CLASSFACTORY_SINGLETON(CTest)
这里是将CComClassFactorySingleton声明为CTest的类厂,使得每个调用createinstance创建Ctest实例的时候,函数只是返回接口的指针,也就是说对于该对象只有一个实例,而不是创建多个对象的实例。这是为了使得连接点的消息是发送给所有的用户而达到广播的效果。
加入MFC支持:
在stdfx.h中加入:
#include <afxwin.h>
#include <afxdisp.h>
服务的安全性设置:
用该语句替换CserviceModule:run()中原有的安全性设置语句使得所有的用户都可以访问该服务。
hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_NONE, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL);
将服务设置为自启动:
将CServiceModule:Install()中CreateService函数的第六个参数设置为SERVICE_AUTO_START使得服务在安装之后是自启动的,这样的设置可以避免客户端用配置工具进行配置。
2. 建立客户端
建立一个简单的MFC对话框程序:
(步骤简单不多说了),在对话框上面加一个编辑框。添加编辑框改变事件OnChangeEdit1()。在classwizard上加入变量定义m_csEdit1
添加ATL支持:
Insert->Add ATL object,弹出对话框问是否加入ATL支持,yes,弹出对话框选择退出。
加入CLSID声明:
在stdafx.h中加入#import "..\TestServ\TestServ.tlb" raw_interfaces_only, no_namespace, named_guids 这里引号处必须声明testServ程序的tlb文件所在位置。(相关参数清查询MSDN文档《The #import Directive》)
加入接收器实现类:
CEventSink程序如下
//eventsink.h
#ifndef __EVENTSINK_H
#define __EVENTSINK_H
namespace {
static const int DISPID_DATAMODIFIED = 1;
_ATL_FUNC_INFO OnDataModifiedInfo =
{
CC_STDCALL, //calling conv...
VT_EMPTY,//return value...
1 ,//number of arguments...
{VT_BSTR}//argumnent types...
};
}
class CEventSink : public IDispEventImpl<1, CEventSink, &DIID__ITestEvents, &LIBID_TESTSERVLib>
{
public:
CTestMFCDlg *parent;
CEventSink(){};
virtual ~CEventSink(){};
void __stdcall OnDataModified(BSTR bstr); //有参数的情况
//必须要用SINK_ENTRY_INFO,必须指定参数信息
BEGIN_SINK_MAP(CEventSink)
SINK_ENTRY_INFO(1,DIID__ITestEvents,DISPID_DATAMODIFIED,OnDataModified,&OnDataModifiedInfo)
END_SINK_MAP()
};
#endif
//event.cpp
#include "stdafx.h"
#include "EventSink.h"
#include "resource.h"
#include "TestMFCDlg.h"
#include <comutil.h>
#pragma comment(lib,"comsupp.lib")
void CEventSink::OnDataModified(BSTR bstr)
{
::PostMessage(parent->m_hWnd,WM_USER+100,0,(LPARAM)bstr);
//稍后讲解这里为何这般设置(参见DCOM研究日志中:连接点激发函数和主窗口传递数据)
}
对话框类中添加必要的的成员变量:
CEventSink *m_pSink;//连接点接受器类
ITest *m_pITest;
DWORD m_dwCookie;
Initdialog事件中添加初试化分布式对象,如下代码:
HRESULT hr;
COSERVERINFO ServerInfo;
ServerInfo.dwReserved1=0;
ServerInfo.dwReserved2=0;
ServerInfo.pwszName=L"192.168.100.151";//服务器地址
ServerInfo.pAuthInfo=NULL;
MULTI_QI Results;
Results.pIID=&IID_ITest;
Results.pItf=NULL;
hr=CoCreateInstanceEx(CLSID_Test,NULL,CLSCTX_REMOTE_SERVER,&ServerInfo,1,&Results);
ASSERT(SUCCEEDED(hr));
m_pITest=(ITest*)Results.pItf;
ASSERT(SUCCEEDED(Results.hr));
加入连接点接受器的初试化和销毁:
在initdialog方法中加入以下代码:
hr=AtlAdvise(m_pITest, (IUnknown*)m_pSink, DIID__ITestEvents, &m_dwCookie);
ASSERT(SUCCEEDED(hr));
在窗口的destory事件中加入以下代码:
AtlUnadvise(m_pITest, DIID__ITestEvents, m_dwCookie);
if(m_pITest) m_pITest->Release();
CoUninitialize();
if (m_pSink)delete m_pSink;
处理对话框改变函数
在对话框的OnEdit1Change()函数中加入以下代码:
BSTR bstr;
UpdateData(TRUE);
bstrOS=csEdit.AllocSysString();
HRESULT hr = m_pITest->SendMsg(bstr);
if(FAILED(hr)) AfxMessageBox("send failed! hr=0x%x",hr);
if(bstrOS) SysFreeString(bstrOS);
消息处理函数
在classwizard上面选中CtestDialog类按反键选择add virtual function在弹出窗口左边的列表中选择,windowproc点击ADD&EDIT按钮在WindowProc函数中加入以下代码用于处理前面OnDataModified()中post过来的message:
switch(message)
{
case WM_USER+100:
{
m_csEdit1=Cstring((BSTR)lParam);
UpdateData(FALSE);
}
}
系统加入DCOM支持
在stdafx.h中加入以下代码:
#ifndef _WIN32_DCOM
#define _WIN32_DCOM
#endif
3. 程序的安装和删除
程序的安装
在命令行中键入以下命令(当然做成批处理更方便一些):
regsvr32 –service c:\test\testserv.exe
这里要写入testserv的路径
net start netlocksvr
程序的配置
经过上面的安装,程序应该可以运行。如果出现意外,在命令行中键入:DCOMCNFG
在程序窗口中选择默认属性,