Frage

Ich habe eine neue Anwendung in WPF geschrieben, die eine alte API unterstützen muß, dass er eine Nachricht empfangen kann, die zu einem versteckten Fenster gebucht wurden. Typischerweise eine andere Anwendung verwendet Findwindow das versteckte Fenster mit dem Namen seiner benutzerdefinierten Fensterklasse zu identifizieren.

1) Ich nehme an eine benutzerdefinierte Fensterklasse der alten Schule, win32 Anrufe verwenden müssen, implementieren?

Meine alte c ++ Anwendung verwendet Register und Create auf möglichst einfaches unsichtbares Fenster zu machen.

Ich glaube, ich sollte das gleiche tun alles in c # können. Ich will nicht mein Projekt alle nicht verwalteten Code kompilieren müssen.

Ich habe versucht, von System.Windows.Interop.HwndHost vererben und mit System.Runtime.InteropServices.DllImport in den oben genannten API-Methoden zu ziehen.

Dadurch kann ich erfolgreich ein Standard-win32 Fenster Host z.B. "Listbox" innerhalb WPF. Allerdings, wenn ich CreateWindowEx für meine benutzerdefinierten Fenster nenne es immer null zurück.

In den Registern Mein Aufruf erfolgreich ist, aber ich bin nicht sicher, was ich werden soll Einstellung WNDCLASS.lpfnWndProc Mitglied zu.

2) Wer weiß, wie dies erfolgreich zu tun?

War es hilfreich?

Lösung

Für die Aufzeichnung schließlich bekam ich dies funktioniert. Es stellte sich heraus die Schwierigkeiten hatte ich waren bis auf Strang Rangier-Probleme. Ich musste um genauer sein in meinem Import von win32-Funktionen.

Unten ist der Code, der eine benutzerdefinierte Fensterklasse in c # erstellen werden - nützlich für die Unterstützung alte APIs Sie haben könnten, die auf benutzerdefinierten Fensterklassen verlassen

.

Es sollte entweder in WPF arbeiten oder WinForms, solange eine Nachricht Pumpe auf dem Thread ausgeführt wird.

EDIT: Aktualisiert den gemeldeten Absturz zu beheben aufgrund der frühen Sammlung des Delegierten, die die Callback-wickelt. Der Beauftragte wird nun gehalten, wie ein Mitglied und der Beauftragte explizit als Funktionszeiger gemarshallte. Dies behebt das Problem und macht es leichter, das Verhalten zu verstehen.

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

Andere Tipps

Ich mag die Antwort von morechilli Kommentar:

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

Im Konstruktor ich oben kopiert ist leichter Fehler: Die WNDCLASS Instanz erstellt, aber nicht gespeichert. Es wird schließlich Müll gesammelt werden. Aber die WNDCLASS hält die WndProc Delegierten. Dies führt zu einem Fehler, sobald WNDCLASS wird Müll gesammelt. Die Instanz von WNDCLASS sollte halten, in einer Membervariable sein, bis das Fenster zerstört wird.

1) können Sie Unterklasse nur eine normale Windows-Klassen Forms ... keine Notwendigkeit für all jene win32 Anrufe, brauchen Sie nur die WndProc Nachricht manuell zu analysieren ... ist alles.

2) Sie kann den System.Windows.Forms-Namespace importieren und neben WPF verwenden, ich glaube, es wird keine Probleme geben, solange Sie nicht zu viele Fenster tun verflechten Formen in Ihre WPF-Anwendung. Sie wollen einfach nur Ihre benutzerdefinierte versteckt Form instanziiert eine Nachricht an receieve ist das richtig?

Beispiel für WndProc Subklassifizieren:

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

Da Sie bereits wissen Register und alle, die Win32-Aufrufe, gehe ich davon aus der WndProc Nachricht wäre für Sie kein Problem sein ...

WNDCLASS wind_class; setzt die Definition in der Klasse, nicht die Funktion, und der Absturz wird festgelegt werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top