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 * 's)のバリアントですが、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は実際には64ビットではなく32ビットの符号なし整数なので、変更しました。また、メソッドはプロパティに変更しました。これは、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文字列へのポインタとして扱うことができるようです。

私はあなたのためにこれを試す設定を持っていませんし、ドキュメントは控えめに言っても控えめなので、これを少し調整する必要があるかもしれません。

他のヒント

[return:MarshalAs(UnmanagedType.BStr)] statememntをclientIDおよびdeviceIDに適用するとどうなりますか?

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top