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 的调试断言失败