Pergunta

Eu tenho um novo aplicativo escrito em WPF que as necessidades para apoiar um velho API que lhe permite receber uma mensagem que foi enviada para uma janela oculta. Normalmente outro aplicativo usa FindWindow para identificar a janela escondida usando o nome da sua classe janela personalizada.

1) Eu assumo implementar uma classe de janela personalizada Eu preciso usar chamadas Win32 da velha escola?

Meu velho c ++ aplicativo usado RegisterClass e CreateWindow para fazer o mais simples possível janela invisível.

Eu acredito que eu deveria ser capaz de fazer o mesmo todos dentro de c #. Eu não quero que meu projeto ter que compilar qualquer código não gerenciado.

Eu tentei herdando de System.Windows.Interop.HwndHost e usando System.Runtime.InteropServices.DllImport para puxar os métodos acima API.

Fazendo isso eu posso com sucesso sediar uma janela win32 padrão por exemplo "Caixa de listagem" dentro WPF. No entanto, quando eu chamo CreateWindowEx para minha janela costume ele sempre retorna null.

O meu chamada para RegisterClass tiver êxito, mas não estou certo de que eu deveria estar definindo o membro WNDCLASS.lpfnWndProc a.

2) Alguém sabe como fazer isso com sucesso?

Foi útil?

Solução

Para o registro eu finalmente consegui isso para trabalho. Apagou as dificuldades que tive foram para baixo para problemas seqüência de triagem. Eu tinha que ser mais preciso no meu importação de win32 funções.

Abaixo está o código que irá criar uma classe de janela personalizado em c # - útil para apoiar APIs velhas que você pode ter que dependem de classes de janela personalizados

.

Ele deve funcionar em qualquer WPF ou WinForms enquanto uma bomba de mensagem está sendo executado no segmento.

EDIT: Atualizados para corrigir o acidente relatado devido à coleta início do delegado que envolve o retorno de chamada. O delegado agora é realizada como um membro e o delegado explicitamente empacotado como um ponteiro de função. Isso corrige o problema e torna mais fácil de entender o comportamento.

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

Outras dicas

Eu gostaria de comentar a resposta de 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
    );
}

No construtor Copiei acima é pequeno erro: Instância A WNDCLASS é criado, mas não salva. Ele acabará por ser lixo coletado. Mas a WNDCLASS detém o delegado WndProc. Isso resulta em um erro tão logo WNDCLASS é lixo coletado. A instância do WNDCLASS deve ser espera em uma variável de membro até que a janela é destruída.

1) Você pode apenas subclasse Classe A normal do Windows Forms ... não há necessidade para todas as chamadas Win32, você só precisa analisar a mensagem WndProc manualmente ... é tudo.

2) Você pode importar o namespace System.Windows.Forms e usá-lo ao lado de WPF, eu acredito que não haverá nenhum problema, desde que você não se entrelaçam demais formas janelas em sua aplicação WPF. Você só quer instanciar o formulário personalizado oculto para receieve uma mensagem é mesmo?

exemplo de WndProc subclasses:

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

Uma vez que você já sabe RegisterClass e todas aquelas chamadas Win32, eu assumo a mensagem WndProc não seria um problema para você ...

WNDCLASS wind_class; colocar a definição da classe, e não a função, e o acidente será corrigido.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top