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;
    }

再次 - 有两个调用GetMessageAsStructure. 一个作为类型参数的获取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++ 中使用 which 将成员初始化为默认值并且不影响对齐填充结构,但事实并非如此,因为即使使用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 回答