PInvoke - 如何表示来自 COM 接口的字段

Posted

技术标签:

【中文标题】PInvoke - 如何表示来自 COM 接口的字段【英文标题】:PInvoke - how to represent a field from a COM interface 【发布时间】:2009-10-22 11:14:27 【问题描述】:

我引用了一个如下开头的 COM 结构:

[scriptable, uuid(ae9e84b5-3e2d-457e-8fcd-5bbd2a8b832e)]
interface nsICacheSession : nsISupports

    /**
     * Expired entries will be doomed or evicted if this attribute is set to
     * true.  If false, expired entries will be returned (useful for offline-
     * mode and clients, such as HTTP, that can update the valid lifetime of
     * cached content).  This attribute defaults to true.
     */
    attribute PRBool doomEntriesIfExpired;
...

Source: http://dxr.proximity.on.ca/dxr/mozilla-central/netwerk/cache/public/nsICacheSession.idl.html#58

我找到了将该接口导入我的 C# 应用程序的代码。但是代码肯定是错误的,因为set 方法似乎没有用,并且当我尝试调用它只是为了看看会发生什么时也会引发错误:

[Guid("ae9e84b5-3e2d-457e-8fcd-5bbd2a8b832e"), ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface nsICacheSession

    [return: MarshalAs(UnmanagedType.Bool)]
    void set_doomEntriesIfExpired();
    [return: MarshalAs(UnmanagedType.Bool)]
    bool get_doomEntriesIfExpired();
...

设置doomEntriesIfExpired 值的正确方法是什么?如何从我的代码中引用它?

编辑

我将我的代码更改为以下代码,产生“System.AccessViolationException: Attempt to read or write protected memory yada yada...”:

[Guid("ae9e84b5-3e2d-457e-8fcd-5bbd2a8b832e"), ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface nsICacheSession

    void set_doomEntriesIfExpired(bool enabled);
    bool get_doomEntriesIfExpired();
...

【问题讨论】:

【参考方案1】:

您插入的答案很好。在 COM Interop 中,布尔值默认被编组为 VARIANT_BOOL,因此添加 MarshalAs 属性来告诉编组器使用标准的 4 字节 BOOL 类型是正确的,尽管方程的 getter 部分也需要添加属性。

一般来说,我喜欢将接口中定义的属性保留为属性,而不是将它们分解为它们的 getter 和 setter。它更好地匹配接口定义的语义,通常也更容易阅读。您应该能够按如下方式重写您的 COM 导入定义,以保留 doomEntriesIfExpired 的属性性质:

[Guid("ae9e84b5-3e2d-457e-8fcd-5bbd2a8b832e"), ComImport, 
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface nsICacheSession


    bool doomEntriesIfExpired 
     
        [param:MarshalAs(UnmanagedType.Bool)]set; 
        [return:MarshalAs(UnmanagedType.Bool)]get; 
    

...

【讨论】:

很好,我没有意识到您可以在使用互操作时保留属性。【参考方案2】:

您为void set 方法声明[return: MarshalAs(UnmanagedType.Bool)] 的事实显然是错误的来源。

也就是说,我设法编写了一个没有 .idl 中 [return ...] 标记的 C++ mozilla 插件,例如:

[scriptable, uuid(ae9e84b5-3e2d-457e-8fcd-5bbd2a8b832e)]
interface nsICacheSession : nsISupports

    void set_doomEntriesIfExpired(in bool value);
    bool get_doomEntriesIfExpired();

顺便问一下,你确定你可以用 C# 编写一个 nsi 插件吗?

【讨论】:

我没有编写 NSI 插件,我正在扩展 GeckoFX (geckofx.org) 以便可以访问缓存中的项目 也就是说,您上面的代码如何允许我将值设置为 false?您包含的 set 方法不包含将其设置为的值... 很公平,我修复了setter(我只是复制了你的方法,错过了这一点) 不知道这个geckofx项目,明显是用C#编码的 您是否输入了“in”关键字?【参考方案3】:

原来答案如下:

[Guid("ae9e84b5-3e2d-457e-8fcd-5bbd2a8b832e"), ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface nsICacheSession

    void set_doomEntriesIfExpired([In, MarshalAs(UnmanagedType.Bool)] ref bool enabled);
    bool get_doomEntriesIfExpired();

【讨论】:

以上是关于PInvoke - 如何表示来自 COM 接口的字段的主要内容,如果未能解决你的问题,请参考以下文章

处理来自 C++ DLL 的用户定义异常 - .NET PInvoke/Marshalling

PInvoke - 读取字符串字段的值 - “尝试读取或写入受保护的内存”

使用 PInvoke 连接 C/C++ DLL 的调试断言失败

PInvoke - 如何编组'SomeType * []'?

如何制作一个 PInvoke 友好的原生 API?

如何使用 C#/pinvoke 将输入发送到 WinSta0\Winlogon 桌面