После вызова ReleaseHandle для экземпляра NativeWindow, почему он все еще получает сообщения после удаления (или вообще?) - PullRequest
0 голосов
/ 26 августа 2018

Я пытаюсь использовать класс NativeWindow, чтобы получить дескриптор из модального CommonDialog, открываемого поверх окна WPF.Для этого я перехватываю родительское окно и слушаю сообщение WM_ACTIVATE с WParam из WA_INACTIVE.Это означает, что главное окно теряет фокус, поэтому LParam содержит HWnd окна, получающего фокус, который будет CommonDialog.Эта часть работает, как и ожидалось.

Что не работает, так это то, что после нескольких вызовов я продолжаю получать сообщения об обратном вызове для делегата со сборкой мусора (см. Ниже). Дело в том, что яне знаю почему.

Вот класс ...

public class DialogHwndScope : NativeWindow, IDisposable {

    public DialogHwndScope(Window parentWindow = null){

        if(parentWindow == null)
            parentWindow = Application.Current.MainWindow
            ?? throw new ArgumentNullException(nameof(parentWindow), $"{nameof(parentWindow)} can't be null if there is no main window.");

        var windowInteropHelper = new System.Windows.Interop.WindowInteropHelper(parentWindow);
        windowInteropHelper.EnsureHandle();

        AssignHandle(windowInteropHelper.Handle);
    }

    protected override void WndProc(ref Message m) {

        base.WndProc(ref m);

        // If we're losing focus, grab the handle of the window gaining focus
        if((WM)m.Msg != WM_ACTIVATE || (WA)m.WParam != WA_INACTIVE)
            return;

        // Stop listening to the main window because we're done
        ReleaseHandle();

        // At this point, although we're still in the using scope,
        // we shouldn't be getting any more messages

        var childHandle = m.LParam;

        // Insert code to do something with 'childHandle' here
    }

    public void Dispose(){

        // This is redundant if we've already released it above (which we should have by now),
        // or it could have been released automatically if the window received a WM_DESTROY message,
        // but per MSDN, it's safe to call again so we can blindly call it again here without worry.
        ReleaseHandle();
    }
}

А вот простой пример того, как я его использую ...

var testDialog = new OpenFileDialog();

using (new DialogHwndScope()) {

    testDialog.ShowDialog();
}

Вотсообщение, которое я продолжаю получать ...

Помощник по управляемой отладке 'CallbackOnCollectedDelegate' Message = Помощник по управляемой отладке 'CallbackOnCollectedDelegate': 'Был выполнен обратный вызов для делегата со сборкой мусора типа' System.Windows.Forms!System.Windows.Forms.NativeMethods + WndProc :: Invoke.Это может вызвать сбои приложения, повреждение и потерю данных.При передаче делегатов в неуправляемый код управляемое приложение должно поддерживать их работу до тех пор, пока не будет гарантировано, что они никогда не будут вызваны. '

Я подозреваю, что это потому, что я пытаюсь отцепить окноmid-WndProc, но я не вижу , почему это было бы проблемой, так как это изменяет адрес для обратного вызова Window Proc, который должен влиять только на следующие сообщения, а не на текущее сообщение, которое мы 'переработка.Однако, это действительно , кажется, имеет место, потому что, если я изменяю это на следующее, это работает.

public class DialogHwndScope : NativeWindow, IDisposable {

    public DialogHwndScope(Window parentWindow = null){

        if(parentWindow == null)
            parentWindow = Application.Current.MainWindow
            ?? throw new ArgumentNullException(nameof(parentWindow), $"{nameof(parentWindow)} can't be null if there is no main window.");

        var windowInteropHelper = new System.Windows.Interop.WindowInteropHelper(parentWindow);
        windowInteropHelper.EnsureHandle();

        AssignHandle(windowInteropHelper.Handle);
    }

    // Define a flag to determine if we're processing messages ourselves
    private bool isHookActive = true;

    protected override void WndProc(ref Message m) {

        base.WndProc(ref m);

        if(!isHookActive)
            return;

        // If we're losing focus, grab the handle of the window gaining focus
        if((WM)m.Msg != WM_ACTIVATE || (WA)m.WParam != WA_INACTIVE)
            return;

        // Set flag to say to stop processing messages ourselves
        isHookActive = false;

        var childHandle = m.LParam;

        // Insert code to do something with 'childHandle' here
    }

    public void Dispose(){

        // This is redundant if we've already released it above (which we should have by now),
        // or it could have been released automatically if the window received a WM_DESTROY message,
        // but per MSDN, it's safe to call again so we can blindly call it again here without worry.
        ReleaseHandle();
    }
}

У любого есть идея относительно того, почему я получаю эту ошибку спервый случай?Я просто не вижу этого.

...