随笔 - 89, 评论 - 163, 引用 - 33

导航

关于

标签

每月存档

最新留言

广告

PInvoke 和COM对象

 

   我偶尔会觉得有必要把COM的互操作和PInvoke结合起来。在某些场景中,用PInvoke的声明和方法会更容易编码一些。在这些场景中包括进COM对象,并且在签名上加上合适的Marshal标记也是合法的。

    最简单的完成这些场景的方法是有本地的签名只暴露IUnkown实例。在托管代码这边,用一个对象声明并且标记上MarshalAs(UnmanagedType.IUnknown)。例如:

[DllImport("SomeDll.dll")]

[return: MarshalAs(UnmanagedType.IUnknown)]

public static extern object GetSomeComObject();

     有一条需要记住,在这种场景中怎么处理ref这个关键字。在任何情况下,如果一个COM对象被当作来自于PInvoke的签名,CLR会假定他会去调用IUKnown::Release()。 相对的本地代码必须考虑这种情况,适当的对这个对象AddRef()。

    这已经包括了任何的场景,像上面的代码,COM对象返回的值是function [1].

posted on 2009-01-23 14:25:09 by vbcti  评论(0) 阅读(3890)

IntPtr(long)被悄无声息地截短了吗?

 

[原文作者]jaredpr
      一言以蔽之:不会,任何情况都不回被截短。
     我和同事前几天聊到一个有趣的案例,它涉及到IntPtr, Pinvoke以及64bit整体概念的准确性。 最终这场讨论把我们引向对IntPtr处理long类型的构造函数。令我吃惊的是,这个构造函数的是这么写的:
     public unsafe IntPtr(long value) { this.m_value = (void*) ((int) value); }
     问题在于long类型的值被肆意地截短成一个int值。这将直接导致丢失一切越过4G的内存地址 (换言之,没有64bit的寻址)。这个确确实实放在眼前的大漏洞,使我想到这是不是有可能是个反编译器(disassembler)bug。于是我用了.NET ReflectorIL模式。
    L_0000: ldarg.0
    L_0001: ldarg.1
    L_0002: conv.ovf.i4
    L_0003: conv.i
    L_0004: stfld void* System.IntPtr::m_value
    L_0009: ret
     这段IL证实了long值确确实实是被截短了(还在开始处做了一个溢出检测)。但是且慢,mscorlib.dll是个与处理器运行模式紧密相关的特殊的DLL,这一切有可能仅仅是32位操作系统惹得祸。于是我切换到一台64bit的机器,重新打开Reflector,令我气恼的是面对我的依旧是惊人相同的代码。
     几分钟后我想到打开任务管理器(task manager)看看,竟然看到reflector是个运行在WoW64的进程。这意味着reflector还是调用了32位版本的mscorlib.dll。顺利成章地,我用ildasm打开了64位的mscorlib,同时看到在64位模式,内存地址将不再被截短。
 IL_0000: ldarg.0
 IL_0001: ldarg.1
 IL_0002: conv.u
 IL_0003: stfld      void* System.IntPtr::m_value
 IL_0008: ret
      conv.u 是一个非托管的unsigned int的惯用方法,在64位平台上,conv.u将是一个unsigned 8 字节数值 (详见:OpCodes.Conv_U
概括一下,对于开发人员来说,IntPtr(long)的在不同的平台上做着完全正确的事,实现因平台不同而异。在32位操作系统上,如果一个非4GB的内存地址被当做参数传入的话,实现代码将(正确地)抛出一个异常。在64位的世界,实现代码将按照程序员的意愿返回一个正确的地址,不去截短任何值。

posted on 2008-11-07 14:18:47 by VBCTI  评论(0) 阅读(2658)

Powered by: Joycode.MVC引擎 0.5.2.0