属性化ATL,DCOM,SIM,IID

//==============================================================================
要不要从IUnknown接口派生?
1 是否要使用GetService,是则要从IUnkown接口派生, 否则往下看:
2 是否为回调,不是回调就不用从IUnkown接口派生,是回调往下看:
3 回调是采用继承方式还是采用复合形式:
继承方式: 不用从IUnkown接口派生(不过这里一般会发生多继承)
复合方式: 为了编码方便,灵活(使用ComSink),最好从IUnkown接口派生。也可以不从IUnown派生,
         不过麻烦,基本上需要做一个comsink<为模板类>类似的具体类来,只不过不处理IUnkown部分

要不要token:
token是用于区别输入的,如果输入的参数已经可以区别自已,就不用token了
//==============================================================================
SIM工程注意事项(如果不Embeded IDL文件并且不注册则不用处理这些注意事项)
1, Embeded IDL设置项:工程/属性/链接/Embeded IDL下面的ingore embeded idl(/ignoreidl)
2, 注册dll设置项: 工程/属性/链接/Genneral 下面的Register Output
3, SIM是属性化ATL工程(dll),从 c/c++ / preprocessor中可以看出来:     

WIN32;_WINDOWS;_DEBUG;_USRDLL;_ATL_ATTRIBUTES
4, 如果要跨套间使用SimDll里提供的接口,则必须嵌入idl并注册dll;否则可不用。
5, SIM工程并没有默认添加rgs资源("REGISTRY"),会导致注册失败,需手工添加.
6, 如果注册失败,可用exeScope.exe查看资源,其中的REGISTRY、TYPELIB资源是否存在以及格式是否正确,用Dependes.exe工具查看DllMain、DllRegisterServer等函数是否正常导出。
7,如果发现SimDll没有导出DllRegisterServer等函数,不要去添加.Def文件导出,在适当文件里包含下列文件即可,然后[module(dll)]属性会自动生成这些函数并导出现:
#include <atlbase.h>
#include <atlcom.h>
#include <atlwin.h>
#include <atltypes.h>
#include <atlctl.h>
#include <atlhost.h>
8, 导出[export]属性用于enum要特别注册,导出后,enum外面的namespace将会失去作用,因为任何属性生成的都是纯C代码,而C语言中没有namespace,这样会很容易产生名字冲突情况。
9, 导出的接口中,不要使用bool类型,因为导出后生成的是纯C代码,没有bool类型,应该用BOOL类型代替。
10,对于Exe的DCOM工程,Exe工程Build后并不会自动注册,也不要自动Build PS工程并注册,所以一定要手工做这两注册,否则调试时会白费工夫:
一、***.exe /RegServer
二、Build ***ps project, 再regsvr32 ***ps.dll (在正常的工程,即非SIM工程,这一步会自动进行,参见2)

注1:对于一般的DCOM,好像主工程和ps工程注册其一就能正常工作,待再确定?
注2:对于同一进程内不同套间传递COM接口,可用使用CoMarshalInterfaceInStream以及

CoGetInterfaceAndReleaseStream,但是不知这两个API背后是否有使用proxy stub.
注3:对于不同进程(跨进程了就一定会跨套间),只能依赖DCOM机制利用***ps.dll及注册来来传递(列集,散集)接口,此时如果在DCOM服务端QueryInterface则意味着**ps.dll里没有这个接口。另外清寒要注册参数的in/out属性。如果是从IDispatch派生的接口,则相应的方法都要正确实现。
注4,对于不跨套间情况,和纯C++接口比较类似,这种情况下没有用到的接口函数都可以不“假实现”,因为实际上不会调用或者不依赖方法调用的结果,只要随便实现以通过编译即可。

//==============================================================================

 属性化接口属性:

o,udp(object,uuid,dual,pointer_default(ref,unique,ptr)).  

在实现接口的时间,如果参数中有用到其它接口,需要看到对应接口的全部实现,这点好像很不方便,在非属性化时,可以在idl文件中用import 避免.

 属性化类属性: 

ct,udp(coclass, threading(apartment,free,both,neutal);  uuid, default, progid(vi_progid, version))  

ref,unique,ptr:

顶层指针默认是ref, 嵌套指针默认unique;

ref指针不充许为null,如果是null则调用直接返回达不到服务器,

ptr指针最类似C,会作重复性检查,如果在client是相同的指针,rpc到服务端后仍然是相同的指针, 如果unique指针则会是不同的;

unique指针充许为null, 但pointer_default(ref,unique,ptr)属性影响不了接口里的顶层指针.

[in][out][in,out] 指针内存:

顶层的话,内存都是由调用者管理;

 [out]的内嵌指针内存则是由被调用者管理(CoTaskMemAlloc),由调用者释放(CoTaskMemFree);除非有专门文档说明,否则当调失败时,caller可以假定该方法没有分配任何内存;

[in,out] 的内嵌指针内存除了有以上特点外,caller还可以先给内嵌指针CoTaskMemAlloc, server端再使用CoTaskMemRealloc/CoTaskMemFree, 最后返回caller时caller再调用CoTaskMemFree. (p.s. CoTaskMemFree可以接受空指针并且不出错)
====================================
ReamMe:

DllNoProp.idl
    This file contains the IDL definitions of the type library, the interfaces
    and co-classes defined in your project.
    This file will be processed by the MIDL compiler to generate:
        C++ interface definitions and GUID declarations (DllNoProp.h)
        GUID definitions                               (DllNoProp_i.c)
        A type library                                   (DllNoProp.tlb)
        Marshaling code                                (DllNoProp_p.c and dlldata.c)

 ====================================

手工添加接口(非属性化, DECLARE_REGISTRY_RESOURCEID):
1,添加IDL文件;
2,在IDL中写接口,如果这个接口在当前工程要单独实现,则还要考虑是否要写coclass;
3,修改IDL文件属性,控制成生的头文件、IID文件、代理文件的名称;
4,写RGS文件;

5,生成工程注册(产生 interface.h[有iid,clsid,libid的声明及coclass,interface,interface_ps的声明]; interface_i.c[有iid,clsid,libid的实现]; interface_p.c[代理存根]等文件)。
6,生成PS工程,并注册(如果没有发生跨套间的行为,则projectnameps.dll用不行,列集&散集时,只处理*ps.dll中出现的接口)。

注1:project的rgs文件内容有:interface, appid。参见 rgs示例2。

注2:interface的rgs文件内容有:clsid, progid, vi_progid。参见 rgs示例1。

手工添加接口(属性化,resource_name = "IDR_DLLPROP"):
1,头加头文件(接口,o, udp);

2,添加头文件&实现文件(接口的实现类, ct, udp);
3,编译并注册。

注1:接口本身无需rgs文件,但是模块还是需要一个RGS文件的,如果没有则会注册失败。

注2:编译工程时,会产生_projectname.idl文件,midl.exe处理这个文并生成 _projectname.h,  _projectname_i.c, _projectname_p.c, dlldata.c文件。
//============================================
rgs示例:

HKCR
{

 simsvc.svcmgr.1 = s 'svcmgr Class'
 {
  CLSID = s '{359DF5E3-3B95-40D0-971C-7A7B98057566}'
 }
 simsvc.svcmgr = s 'svcmgr Class'
 {
  CLSID = s '{359DF5E3-3B95-40D0-971C-7A7B98057566}'
  CurVer = s 'simsvc.svcmgr.1'
 }
 NoRemove CLSID
 {
  ForceRemove {359DF5E3-3B95-40D0-971C-7A7B98057566} = s 'svcmgr Class'
  {
   ProgID = s 'simsvc.svcmgr.1'
   VersionIndependentProgID = s 'simsvc.svcmgr'
   ForceRemove 'Programmable'
   LocalServer32 = s '%MODULE%'
   val AppID = s '%APPID%'
   'TypeLib' = s '{EF8C3909-361A-4EB4-8A19-8E9ED017157A}'
  }
 }
}

----------------------------------
HKCR
{
 NoRemove Interface
 {
  '%IID_IIMAppManager%'
  {
   TypeLib = s '%APPID%'
   {
    val Version = s '1.0'
   }
  }
  '%IID_IIMStdApp%'
  {
   TypeLib = s '%APPID%'
   {
    val Version = s '1.0'
   }
  } 
 }
 
 NoRemove AppID
 {
  '%APPID%' = s 'simsvc'
  'simsvc.exe'
  {
   val AppID = s '%APPID%'
  }
 }
}
//============================================

interface(IID):

1, ProxyStubClsid/ProxyStubClsid32. 多个interface指向同一个clsid(非标准代理存根,如果接口实现了IDispatch,代理存根DLL可以不注册而使用系统标准的PS_CLSID).

2, TypeLib(可选, 非属性化工程对应于DECLARE_REGISTRY_APPID_RESOURCEID宏参数中定义的uuid,对于属性化工程则对应于module属性的uuid值; 类型库最后存放在exe/dll的TYPELIB资源中);

3, NumMethods(可选);


clsid:

1, InprocServer32/LocalServer/RemoteServer(Apartment, 默认modulename).

2,ProgID & VersionIndependentProgID.

3,TypeLib(同interface, 类型库最后存放在exe/dll的TYPELIB资源中, 另外如果这个资源不存在则注册会失败,如果不完整则会有部iid/clsid没有注册).

4,Version

appid:
1, {appid} -> 默认(module name without suffix).

2, module name with suffix -> appid键值对.
注:非属性化工程对应于idl文件中的library的uuid属性,对于属性化工程则对应于module属性的uuid值;  另外还需要REGISTRY类型的资源,资源里的%appid%替换成这个值。
 

typelib:

1, HKCR > TypeLib > {libid} > 1.0 > 0 > win32 .

2, HKCR > TypeLib > {libid} > 1.0 > flags.

3, HKCR > TypeLib > {libid} > 1.0 > helpdir(可选).
注:属性化工程,APPID与{libid}共用一个,即module属性里的uuid;
非属性化工程,DECLARE_REGISTRY_APPID_RESOURCEID(IDR_EXECOMSVRTEST, "{3C31F58A-2FAF-463A-9292-AE95726B9794}") 声明APPID; DECLARE_LIBID(LIBID_ExeComSvrTestLib) 声明LIBID_XXXX。
#define DECLARE_LIBID(libid) \
 static void InitLibId() throw() \
 { \
  ATL::CAtlModule::m_libid = libid; \
 }

//============================================  

C++ 类:

类的声明,可以在同一cpp文件中出现多次;

类的定义,同一工程内可以出现多次,但一个cpp内只能出现一次; 

类的静态数据成员/类的成员函数 的实现(非内联),在一个工程里面只能出现一次或者0次(从DLL中引入,不过需要.lib文件)。

C++ 类和类的定义 

http://www.yesky.com/22/194022.shtml 

类的定义格式
  类的定义格式一般地分为说明部分实现部分。说明部分是用来说明该类中的成员,包含数据成员的说明和成员函数的说明。成员函数是用来对数据成员进行操作的,又称为“方法”。实现部分是用来对成员函数的定义。概括说来,说明部分将告诉使用者“干什么”,而实现部分是告诉使用者“怎么干”。

//============================================ 

属性化ATL接口中,如果参数中有另外一个接口的指针,则必须看到这个接口的实现,而不是声明。这点和纯C++不一致,在纯C++中,有类的声明就可以使用类的指针。

原文地址:https://www.cnblogs.com/gakusei/p/2551908.html