PInvoke - 读取字符串字段的值 - “尝试读取或写入受保护的内存”
Posted
技术标签:
【中文标题】PInvoke - 读取字符串字段的值 - “尝试读取或写入受保护的内存”【英文标题】:PInvoke - reading the value of a string field - "Attempted to read or write protected memory" 【发布时间】:2009-10-22 13:52:10 【问题描述】:我在访问 COM 接口中的某些字符串字段时遇到问题。调用整数字段不会导致错误。尝试调用 clientID()
、deviceID()
或 key()
时,我收到旧的“尝试读取或写入受保护的内存” 错误。
接口源代码如下:(代码来源于here)
[scriptable, uuid(fab51c92-95c3-4468-b317-7de4d7588254)]
interface nsICacheEntryInfo : nsISupports
readonly attribute string clientID;
readonly attribute string deviceID;
readonly attribute ACString key;
readonly attribute long fetchCount;
readonly attribute PRUint32 lastFetched;
readonly attribute PRUint32 lastModified;
readonly attribute PRUint32 expirationTime;
readonly attribute unsigned long dataSize;
boolean isStreamBased();
;
下面是访问接口的C#代码:
[Guid("fab51c92-95c3-4468-b317-7de4d7588254"), ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface nsICacheEntryInfo
string clientID();
string deviceID();
nsACString key();
int fetchCount();
Int64 lastFetched();
Int64 lastModified();
Int64 expirationTime();
uint dataSize();
[return: MarshalAs(UnmanagedType.Bool)]
bool isStreamBased();
关于为什么简单地尝试读取字段会引发访问冲突的任何建议?
【问题讨论】:
接口有成员lastFetched、lastModified等,如PRUint32。我不确定那是什么,但它似乎是 32 位的,但您在 c# 代码中使用 Int64。这可能是个问题吗? 我进行了更改,但遇到了同样的异常:( C++代码导出了哪些字符串? 【参考方案1】:此接口中的字符串是 C 样式字符串 (char*) 的变体,但 COM Interop 默认将字符串视为 BSTR。您让编组器尝试读取错误类型的字符串,然后使用 CoTask 内存分配器将其释放,因此您遇到访问冲突也就不足为奇了。如果您的字符串是 [In] 参数,您可以使用适当的 MarshalAs 属性来装饰它们,但这不适用于返回值参数。因此,您需要将它们编组为 IntPtrs,然后手动编组并释放底层内存。
我会尝试以下方法:
[Guid("fab51c92-95c3-4468-b317-7de4d7588254"), ComImport,
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface nsICacheEntryInfo
IntPtr clientID get;
IntPtr deviceID get;
IntPtr key get;
int fetchCount get;
uint lastFetched get;
uint lastModified get;
uint expirationTime get;
uint dataSize get;
[return: MarshalAs(UnmanagedType.Bool)]
bool isStreamBased();
正如克里斯上面提到的,PRUint32s 实际上是 32 位而不是 64 位无符号整数,所以我已经更改了它们。另外,我已经将方法更改为属性,因为这可以更好地捕获 idl 的含义,因为它们都是只读的,它实际上并不影响布局。
clientID 和 deviceID 的字符串可以使用 Marshal.PtrToStrAnsi 读取,如下所示:
nsIMemory memoryManagerInstance = /*maybe get this from somewhere*/;
nsICacheEntryInfo cacheEntryInstance = /*definitely get this from somewhere*/;
IntPtr pClientID = cacheEntryInstance.clientID;
string clientID = Marshal.PtrToStringAnsi(pClientID);
NS_Free(pClientID);
//or
memoryManagerInstance.free(pClientID);
您是使用 NS_Free 还是内存接口来释放字符串取决于您如何使用整个 xpcom 设置。键值是一个抽象字符串,因此它的表示取决于它的来源。看起来它通常可以像其他字符串一样被视为指向 ANSI 字符串的指针。
我没有设置可以为您尝试任何这些,并且文档至少可以说有些不透明,因此您可能需要对此进行一些调整。
【讨论】:
你理应得到 +150 的赏金!请允许我添加我的 +1,我正在处理 C++ DLL 中的(非 COM)遗留代码,它从非托管 -> 托管代码传递了一个标准的 stl c_str()。一切都在 32 位环境下运行良好,但在 64 位环境下就坏了。你的解决方案成功了。谢谢!!【参考方案2】:如果将 [return: MarshalAs(UnmanagedType.BStr)] 语句应用于 clientID 和 deviceID 会怎样?
【讨论】:
以上是关于PInvoke - 读取字符串字段的值 - “尝试读取或写入受保护的内存”的主要内容,如果未能解决你的问题,请参考以下文章