Я пытаюсь использовать класс 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();
}
}
У любого есть идея относительно того, почему я получаю эту ошибку спервый случай?Я просто не вижу этого.