2

バッファ内のいくつかの値がどこから来ているのか、なぜSystem.ExecutionEngineExceptionが発生するのかを理解するのに苦労しています。

私の場合は次のとおりです。私のシステムには、名前付きパイプを介してマネージドサービスと通信するネイティブアプリケーションがあります。ネイティブアプリケーションはWriteFile(pipehandle, &msg, sizeof(msg), &cbBytes, NULL)、次の構造体によって保持されているデータを送信するために使用します。

struct NotificationMessageHeader
{
    __int32 A;
    __int64 B;
    __int32 MessageType;
    __int32 D;
};

struct NotificationMessageA
{
    NotificationMessageHeader Header;
    unsigned char X;
    wchar_t Y[MAX_PATH];
};

マネージドサービスには、次のような構造のマネージドバージョンがあります。

[StructLayout(LayoutKind.Sequential)]
public struct NotificationMessageHeader
{
    public UInt32 A;
    public UInt64 B;
    public UInt32 MessageType;
    public UInt32 D;
}

[StructLayout(LayoutKind.Sequential)]
public struct NotificationMessageA
{
    public NotificationMessageHeader Header;
    [MarshalAs(UnmanagedType.I1)]
    public byte X;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string Y;
}

ネイティブアプリケーションからデータを送信する場合、最初に行うことは、バッファーを汎用構造に読み込むことです。

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GenericNotificationMessage
{
    public NotificationMessageHeader Header;
}

メッセージタイプを判別し、メッセージタイプがサポートされていることを確認した後、この関数を使用して、そのバッファーの残りの部分を適切な構造としてデコードします。

    T GetMessageAsStructure<T>(object data)
        where T : struct
    {
        T output = default(T);
        GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
        try
        {
            IntPtr dataPtr = handle.AddrOfPinnedObject();
            output = (T)Marshal.PtrToStructure(dataPtr, typeof(T));
        }
        finally
        {
            handle.Free();
        }
        return output;
    }

繰り返しますが、への呼び出しは2つありますGetMessageAsStructure。ヘッダーのみをデコードする型引数としての1つが取得され、GenericNotificationMessage正しく機能します。期待どおりにヘッダーフィールドの値を取得しています。次に、メッセージがサポートしているタイプであることがわかった場合はGetMessageAsStructure、typeパラメーター(この場合は)を使用して呼び出しますNotificationMessageA

...そしてここで事態は悪化し始めます。CLRは、アクセス違反の例外で失敗します。管理側にあるバッファ内の値を調べてみました。たとえば、次のようなものを送信した場合です。

    NotificationMessageA msg = { };
    memset(&msg, 0, sizeof(msg));

    msg.Header.A = 2;
            msg.Header.B = 999;
    msg.Header.MessageType = 1;
    msg.Header.D = 3;
    msg.X = 64;
    wcscpy(msg.Y, L"somexec.exe");

    DWORD written = 0;
    WriteFile(_hPipe, &msg, sizeof(msg), &written, NULL);

管理対象バッファの値は次のとおりです。

[0] 2 <---- This shouldn't be at index 3?
[1] 0
[2] 0
[3] 0
[4] 0
[5] 0
[6] 0
[7] 0
[8] 231 <--- WTF is this? should't it start at index 11?
[9] 3
[10] 0
[11] 0
[12] 0
[13] 0
[14] 0
[15] 0
[16] 1
[17] 0
[18] 0
[19] 0
[20] 3
[21] 0
[22] 0
[23] 0
[24] 64
[25] 0
[26] 115
[27] 0
[28] 111
[29] 0
[30] 109
[31] 0
[32] 101
[33] 0
[34] 101
[35] 0
[36] 120
[37] 0
[38] 101
[39] 0
[40] 99
[41] 0
[42] 46
[43] 0
[44] 101
[45] 0
[46] 120
[47] 0
[48] 101
[49] 0
[50] 0

= { }C ++でメンバーごとにデフォルト値に初期化され、アライメントが埋め込まれた構造に影響を与えないものを使用しているため、そこにゴミが入っている可能性がありますが、そうでmemset(..., 0, ...)はありません。受信したバイトに影響します。

さらに、ヘッダーだけが完全にデコードされます。これは、System.ExecutionEngineExceptionを取得している文字列を持つ構造の残りの部分をデコードしようとしている場合のみです。

また、マネージドバッファは、構造を確認することで期待しているものを保持していません-。

なんで?

さらに不可解なのは、Visual StudioがスローされたのはExecutionEngineExceptionであると報告し、MSDNは、この例外はランタイムによってスローされなくなり、廃止されたと述べていることです。

その文字列を逆シリアル化するときに何が間違っていますか?

4

2 に答える 2

4

あなたはポインターとしてマーシャリングNotificationMessageA.Yしていますが、ネイティブコードではポインターではなく構造内のバッファーです。使用する必要がありますUnmanagedType.ByValTStr

于 2012-04-20T07:25:39.477 に答える
2

MiMoの答えに加えて:

Index 0 to 3: MessageHeader.A, little endian
Index 4 to 7: padding, since B must be 8 byte aligned
Index 8 to 15: B, little endian
...
Index 24: X
Index 25: padding
Index 26: start of the wchar_t array Y, but will be incorrectly interpreted as a pointer
于 2012-04-20T07:54:32.517 に答える