PInvoke - 读取字符串字段的值 - “尝试读取或写入受保护的内存”
题
我在访问COM接口中的某些字符串字段时遇到问题。调用整数字段不会导致错误。当尝试调用 clientID()
, deviceID()
或 key()
时,我得到旧的“尝试读取或写入受保护的内存“错误。
以下是源界面代码:(代码来自这里)
[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();
}
关于为什么只是尝试阅读字段的任何建议都应该违反访问权限?
解决方案
此接口中的字符串是C样式字符串(char *)的变体,但默认情况下COM Interop将字符串视为BSTR。您让marshaller尝试读取错误类型的字符串然后使用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();
}
正如Chris上面提到的,PRUint32s实际上是32位而不是64位无符号整数,所以我改变了它们。此外,我已经将方法更改为属性,因为它更好地捕获了idl的含义,因为它们只是readonly它实际上不会影响布局。
可以使用Marshal.PtrToStrAnsi读取clientID和deviceID的字符串,如下所示:
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字符串的指针,就像其他字符串一样。
我没有设置为你尝试任何这个,文档是,至少可以说有点不透明,所以你可能需要对此进行一些调整。
其他提示
如果将[return:MarshalAs(UnmanagedType.BStr)] statememnt应用于clientID和deviceID,该怎么办?