关于免注册COM的使用,Simplify App Deployment with ClickOnce and Registration-Free COM 已经有了很详细的介绍。正如该文指出的,Windows会在CreateProcess的早期阶段根据*.exe.manifest创建一个内部表供将来CoCreateInstance使用。如果你无法控制exe.manifest,譬如你需要在Office Word Add-in中使用免注册COM,你不能也不应该修改Word.exe.manifest,那么这种方法就不灵了。
幸好Microsoft提供了Activation Context API。Programming the Activation Context API 详细介绍了如何在C++/C#中使用Activation Context API。下面通过一个简单例子来介绍其具体使用:
1。PInvoke帮助类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
{
[StructLayout(LayoutKind.Sequential, Pack = 4 , CharSet = CharSet.Unicode)]
public struct ACTCTX
{
public int cbSize;
public uint dwFlags;
public string lpSource;
public ushort wProcessorArchitecture;
public Int16 wLangId;
public string lpAssemblyDirectory;
public string lpResourceName;
public string lpApplicationName;
public IntPtr hModule;
}
[DllImport( " Kernel32.dll " , SetLastError = true )]
public static extern IntPtr CreateActCtxW( ref ACTCTX pActCtx);
[DllImport( " Kernel32.dll " , SetLastError = true )]
[ return : MarshalAs(UnmanagedType.Bool)]
public static extern bool ActivateActCtx(IntPtr hActCtx, out IntPtr lpCookie);
[DllImport( " Kernel32.dll " , SetLastError = true )]
[ return : MarshalAs(UnmanagedType.Bool)]
public static extern bool DeactivateActCtx( int dwFlags, IntPtr lpCookie);
[DllImport( " Kernel32.dll " , SetLastError = true )]
public static extern void ReleaseActCtx(IntPtr hActCtx);
public const int ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID = 0x004 ;
}
public static class ActivationCtx
{
public static bool Activate(Action action, string manifestPath, string assemblyRootDir)
{
NativeActivationCtxApi.ACTCTX ac = new NativeActivationCtxApi.ACTCTX();
ac.cbSize = Marshal.SizeOf( typeof (NativeActivationCtxApi.ACTCTX));
ac.lpAssemblyDirectory = assemblyRootDir;
ac.lpSource = manifestPath;
ac.dwFlags = NativeActivationCtxApi.ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID;
IntPtr cookie;
IntPtr hActCtx = NativeActivationCtxApi.CreateActCtxW( ref ac);
if (hActCtx == (IntPtr)( - 1 ))
return false ;
if (NativeActivationCtxApi.ActivateActCtx(hActCtx, out cookie))
{
try
{
action();
}
finally
{
NativeActivationCtxApi.DeactivateActCtx( 0 , cookie);
}
}
else
{
return false ;
}
NativeActivationCtxApi.ReleaseActCtx(hActCtx);
return true ;
}
}
2. COM组件及对应的manifest,这里以dsofile.dll(下载)为例,你可以在dsofile安装目录下运行VS命令行工具mt -tlb:dsofile.dll -dll:dsofile.dll -identity:dsofile.x,type=win32,version=1.0.0.0 -out:dsofile.x.manifest生成必要的manifest file:dsofile.x.manifest
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
< assembly xmlns ="urn:schemas-microsoft-com:asm.v1" manifestVersion ="1.0" >
< assemblyIdentity name ="dsofile.x" type ="win32" version ="1.0.0.0" ></ assemblyIdentity >
< file name ="dsofile.dll" hashalg ="SHA1" >
< comClass clsid =" {58968145-CF05-4341-995F-2EE093F6ABA3} " tlbid =" {58968145-CF00-4341-995F-2EE093F6ABA3} " description ="DSOFile OleDocumentProperties" ></ comClass >
< typelib tlbid =" {58968145-CF00-4341-995F-2EE093F6ABA3} " resourceid ="1" version ="2.1" helpdir ="" flags ="HASDISKIMAGE" ></ typelib >
</ file >
< comInterfaceExternalProxyStub name ="CustomProperty" iid =" {58968145-CF03-4341-995F-2EE093F6ABA3} " tlbid =" {58968145-CF00-4341-995F-2EE093F6ABA3} " proxyStubClsid32 =" {00020424-0000-0000-C000-000000000046} " >
</ comInterfaceExternalProxyStub >
< comInterfaceExternalProxyStub name ="CustomProperties" iid =" {58968145-CF04-4341-995F-2EE093F6ABA3} " tlbid =" {58968145-CF00-4341-995F-2EE093F6ABA3} " proxyStubClsid32 =" {00020424-0000-0000-C000-000000000046} " >
</ comInterfaceExternalProxyStub >
< comInterfaceExternalProxyStub name ="SummaryProperties" iid =" {58968145-CF02-4341-995F-2EE093F6ABA3} " tlbid =" {58968145-CF00-4341-995F-2EE093F6ABA3} " proxyStubClsid32 =" {00020424-0000-0000-C000-000000000046} " >
</ comInterfaceExternalProxyStub >
< comInterfaceExternalProxyStub name ="_OleDocumentProperties" iid =" {58968145-CF01-4341-995F-2EE093F6ABA3} " tlbid =" {58968145-CF00-4341-995F-2EE093F6ABA3} " proxyStubClsid32 =" {00020424-0000-0000-C000-000000000046} " >
</ comInterfaceExternalProxyStub >
</ assembly >
3.直接传给Activation Context的manifest: regFree.manifest
代码
< assembly xmlns ="urn:schemas-microsoft-com:asm.v1" manifestVersion ="1.0" >
< assemblyIdentity type ="win32" name ="regFree" version ="1.0.0.0" />
< dependency >
< dependentAssembly >
< assemblyIdentity
type ="win32"
name ="dsofile.x"
version ="1.0.0.0" />
</ dependentAssembly >
</ dependency >
</ assembly >
4. 在你的程序中使用Activation Context,假设dsofile.dll以及dsofile.x.manifest位于c:\dsofile
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
{
OleDocumentProperties properties = new OleDocumentProperties();
properties.Open( " c:\\my.doc " , true , dsoFileOpenOptions.dsoOptionOnlyOpenOLEFiles);
}
private void UseContext()
{
ActivationCtx.Activate(CreateAndUseDso, " d:\\yourApp\\regFree.manifest " , " c:\\dsofile " );
}
在这里有一点需要指出的是,貌似第三步的regFree.manifest可以不需要,可以在第四步中直接传入dsofile.x.manifest。满足下面条件之一你的确可以这么做:
a。你的程序运行在Windows 7上(没有在Vista上测过)
b。你可以确保COM dll和application(your executable)位于同一目录下
在XP/2003下,如果你在传入context api的manifest中直接指定COM文件,系统会忽略掉你指定的lpAssemblyDirectory而直接在程序exe的目录下寻找COM,有兴趣的可以通过Process Monitor来验证这一点。