通过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帮助类

代码
    public static class NativeActivationCtxApi
    {
        [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

 

代码
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<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

代码

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<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

 

代码
        private void CreateAndUseDso()
        {
            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来验证这一点。

 

posted @ 2010-08-01 16:06  Erich Wang  阅读(2118)  评论(0编辑  收藏  举报