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

3
我在访问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();
}

有什么建议可以解释为什么尝试读取一个字段会导致访问冲突?


该接口具有成员lastFetched、lastModified等,其类型为PRUint32。我不确定这是什么,但它似乎是32位,然而你在C#代码中使用的是Int64。这可能会出现问题吗? - Chris Dunaway
我做了更改,但仍然得到相同的异常 :( - Nathan Ridley
C++代码会导出哪些类型的字符串? - Danny Varod
2个回答

3
在此接口中的字符串是C风格字符串(char *),但COM互操作默认将字符串视为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();
}

正如Chris上面提到的,PRUint32实际上是32位而不是64位的无符号整数,所以我已经进行了更改。此外,我将方法更改为属性,因为这更好地捕捉了IDL的含义,由于它们都是只读的,实际上并不影响布局。

可以使用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字符串的指针。

我没有任何设置来为您尝试这些内容,而文档则有点晦涩难懂,因此您可能需要对此进行一些微调。


你非常值得获得150美元的赏金!让我来加上我的+1。我正在处理一个C++ DLL中的(非COM)遗留代码,该代码从非托管代码传递了一个标准的stl c_str()到托管代码。 在32位环境下一切都很顺利,但在64位环境下却出现了问题。 你的解决方案解决了这个问题。 谢谢! - AVIDeveloper

0
如果您将 [return: MarshalAs(UnmanagedType.BStr)] 语句应用于 clientID 和 deviceID,会发生什么呢?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接