6

私はたくさん見回しましたが、私がやっていることと似たような解決策を見つけることができないようです. ネイティブ C++ アプリとマネージ C# アプリの 2 つのアプリケーションがあります。C++ アプリは、メモリ マネージャーで使用されるバイトのプールを割り当てます。このメモリ マネージャーに割り当てられた各オブジェクトにはヘッダーがあり、各ヘッダーには、オブジェクトに付けられた名前を指す char* があります。C# アプリは、このメモリのビューアーとして機能します。メモリ マップ ファイルを使用して、C++ アプリの実行中に C# アプリがメモリを読み取れるようにします。私の問題は、ヘッダー構造からオブジェクトの名前を読み取ってC#で表示しようとしている(または単に文字列に保存しようとしている)ことです。アンセーフ コードを使用して、 を構成する 4 バイトをに変換char*IntPtr、それを に変換してvoid*、 を呼び出すことができますMarshal.PtrToStringAnsi。これはコードです:

IntPtr namePtr = new IntrPtr(BitConverter.ToInt32(bytes, index));
unsafe
{
    void* ptr = namePtr.ToPointer();
    char* cptr = (char*)ptr;
    output = Marshal.PtrToStringAnsi((IntPtr)ptr);
}

この場合、bytesは、ネイティブ アプリによって作成されたすべてのバイト プールを表すメモリ マップ ファイルから読み取られた配列でindexあり、名前ポインターの最初のバイトのインデックスです。

マネージ側では、 への呼び出しによって返されるnamePtr.ToPointer()アドレスが、ネイティブ アプリの名前ポインターのアドレスとまったく同じであることを確認しました。これがネイティブ コードの場合は、単にptra にキャストするだけchar*で問題ありませんが、私が読んだマネージ コードでは、マーシャラーを使用してこれを行う必要があります。

このコードはさまざまな結果をもたらします。cptrnull の場合もあれば、 を指している場合もあれば\0、いくつかのアジア文字を指している場合もあります (PtrToStringAnsiメソッドを実行すると、一見無関係な文字が生成されます)。何かと思ったのですが、固定ポインタfixedが生成されます。そして、デバッガーToPointerへのキャストの後に、またはそのようなことを言うことがあります(戻ってくるさまざまなものをすべて再現するのは簡単ではありません)。また、メモリを読み取るときにアクセス違反が発生し、C++ 側につながることもあります。char*Unable to evaluate the expression. The pointer is not valid

C++ 側では、ポインタを格納するメモリはメモリ マップド ファイルの一部ですが、テキストを構成する実際のバイトはそうではないため、メモリを実際に読み取る際に問題が発生する可能性があると考えました。そこで、メモリへの読み取り/書き込みアクセスを変更する方法を調べたところ (Windows では注意してください)、Windows ライブラリで VirtualProtect メソッドを見つけました。これは、メモリへのアクセスを PAGE_EXECUTE_WRITECOPY に変更するために使用します。そのアドレスへのポインタがあれば、そこにあるものを少なくとも読み取ることができます。しかし、それでも問題は解決しませんでした。

手短に言えば:

char 配列 (C++ アプリで割り当てられた) の最初の char へのポインター (C#) があり、その char の配列を C# の文字列に読み取ろうとしています。

編集:

ソース ヘッダーは次のようになります。

struct AllocatorHeader
{
// These bytes are reserved, and their purposes may change.
char _reserved[4];

// A pointer to a destructor mapping that is associated with this object.
DestructorMappingBase* _destructor;

// The size of the object this header is for.
unsigned int _size;

char* _name;
};

_nameフィールドは、C# で逆参照しようとしているフィールドです。

編集:

現在のところ、以下に示すソリューションを使用しても、マネージ コードでこの char* を逆参照できません。そのため、メモリ マップト ファイルによって参照されるプールに char* のコピーを作成し、それへのポインターを使用するだけです。これは機能するため、これは保護関連の問題であると私は信じています。ある時点でこれを回避する方法を見つけたら、自分の質問に答えます。それまでは、これが私の回避策になります。助けてくれたすべての人に感謝します!

4

2 に答える 2

1

これは私の簡単なテストでうまくいくようです:

private static unsafe String MarshalUnsafeCStringToString(IntPtr ptr, Encoding encoding) {
    void *rawPointer = ptr.ToPointer();
    if (rawPointer == null) return "";

    char* unsafeCString = (char*)rawPointer;

    int lengthOfCString = 0;
    while (unsafeCString[lengthOfCString] != '\0') {
        lengthOfCString++;
    }

    // now that we have the length of the string, let's get its size in bytes
    int lengthInBytes = encoding.GetByteCount (unsafeCString, lengthOfCString);
    byte[] asByteArray = new byte[lengthInBytes];

    fixed (byte *ptrByteArray = asByteArray) {
        encoding.GetBytes(unsafeCString, lengthOfCString, ptrByteArray, lengthInBytes);
    }

    // now get the string
    return encoding.GetString(asByteArray);
}

おそらく少し複雑ですが、文字列が NUL で終了していると仮定すると、うまくいくはずです。

于 2013-10-21T18:57:36.890 に答える