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

StackOverflow https://stackoverflow.com/questions/1607380

  •  05-07-2019
  •  | 
  •  

我在访问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,该怎么办?

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top