ما هو الخطأ مع هذا الاستخدام StructLayout( LayoutKind.الصريح ) عند استدعاء PInvoke البنية مع الاتحاد ؟

StackOverflow https://stackoverflow.com/questions/1602487

سؤال

وفيما يلي البرنامج الكامل.أنه يعمل بشكل جيد طالما أنك لا ضافه '#تعريف كسر' في الجزء العلوي.كسر بسبب PInvoke عدم المشير الاتحاد بشكل صحيح.على INPUT_RECORD هيكل في عدد من الأساسات التي يمكن استخدامها اعتمادا على قيمة في EventType.

ما لا أفهمه هو أنني عندما تحدد فقط طفل واحد هيكل KEY_EVENT_RECORD يعمل مع الإعلان الصريح في تعويض 4.ولكن عندما أقوم بإضافة الهياكل الأخرى في نفس الإزاحة هيكل المحتوى الحصول على مخطئا.

//UNCOMMENT THIS LINE TO BREAK IT:
//#define BROKEN

using System;
using System.Runtime.InteropServices;

class ConIOBroken
{
    static void Main()
    {
        int nRead = 0;
        IntPtr handle = GetStdHandle(-10 /*STD_INPUT_HANDLE*/);
        Console.Write("Press the letter: 'a': ");

        INPUT_RECORD record = new INPUT_RECORD();
        do
        {
            ReadConsoleInputW(handle, ref record, 1, ref nRead);
        } while (record.EventType != 0x0001/*KEY_EVENT*/);

        Assert.AreEqual((short)0x0001, record.EventType);
        Assert.AreEqual(true, record.KeyEvent.bKeyDown);
        Assert.AreEqual(0x00000000, record.KeyEvent.dwControlKeyState & ~0x00000020);//strip num-lock and test
        Assert.AreEqual('a', record.KeyEvent.UnicodeChar);
        Assert.AreEqual((short)0x0001, record.KeyEvent.wRepeatCount);
        Assert.AreEqual((short)0x0041, record.KeyEvent.wVirtualKeyCode);
        Assert.AreEqual((short)0x001e, record.KeyEvent.wVirtualScanCode);
    }

    static class Assert { public static void AreEqual(object x, object y) { if (!x.Equals(y)) throw new ApplicationException(); } }

    [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern IntPtr GetStdHandle(int nStdHandle);

    [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern bool ReadConsoleInputW(IntPtr hConsoleInput, ref INPUT_RECORD lpBuffer, int nLength, ref int lpNumberOfEventsRead);

    [StructLayout(LayoutKind.Explicit)]
    public struct INPUT_RECORD
    {
        [FieldOffset(0)]
        public short EventType;
        //union {
        [FieldOffset(4)]
        public KEY_EVENT_RECORD KeyEvent;
#if BROKEN
        [FieldOffset(4)]
        public MOUSE_EVENT_RECORD MouseEvent;
        [FieldOffset(4)]
        public WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
        [FieldOffset(4)]
        public MENU_EVENT_RECORD MenuEvent;
        [FieldOffset(4)]
        public FOCUS_EVENT_RECORD FocusEvent;
        //}
#endif
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct KEY_EVENT_RECORD
    {
        public bool bKeyDown;
        public short wRepeatCount;
        public short wVirtualKeyCode;
        public short wVirtualScanCode;
        public char UnicodeChar;
        public int dwControlKeyState;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct MOUSE_EVENT_RECORD
    {
        public COORD dwMousePosition;
        public int dwButtonState;
        public int dwControlKeyState;
        public int dwEventFlags;
    };

    [StructLayout(LayoutKind.Sequential)]
    public struct WINDOW_BUFFER_SIZE_RECORD
    {
        public COORD dwSize;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct MENU_EVENT_RECORD
    {
        public int dwCommandId;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct FOCUS_EVENT_RECORD
    {
        public bool bSetFocus;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct COORD
    {
        public short X;
        public short Y;
    }
}

تحديث:

بالنسبة لأولئك قلقا بشأن البنية الإعلانات أنفسهم:

  1. منطقي هو تعامل قيمة 32 بت
  2. والسبب في الإزاحة(4) عن البيانات للسماح 32 بت هيكل محاذاة الذي يمنع الاتحاد من بداية عند الإزاحة 2.

مرة أخرى, مشكلتي لا يجعل PInvoke العمل في كل مرة أحاول معرفة سبب هذه الهياكل إضافية (يفترض في نفس الإزاحة) هي fowling البيانات ببساطة عن طريق إضافة لهم.

هل كانت مفيدة؟

المحلول


//UNCOMMENT THIS LINE TO BREAK IT:
//#define BROKEN

باستخدام النظام ؛ باستخدام النظام.وقت التشغيل.InteropServices;

فئة ConIOBroken { static void Main() { الباحث nRead = 0;IntPtr التعامل مع = GetStdHandle(-10 /STD_INPUT_HANDLE/);وحدة التحكم.Write("اضغط على الرسالة:'أ':");

    INPUT_RECORD record = new INPUT_RECORD();
    do
    {
        ReadConsoleInputW(handle, ref record, 1, ref nRead);
    } while (record.EventType != 0x0001/*KEY_EVENT*/);

    Assert.AreEqual((short)0x0001, record.EventType);
    Assert.AreEqual(1u, record.KeyEvent.bKeyDown);
    Assert.AreEqual(0x00000000, record.KeyEvent.dwControlKeyState & ~0x00000020);//strip num-lock and test
    Assert.AreEqual('a', record.KeyEvent.UnicodeChar);
    Assert.AreEqual((short)0x0001, record.KeyEvent.wRepeatCount);
    Assert.AreEqual((short)0x0041, record.KeyEvent.wVirtualKeyCode);
    Assert.AreEqual((short)0x001e, record.KeyEvent.wVirtualScanCode);
    return;
}

static class Assert { public static void AreEqual(object x, object y) { if (!x.Equals(y)) throw new ApplicationException(); } }

[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr GetStdHandle(int nStdHandle);

[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool ReadConsoleInputW(IntPtr hConsoleInput, ref INPUT_RECORD lpBuffer, int nLength, ref int lpNumberOfEventsRead);

[StructLayout(LayoutKind.Explicit)]
public struct INPUT_RECORD
{
    [FieldOffset(0)]
    public short EventType;
    //union {
    [FieldOffset(4)]
    public KEY_EVENT_RECORD KeyEvent;
    [FieldOffset(4)]
    public MOUSE_EVENT_RECORD MouseEvent;
    [FieldOffset(4)]
    public WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
    [FieldOffset(4)]
    public MENU_EVENT_RECORD MenuEvent;
    [FieldOffset(4)]
    public FOCUS_EVENT_RECORD FocusEvent;
}

[StructLayout(LayoutKind.Sequential)]
public struct KEY_EVENT_RECORD
{
    public uint bKeyDown;
    public short wRepeatCount;
    public short wVirtualKeyCode;
    public short wVirtualScanCode;
    public char UnicodeChar;
    public int dwControlKeyState;
}

[StructLayout(LayoutKind.Sequential)]
public struct MOUSE_EVENT_RECORD
{
    public COORD dwMousePosition;
    public int dwButtonState;
    public int dwControlKeyState;
    public int dwEventFlags;
};

[StructLayout(LayoutKind.Sequential)]
public struct WINDOW_BUFFER_SIZE_RECORD
{
    public COORD dwSize;
}

[StructLayout(LayoutKind.Sequential)]
public struct MENU_EVENT_RECORD
{
    public int dwCommandId;
}

[StructLayout(LayoutKind.Sequential)]
public struct FOCUS_EVENT_RECORD
{
    public uint bSetFocus;
}

[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
    public short X;
    public short Y;
}

}

نصائح أخرى

أعتقد أنه سوف يعمل إذا قمت بإجراء bSetFocus و dwCommandId من نوع uint.

في المستقبل, تحقق من PInvoke ويكي لحسن التوقيعات.انها عادة ما تكون دقيقة أو على الأقل نقطة انطلاق جيدة.

1) منطقي العام bKeyDown ينبغي Int32 bKeyDown لأنه منطقي (4بايت) في c++

2) منطقي العام bSetFocus ينبغي Int32

مجرد فكرة, ماذا يحدث إذا كنت تعلن أكبر حقل آخر ؟ ربما P/الاحتجاج هو نسخ تصل إلى الحقل الأخير الذي ينتهي قبل في وقت سابق من المجالات.

حاول إضافة يدويا احتساب حجم الحقل إلى StructLayout السمة مثل هذا:

[StructLayout(LayoutKind.Explicit, Size=...)]

رمز الأصلي مع منطقي الواردة 13 بايت, بدءا من FieldOffset(4) ...
على MOUSE_EVENT_RECORD تبدأ في نفس الإزاحة conatianed 16 بايت تبدأ في نفس الإزاحة.

عند تغيير منطقي (1byte) إلى uint(4بايت) تتكون من 3 بايت.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top