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 * '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に適用するとどうなりますか?