通过Activation Context API使用免注册COM
关于免注册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帮助类
{
[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
<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
{
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来验证这一点。