سؤال

لدي تطبيق جديد مكتوب بلغة WPF يحتاج إلى دعم واجهة برمجة التطبيقات القديمة التي تسمح له بتلقي رسالة تم نشرها في نافذة مخفية.عادةً ما يستخدم تطبيق آخر FindWindow للتعرف على النافذة المخفية باستخدام اسم فئة النافذة المخصصة الخاصة بها.

1) أفترض أنني سأحتاج إلى تنفيذ فئة نافذة مخصصة لاستخدام مكالمات Win32 للمدرسة القديمة؟

استخدم تطبيق c++ القديم الخاص بي RegisterClass وCreateWindow لإنشاء أبسط نافذة غير مرئية ممكنة.

أعتقد أنني يجب أن أكون قادرًا على فعل الشيء نفسه في لغة C#.لا أريد أن يقوم مشروعي بتجميع أي تعليمات برمجية غير مُدارة.

لقد حاولت الوراثة من System.Windows.Interop.HwndHost واستخدام System.Runtime.InteropServices.DllImport لسحب أساليب API المذكورة أعلاه.

من خلال القيام بذلك، يمكنني بنجاح استضافة نافذة Win32 قياسية على سبيل المثال."مربع القائمة" داخل WPF.ومع ذلك، عندما أقوم باستدعاء CreateWindowEx لنافذتي المخصصة، فإنها تُرجع دائمًا قيمة فارغة.

تنجح مكالمتي إلى registerClass ، لكنني لست متأكدًا مما يجب أن أقوم بإعداده لعضو wndclass.lpfnwndproc.

2) هل يعرف أحد كيفية القيام بذلك بنجاح؟

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

المحلول

للتسجيل أخيرا حصلت على هذا العمل.تبين أن الصعوبات التي واجهتها كانت بسبب مشاكل تنظيم السلسلة.كان علي أن أكون أكثر دقة في استيراد وظائف win32.

يوجد أدناه الكود الذي سينشئ فئة نافذة مخصصة في c# - وهو مفيد لدعم واجهات برمجة التطبيقات القديمة التي قد تكون لديك والتي تعتمد على فئات النوافذ المخصصة.

يجب أن يعمل إما في WPF أو Winforms طالما أن مضخة الرسائل تعمل على مؤشر الترابط.

يحرر:تم التحديث لإصلاح العطل الذي تم الإبلاغ عنه بسبب التجميع المبكر للمفوض الذي يغلف رد الاتصال.يتم الاحتفاظ بالمفوض الآن كعضو ويتم تنظيم المفوض بشكل صريح كمؤشر دالة.يؤدي هذا إلى حل المشكلة وتسهيل فهم السلوك.

class CustomWindow : IDisposable
{
    delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

    [System.Runtime.InteropServices.StructLayout(
        System.Runtime.InteropServices.LayoutKind.Sequential,
       CharSet = System.Runtime.InteropServices.CharSet.Unicode
    )]
    struct WNDCLASS
    {
        public uint style;
        public IntPtr lpfnWndProc;
        public int cbClsExtra;
        public int cbWndExtra;
        public IntPtr hInstance;
        public IntPtr hIcon;
        public IntPtr hCursor;
        public IntPtr hbrBackground;
        [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
        public string lpszMenuName;
        [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
        public string lpszClassName;
    }

    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    static extern System.UInt16 RegisterClassW(
        [System.Runtime.InteropServices.In] ref WNDCLASS lpWndClass
    );

    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr CreateWindowExW(
       UInt32 dwExStyle,
       [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
       string lpClassName,
       [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
       string lpWindowName,
       UInt32 dwStyle,
       Int32 x,
       Int32 y,
       Int32 nWidth,
       Int32 nHeight,
       IntPtr hWndParent,
       IntPtr hMenu,
       IntPtr hInstance,
       IntPtr lpParam
    );

    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    static extern System.IntPtr DefWindowProcW(
        IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam
    );

    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    static extern bool DestroyWindow(
        IntPtr hWnd
    );

    private const int ERROR_CLASS_ALREADY_EXISTS = 1410;

    private bool m_disposed;
    private IntPtr m_hwnd;

    public void Dispose() 
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing) 
    {
        if (!m_disposed) {
            if (disposing) {
                // Dispose managed resources
            }

            // Dispose unmanaged resources
            if (m_hwnd != IntPtr.Zero) {
                DestroyWindow(m_hwnd);
                m_hwnd = IntPtr.Zero;
            }

        }
    }

    public CustomWindow(string class_name){

        if (class_name == null) throw new System.Exception("class_name is null");
        if (class_name == String.Empty) throw new System.Exception("class_name is empty");

        m_wnd_proc_delegate = CustomWndProc;

        // Create WNDCLASS
        WNDCLASS wind_class = new WNDCLASS();
        wind_class.lpszClassName = class_name;
        wind_class.lpfnWndProc = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(m_wnd_proc_delegate);

        UInt16 class_atom = RegisterClassW(ref wind_class);

        int last_error = System.Runtime.InteropServices.Marshal.GetLastWin32Error();

        if (class_atom == 0 && last_error != ERROR_CLASS_ALREADY_EXISTS) {
            throw new System.Exception("Could not register window class");
        }

        // Create window
        m_hwnd = CreateWindowExW(
            0,
            class_name,
            String.Empty,
            0,
            0,
            0,
            0,
            0,
            IntPtr.Zero,
            IntPtr.Zero,
            IntPtr.Zero,
            IntPtr.Zero
        );
    }

    private static IntPtr CustomWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) 
    {
        return DefWindowProcW(hWnd, msg, wParam, lParam);
    }

    private WndProc m_wnd_proc_delegate;
}

نصائح أخرى

أود التعليق على إجابة morechilli:

public CustomWindow(string class_name){

    if (class_name == null) throw new System.Exception("class_name is null");
    if (class_name == String.Empty) throw new System.Exception("class_name is empty");

    // Create WNDCLASS
    WNDCLASS wind_class = new WNDCLASS();
    wind_class.lpszClassName = class_name;
    wind_class.lpfnWndProc = CustomWndProc;

    UInt16 class_atom = RegisterClassW(ref wind_class);

    int last_error = System.Runtime.InteropServices.Marshal.GetLastWin32Error();

    if (class_atom == 0 && last_error != ERROR_CLASS_ALREADY_EXISTS) {
        throw new System.Exception("Could not register window class");
    }

    // Create window
    m_hwnd = CreateWindowExW(
        0,
        class_name,
        String.Empty,
        0,
        0,
        0,
        0,
        0,
        IntPtr.Zero,
        IntPtr.Zero,
        IntPtr.Zero,
        IntPtr.Zero
    );
}

في المنشئ الذي قمت بنسخه أعلاه يوجد خطأ بسيط:تم إنشاء مثيل WNDCLASS، ولكن لم يتم حفظه.سيتم جمع القمامة في نهاية المطاف.لكن WNDCLASS يحمل مندوب WndProc.وينتج عن هذا خطأ بمجرد تجميع البيانات المهملة لـ WNDCLASS.يجب أن يتم الاحتفاظ بمثيل WNDCLASS في متغير عضو حتى يتم إتلاف النافذة.

1) يمكنك فقط فئة فرعية من فئة Windows Forms العادية ...لا حاجة إلى كل تلك المكالمات win32، كل ما تحتاجه هو تحليل رسالة WndProc يدويًا...هو كل شيء.

2) يمكنك استيراد مساحة الاسم System.Windows.Forms واستخدامها جنبًا إلى جنب مع WPF، وأعتقد أنه لن تكون هناك أية مشكلات طالما أنك لا تدمج الكثير من نماذج Windows في تطبيق WPF الخاص بك.أنت فقط تريد إنشاء نموذج مخفي مخصص لتلقي رسالة، هل هذا صحيح؟

مثال على فئة WndProc الفرعية:

protected override void WndProc(ref System.Windows.Forms.Message m)
{
   // *always* let the base class process the message
   base.WndProc(ref m);

   const int WM_NCHITTEST = 0x84;
   const int HTCAPTION = 2;
   const int HTCLIENT = 1;

   // if Windows is querying where the mouse is and the base form class said
   // it's on the client area, let's cheat and say it's on the title bar instead
   if ( m.Msg == WM_NCHITTEST && m.Result.ToInt32() == HTCLIENT )
      m.Result = new IntPtr(HTCAPTION);
}

نظرًا لأنك تعرف RegisterClass وجميع مكالمات Win32، أفترض أن رسالة WndProc لن تمثل مشكلة بالنسبة لك...

WNDCLASS Wind_class;ضع التعريف في الفصل وليس في الوظيفة، وسيتم إصلاح العطل.

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