调整:将函数调用的参数从一个进程的地址空间传到另一个进程的地址空间。

代理:同另外一个组件行为相同的组件,必须是DLL形式的,因为需要访问客户进程的地址空间以便对接口数据进行调整。

残根:对客户传过来的数据进行反调整。

IDL(接口定义语言)

定义IX接口

//
// Server.idl - IDL source for Server.dll
//
//   The MIDL compiler generates proxy/stub code and a type library
//   from this file.
////
// Interface descriptions
//
//import将其他IDL文件中的定义包含到当前文件中
//unknwn.idl描述了IUnkown接口
import "unknwn.idl" ;  // Interface IX
//属性列表作为接口头
//用方括号作为信息分隔符
[object,   //COM接口uuid(32bb8323-b41b-11cf-a6bb-0080c7b2d682),  //相应的IIDhelpstring("IX Interface"),  //将一个帮助串放到一个类型库中//如何处理指针//ref:将指针当成引用,不能为空,不能指定别名//unique:可以为空,可以修改值,不能指定别名//ptr:C指针,可以为空,可以被修改,可以有别名pointer_default(unique)
]
interface IX : IUnknown
{//对于标记为in的参数,MIDL知道仅需将此参数从客户传递给组件//残根代码不需要送回任何值//对于标记为out的参数,MIDL指定仅需将词参数从组件传递给客户//代理不需要对输出参数进行调整,输出参数必须为指针HRESULT FxStringIn([in, string] wchar_t* szIn) ;HRESULT FxStringOut([out, string] wchar_t** szOut) ;
} ;

使用IX接口

        //COM对于字符串的标准约定是使用Unicode字符,即wchar_twchar_t* szOut = NULL ;hr = pIX->FxStringIn(L"This is the test.") ;assert(SUCCEEDED(hr)) ;//大多数COM函数返回的均为HRESULT值//若某个函数需要返回一个非HRESULT类型的值,在FxStringOut中定义了//一个输出参数,以从组件中得到一个返回串hr = pIX->FxStringOut(&szOut) ;assert(SUCCEEDED(hr)) ;// Display returned string.ostrstream sout ;sout << "FxStringOut returned a string:  "<< szOut<< ends ;trace(sout.str()) ;// 释放内存::CoTaskMemFree(szOut) ;

定义IY接口

// Interface IY
// 在客户和数组之间传递数组的接口的IDL描述
[object,uuid(32bb8324-b41b-11cf-a6bb-0080c7b2d682),helpstring("IY Interface"),pointer_default(unique)
]
interface IY : IUnknown
{HRESULT FyCount([out] long* sizeArray) ;//size_is告诉MIDL数组中元素个数将被保存在sizeIn中HRESULT FyArrayIn([in] long sizeIn, [in, size_is(sizeIn)] long arrayIn[]) ;//函数将用内部数组填充arrayOut,将psizeInOut设为实际填充到数组中//元素个数HRESULT FyArrayOut([out, in] long* psizeInOut, [out, size_is(*psizeInOut)] long arrayOut[]) ;
} ;

使用IY接口

            // Send an array to the component. long arrayIn[] = { 22, 44, 206, 76, 300, 500 } ;long sizeIn = sizeof(arrayIn) / sizeof(arrayIn[0]) ; hr = pIY->FyArrayIn(sizeIn, arrayIn) ;assert(SUCCEEDED(hr)) ;// Get the array back from the component.// Get the size of the array.long sizeOut = 0 ;hr = pIY->FyCount(&sizeOut) ;assert(SUCCEEDED(hr)) ;// Allocate the array.long* arrayOut = new long[sizeOut] ;// Get the array.hr = pIY->FyArrayOut(&sizeOut, arrayOut) ;assert(SUCCEEDED(hr)) ;// Display the array returned from the function.ostrstream sout ;sout    << "FyArray returned " << sizeOut<< " elements: " ;for (int i = 0 ; i < sizeOut ; i++){sout << " " << arrayOut[i] ;}sout << "." << ends ;trace(sout.str()) ;// Cleanuptrace("Release IY.") ;delete [] arrayOut ;

定义接口IZ

// Structure for interface IZ
// IDL中也可定义C和C++风格的结构,并可用它们作为函数的参数
typedef struct
{double x ;double y ;double z ;
} Point3d ;// Interface IZ
[object,uuid(32bb8325-b41b-11cf-a6bb-0080c7b2d682),helpstring("IZ Interface"),pointer_default(unique)
]
interface IZ : IUnknown
{HRESULT FzStructIn([in] Point3d pt) ;HRESULT FzStructOut([out] Point3d* pt) ;
} ;

编写好IDL文件后,可用MIDL编译器进行编译,生成相应文件,比如可以将server.idl生成iface.h、guids.c、proxy.c、dlldata.c、server.tlb

通过makefile文件,应同时生成server.dll和server.exe

代理DLL

MIDL编译器

代理DLL的建立

编译和链接MIDL生成的头文件后,MAIDL将为组件生成相应的代理和残根的代码,还需写一个DEF文件生成DLL

LIBRARY         Proxy.dll
DESCRIPTION     'Proxy/Stub DLL'EXPORTSDllGetClassObject   @1  PRIVATEDllCanUnloadNow     @2  PRIVATEGetProxyDllInfo     @3  PRIVATEDllRegisterServer   @4  PRIVATEDllUnregisterServer @5  PRIVATE

代理/残根的登记

生成代理DLL后将DLL登记,查看:
运行regedit.exe

本地服务器的实现

EXE提供组件不同于DLL,需对CUnkown和CFactory进行相应修改,但组件不需要变化。

示例程序运行

先点击server.exe,然后点击client.exe,选择2

client.cpp部分代码:

int main()
{cout << "To which server do you want to connect?\r\n"<< "1) In-proc Server\r\n" << "2) Local Server\r\n:" ;int i = 0 ;cin >> i ;DWORD clsctx ;if (i == 1){clsctx = CLSCTX_INPROC_SERVER ;  //连接到进程中服务器trace("Attempt to create in-proc component.") ;}else{clsctx = CLSCTX_LOCAL_SERVER ;   //连接到进程外服务器trace("Attempt to create local component.") ;}... ...
}

可以看到server.exe输出:

去掉入口点函数

EXE无法输出函数,进程中服务器依赖如下输出函数:
DllGetClassObject
DllRegisterServer
DllUnregisterServer
DllCanUnloadNow

EXE可自己对生命期进行控制,不需要DllCanUnloadNow 。
EXE可通过命令含参数RegServer、UnRegServer完成自登记,不需要DllRegisterServer、DllUnregisterServer。
DllGetClassObject的替换会比较麻烦一些。

类厂的启动

CoCreateInstance调用CoGetClassObject,CoGetClassObject调用DllGetClassObject,DllGetClassObject返回一个IClassFactory指针。但exe不输出DllGetClassObject,需要另想办法获得IClassFactory指针。

COM解决的办法是维护一个被登记的类厂的内部表格,根据客户请求的CLISD得到相应的类厂。若找不到相应类厂,COM将在注册表中查找并启动相应的EXE,此EXE可调用COM函数CoRegisterClassObject完成类厂的登记,以便COM能找到它们。

可对CFactory增加一个新的 静态成员函数StartFactories,对每个组件调用CoRegisterClassObect。

CFactory.cpp

//
// Start factories
//
BOOL CFactory::StartFactories()
{CFactoryData* pStart = &g_FactoryDataArray[0] ;const CFactoryData* pEnd =&g_FactoryDataArray[g_cFactoryDataEntries - 1] ;for(CFactoryData* pData = pStart ; pData <= pEnd ; pData++){// 初始化类厂指针和cookie.//以下两个成员变量为CFactory新增加的//保存与m_pCLSID相应的类ID的运行时类厂pData->m_pIClassFactory = NULL ;//保存此类厂的cookiepData->m_dwRegister = NULL ;// 创建类厂IClassFactory* pIFactory = new CFactory(pData) ;// 注册类厂//对于第2、3个参数,若EXE的单个实例只能提供单个组件,应该为//CLSCTX_LOCAL_SERVER,REGCLS_SINGLEUSER//否则,可为CLSCTX_LOCAL_SERVER,REGCLS_MULTI_SEPARATE//为了将一个EXE服务器作为它自己的进程中服务器登记,可将//CLSCTX_LOCAL_SERVER 和 CLSTX_INPROC_SERVER组合使用//如果使用REGCLS_MULTIPLEUSE,出现 CLSCTX_LOCAL_SERVER时自动//设置CLSTX_INPROC_SERVERDWORD dwRegister ;HRESULT hr = ::CoRegisterClassObject(*pData->m_pCLSID,static_cast<IUnknown*>(pIFactory),CLSCTX_LOCAL_SERVER,REGCLS_MULTIPLEUSE,  // REGCLS_MULTI_SEPARATE, //@Multi &dwRegister) ; //传回一个cookie,注销时用到if (FAILED(hr)){pIFactory->Release() ;return FALSE ;}// 设置数据pData->m_pIClassFactory = pIFactory ;pData->m_dwRegister = dwRegister ;}return TRUE ;
}

类厂的释放

服务器被关闭时,必须删除相应类厂。CFactory成员函数StopFactories将为EXE所支持的所有类厂调用CoRevokeClassObeject。

CFactory.cpp

//
// Stop factories
// 当服务器被关闭时,为所有EXE所支持的所有类厂调用CoRevokeClassObject
//
void CFactory::StopFactories()
{CFactoryData* pStart = &g_FactoryDataArray[0] ;const CFactoryData* pEnd =&g_FactoryDataArray[g_cFactoryDataEntries - 1] ;for (CFactoryData* pData = pStart ; pData <= pEnd ; pData++){// 得到 magic cookie 并停止类厂// 将从CoRegisterClassObject得到的cookie传给CoRevokeClassObjectDWORD dwRegister = pData->m_dwRegister ;if (dwRegister != 0) {::CoRevokeClassObject(dwRegister) ;}// 释放类厂IClassFactory* pIFactory  = pData->m_pIClassFactory ;if (pIFactory != NULL) {pIFactory->Release() ;}}
}

对LockServet的修改

// LockServer
// 保证客户在创建组件时,需要的服务器在内存中
HRESULT __stdcall CFactory::LockServer(BOOL bLock)
{if (bLock) {::InterlockedIncrement(&s_cServerLocks) ; }else{::InterlockedDecrement(&s_cServerLocks) ;}// If this is an out-of-proc server, check to see// whether we should shut down.// dll无法控制自己的生命期,因为装载和卸载是在另外一个exe中的,// 但exe可以CloseExe() ;  //@localreturn S_OK ;
}//
// Destructor
//
CUnknown::~CUnknown()
{::InterlockedDecrement(&s_cActiveComponents) ;// If this is an EXE server, shut it down.// 向程序的消息循环发散WM_QUIT消息CFactory::CloseExe() ;
}// 标示出同本地服务器相关的部分(当定义此宏时)
// 或同进程中服务器相关的部分(未定义此宏时)
#ifdef _OUTPROC_SERVER_  //// Out-of-process server support//static BOOL StartFactories() ;static void StopFactories() ;static DWORD s_dwThreadID ;// Shut down the application.// 将给应用程序的消息循环发送一条WM_QUIT消息static void CloseExe(){if (CanUnloadNow() == S_OK){::PostThreadMessage(s_dwThreadID, WM_QUIT, 0, 0) ;}}
#else// CloseExe doesn't do anything if we are in process.static void CloseExe() { /*Empty*/ }
#endif

消息循环:

为不致使EXE退出,应加上一个消息循环,可以是Windows消息循环,在名为outproc.cpp中,只有在建立进程外服务器是需要被编译和连接。

远程访问能力

运行DCOMCNFG.EXE可使本地服务器变成一个远程服务器。


在程序中访问某个远程服务器

需要用CoCreateInstanceEX体会CoCreateInstance

MULTI_QI:

跨网络时,函数的调用的开销比较大,为减少QueryInterface调用的影响,DCOM(分布式COM)建立MULTI_QI的新结构,同时查询多个接口时,降低开销。

CoCreateInstanceEX退出时,若能查询到MULTI_QI中的所有接口,返回S_OK;部分接口,CO_S_NOTALLNTERFACES;无法查询任何接口,E_NO_INTERFACE。

MULTI_QI声明如下

IMultiQI的实现由组件的远程代理“免费”提供。

还需要确定DCOM是否可用,这里不再列出,详见书中内容。

COM---EXE中的服务器相关推荐

  1. 应用程序 iis 中的服务器错误,unhandled-exception

    我收到了来自开发人员的网页,当我尝试在使用IIS 8的Windows Server 2012全新安装中安装该网页时,出现以下错误: Could not load file or assembly 'S ...

  2. Windows下使用Jconsole远程监控Linux系统中java服务器资源占用情况

    1.首先需要停止正在运行的服务:resin-XXX stop 2.然后在Linux的服务器启动项中添加如下信息: -Djava.rmi.server.hostname=192.168.1.122-Dc ...

  3. vs2017配置opencv、出现错误:0x00007FFA1CB84FD9 处(位于 test_code.exe 中)有未经处理的异常: Microsoft C++

    1.首先,具体参考文献请移步"vs2017中配置opencv",在首页搜索结果第一个就是(作者:Error 0) 2.本人根据上面的步骤操作完成后,在编译测试代码时,出现错误:0x ...

  4. linux查询Samba的安装,Linux_Linux中Samba服务器的编译安装以及配置的宏的写法,编译安装查询系统是否安装samb - phpStudy...

    Linux中Samba服务器的编译安装以及配置的宏的写法 编译安装查询系统是否安装samba 复制代码代码如下: # rpm -aq | grep samba yast2-samba-client-2 ...

  5. 视频监控系统中的平台服务器,视频监控系统中的服务器

    视频监控系统中的服务器 内容精选 换一换 针对于医院的时钟同步系统装置设计范围比较广,在医院内提供一套可靠.经济和有效,能够提供一个统一的北斗时间服务器对医院的数字化管理和医院各部门的统一协调意义重大 ...

  6. 其原因可能是堆被损坏,这也说明 xxx.exe 中或它所加载的任何 DLL 中有 bug

    1.代码如下: string src ="abcdabcd"; char* dst = new char[8]; strcpy(dst,src.c_str()); delete[] ...

  7. SharePoint2013 访问“/”应用程序中的服务器错误。解决方案:

    SharePoint2013 访问"/"应用程序中的服务器错误.解决方案: 在浏览器中键入访问SharePoint服务器地址时,报如下错误 按照,提示修改web.config文件. ...

  8. System.BadImageFormatException”类型的未经处理的异常在 xx.exe 中发生

    System.BadImageFormatException"类型的未经处理的异常在 xx.exe 中发生 参考文章: (1)System.BadImageFormatException&q ...

  9. 0x0F19B7EC (ucrtbased.dll)处(位于 ex6.exe 中)引发的异常: 0xC0000005: 写入位置 0x00740000 时发生访问冲突。

    0x0F19B7EC (ucrtbased.dll)处(位于 ex6.exe 中)引发的异常: 0xC0000005: 写入位置 0x00740000 时发生访问冲突. 参考文章: (1)0x0F19 ...

最新文章

  1. Mac OS X如何进行字体管理
  2. 弹性碰撞后速度方向_MEMS加速度计辐射效应
  3. 什么是‘YTowOnt9‘?
  4. (转)创业的注意事项
  5. 使用python写一个名片管理系统
  6. oracle 安全备份与rman_Oracle 11g下使用RMAN进行备份和恢复操作(一)
  7. win2008r2服务器维护,win2008 r2 服务器安全设置
  8. Redis开发与运维
  9. Java 游戏手柄 编程
  10. 函数图像生成器 (吉林大学 孙立鑫)
  11. 单页面应用(SPA)前端路由hash 模式 VS history 模式
  12. 便携式储能系统---“钱景”无限
  13. 如何使用IDEA将代码提交至SVN
  14. 1.3一摞烙饼的问题
  15. python3 sleep 延时秒 毫秒
  16. 第四章 专业统计(上)-统计实务
  17. MySQL CPU 使用率高的原因和解决方法(来自aliyun官方文档)
  18. 2017华为笔试、面试经历
  19. 苹果开发者账号 转让已上架的App应用(更换开发者账号)
  20. 两位阿里大牛联合敬献,码出高效的Java学习笔记,你值得拥有

热门文章

  1. java怎么求平方怎么求指数?
  2. 『解题报告』数学 - Standard Deviation (标准差) - UVA10886
  3. 获苹果中国区推荐,能带来多少新增
  4. 开源了个人开发的一个微信小程序,提供共享场景服务,支持图片和小视频
  5. 用LangChain构建大语言模型应用
  6. 三大报再呼救市:A股估值到了崩溃边缘
  7. uniapp 微信小程序表单input输入框,点击下面的输入框时输入法弹出会把输入框内容顶上去
  8. 初识中央处理器CPU
  9. 杭电 HDU ACM 圆桌会议
  10. 4月刊佳文推荐:开发者的眼界