从c#注册自定义win32窗口类

mor*_*lli 12 c# windows winapi interop

我有一个用WPF编写的新应用程序,需要支持一个旧API,允许它接收已发布到隐藏窗口的消息.通常,另一个应用程序使用FindWindow使用其自定义窗口类的名称来标识隐藏窗口.

1)我假设要实现一个自定义窗口类我需要使用旧学校的win32调用?

我的旧c ++应用程序使用RegisterClass和CreateWindow来创建最简单的隐形窗口.

我相信我应该能够在c#中做同样的事情.我不希望我的项目必须编译任何非托管代码.

我尝试从System.Windows.Interop.HwndHost继承并使用System.Runtime.InteropServices.DllImport来引入上述API方法.

这样做我可以在WPF中成功托管标准的win32窗口,例如"listbox".但是当我为自定义窗口调用CreateWindowEx时,它总是返回null.

我对RegisterClass的调用成功,但我不确定应该将WNDCLASS.lpfnWndProc成员设置为什么.

2)有谁知道如何成功地做到这一点?

mor*_*lli 34

为了记录,我终于得到了这个工作.结果发现我遇到的困难是编组编组问题.我必须更精确地导入win32函数.

下面是在c#中创建自定义窗口类的代码 - 对于支持可能依赖于自定义窗口类的旧API非常有用.

只要消息泵在线程上运行,它应该在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;
}
Run Code Online (Sandbox Code Playgroud)