现在的位置: 首页 > 综合 > 正文

走近COM Interop--编程以实现COM–>Assembly

2012年07月13日 ⁄ 综合 ⁄ 共 3928字 ⁄ 字号 评论关闭
  这篇文章是《走近COM Interop》系列的最后一篇,也是涉及技术点最多的一篇。希望我的描述大家能看到懂,难免有错,请各位不吝赐教。

  书归正传,我开始介绍如果通过编程的方式导入一个COM组件,并生成相应的.Net Assembly。其实,过程非常简单,个人认为只是自己实现了TLBIMP.EXE做的事情而已。(当然,也可以实现TLBEXP.EXE的功能,只是它属于CCW的范畴,这儿就不多说了。)

一、准备工作

  具体说来,编程实现COM-->Assembly的功能,需要使用的以前几个类:

  System.Runtime.InteropServices

   -TypeLibConverter      
提供一组服务,将托管程序集转换为 COM 类型库或进行反向转换。

   -ITypeLibImporterNotifySink  提供回调机制,以供类型库转换器向调用方通知转换的状态,并在转换过程本身之中涉及调用方。

  System.Reflection

   -StrongNameKeyPair(可选)  
封装对公钥或私钥对的访问,该公钥或私钥对用于为强名称程序集创建签名。

  System.Reflection.Emit

   -AssemblyBuilder                           
定义并表示动态程序集。

  此外,还需要使用一个WinAPI,LoadTypeLibEx,具体定义如下:

    [DllImport( "oleaut32.dll", CharSet = CharSet.Unicode, PreserveSig = false )]

          private static extern void LoadTypeLibEx(String strTypeLibName, RegKind regKind, [MarshalAs(UnmanagedType.Interface)] out     Object typeLib );

  
为了让这个WinAPI function可以正常使用,我们还需要定义一个枚举,

private enum RegKind

{

    RegKind_Default 
= 0
,

    RegKind_Register 
= 1
,

    RegKind_None 
= 2


}


  注:上述类的说明来自MSDN。

  大家都看到了,上述几个类中,仅有StrongNameKeyPair是可选的,这是因为如果我们不需要生成PIA,那么是不需要使用这个类的。同时,如果需要生成PIA,那么需要提供相应的密钥文件。在后面的描述中,我们将使用《走近COM Interop--浅谈PIA》中的例子做进一步的演示。

二、实战演练

  在此,我们仍就由VB生成的PIADemo.dll展开演示。

1. 载入一个COM组件

Object typeLib;

LoadTypeLibEx(
"PIADemo.dll", RegKind.RegKind_None, out typeLib); 

        

if(typeLib == null )

{

  
throw new Exception("载入失败!");

}

2. 定义一个实现ITypeLibImporterNotifySink接口的类,基于提供回调机制,以供类型库转换器向调用方通知转换的状态,并在转换过程本身之中涉及调用方。

public class ConversionEventHandler: ITypeLibImporterNotifySink

{

    
public void ReportEvent(ImporterEventKind eventKind, int eventCode, string eventMsg )

    
{

        
// Do nothing.

    }


    

    
public Assembly ResolveRef(object typeLib)

    
{

  
// 此处直接返回null,避免把演示复杂化了

        return null

    }
    

}


3. 将COM类型库生成程序集

 A. 生成PIA Assembly

FileStream stream = new FileStream("common.snk", FileMode.Open);

try

{

    StrongNameKeyPair pair 
= new StrongNameKeyPair(stream);

    TypeLibConverter converter 
= new TypeLibConverter();

    ConversionEventHandler eventHandler 
= new ConversionEventHandler();

    AssemblyBuilder ab 
= converter.ConvertTypeLibToAssembly(typeLib, "interop.PIADemo.dll", TypeLibImporterFlags.PrimaryInteropAssembly, eventHandler, null, pair, nullnull);    

    ab.Save(
"interop.PIADemo.dll");


    MessageBox.Show(
"Importing is ok.");


    Assembly asm 
= Assembly.LoadFile(Application.StartupPath + @"\interop.PIADemo.dll");

    Type t 
= asm.GetType("interop.PIADemo.TestClass");

    
object obj = t.InvokeMember(null, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance, nullnullnull);

    
string ret = (string)t.InvokeMember("Format", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | 

BindingFlags.Instance 
| BindingFlags.InvokeMethod, null, obj, new object[]{"Go!"});

    MessageBox.Show(ret);

}


catch(Exception ep)

{

    
if(stream != null)

    
{

        stream.Close();

    }



    MessageBox.Show(ep.Message);

}

 B. 生成一般的Assembly

TypeLibConverter converter = new TypeLibConverter();

ConversionEventHandler eventHandler 
= new ConversionEventHandler();

AssemblyBuilder ab 
= converter.ConvertTypeLibToAssembly(typeLib, "interop.PIADemo.dll"0

eventHandler, 
nullnullnullnull);    

ab.Save(
"interop.PIADemo.dll");


MessageBox.Show(
"Importing is ok.");


Assembly asm 
= Assembly.LoadFile(Application.StartupPath + @"\interop.PIADemo.dll");

Type t 
= asm.GetType("interop.PIADemo.TestClass");

object obj = t.InvokeMember(null, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance, nullnullnull);

string ret = (string)t.InvokeMember("Format", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | 

   BindingFlags.Instance 
| BindingFlags.InvokeMethod, null, obj, new object[]{"Go!"});

MessageBox.Show(ret);

 需要说明几点:

 1. 上述示例中使用的PIADemo.dll和Common.snk都需要被copy至测试程序的bin目录中,否则,就需要指定可达到的文件路径。

 2. Assembly.LoadFile的参数是要加载的文件的绝对路径,相对路径将会引发异常。

  至此,本系列文章全部完成。因本人水平有限,有不对的地方,欢迎指正。

抱歉!评论已关闭.