Unterkrichtung eines Fensters von einem Thread in C#
-
22-09-2019 - |
Frage
Ich erstelle einen Thread, der nach einem Fenster sucht. Wenn es das Fenster findet, überschreibt es das Fensterproc und behandelt Wm_Command und Wm_Close.
Hier ist der Code, der nach dem Fenster sucht und es unterklebt:
public void DetectFileDialogProc()
{
Window fileDialog = null;
// try to find the dialog twice, with a delay of 500 ms each time
for (int attempts = 0; fileDialog == null && attempts < 2; attempts++)
{
// FindDialogs enumerates all windows of class #32770 via an EnumWindowProc
foreach (Window wnd in FindDialogs(500))
{
IntPtr parent = NativeMethods.User32.GetParent(wnd.Handle);
if (parent != IntPtr.Zero)
{
// we're looking for a dialog whose parent is a dialog as well
Window parentWindow = new Window(parent);
if (parentWindow.ClassName == NativeMethods.SystemWindowClasses.Dialog)
{
fileDialog = wnd;
break;
}
}
}
}
// if we found the dialog
if (fileDialog != null)
{
OldWinProc = NativeMethods.User32.GetWindowLong(fileDialog.Handle, NativeMethods.GWL_WNDPROC);
NativeMethods.User32.SetWindowLong(fileDialog.Handle, NativeMethods.GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(new WindowProc(WndProc)).ToInt32());
}
}
Und das Fensterproc:
public IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
lock (this)
{
if (!handled)
{
if (msg == NativeMethods.WM_COMMAND || msg == NativeMethods.WM_CLOSE)
{
// adding to a list. i never access the window via the hwnd from this list, i just treat it as a number
_addDescriptor(hWnd);
handled = true;
}
}
}
return NativeMethods.User32.CallWindowProc(OldWinProc, hWnd, msg, wParam, lParam);
}
Dies alles funktioniert unter normalen Bedingungen gut. Aber ich sehe zwei Fälle von schlechter Verhalten in der Reihenfolge der Schlechtigkeit:
Wenn ich den Dialog nicht innerhalb einer Minute schließe, stürzt die App ab. Liegt das daran, dass der Faden Müll erfasst wird? Dies würde einen Sinn ergeben, was GC angeht, was den Thread erledigt hat? Wenn dies der Fall ist (und ich weiß nicht, dass dies der Fall ist), wie kann ich den Thread so lange bleiben, solange der Dialog in der Nähe ist?
Wenn ich den Dialog mit der Schaltfläche 'x' sofort schließe (WM_CLOSE), stürzt die App ab. Ich glaube, es stürzt im Fensterproc zusammen, aber ich kann dort keinen Haltepunkt bekommen. Ich erhalte eine AccessViolationException, in der Ausnahme "Versuch, geschütztes Gedächtnis zu lesen oder zu schreiben. Dies ist oft ein Hinweis darauf, dass ein anderer Gedächtnis beschädigt ist." Es ist eine Rennbedingung, aber von dem, was ich nicht weiß. Zu Ihrer Information, ich hatte das alte Fensterproc zurückgesetzt, als ich die Befehle verarbeitet habe, aber das stürzte noch öfter ab!
Irgendwelche Ideen, wie ich diese Probleme lösen kann?
Lösung 2
Kam schließlich auf eine Lösung und griff das Problem aus einem anderen Blickwinkel an. Ich konnte einen systemweiten Haken im verwalteten Code mit SetWineventHook und der Option Winevent_outofContext festlegen, die erstaunlich über die Eigenschaft verfügt: Die Rückruffunktion wird nicht in den Adressraum des Prozesses zugeordnet, der das Ereignis generiert. Ich fege das Event Event_System_Dialogstart, um Benachrichtigungen zu erhalten, wenn ein Dialog erstellt wird, und Event_System_Dialogend, wenn es zerstört wird.
Andere Tipps
Zwei Beobachtungspunkte, die ich machen kann ....
- In deiner
DetectFileDialogProc
, Sie vergleichenwnd
zu null, das ist einIntPtr
Tippen Sie ja? Wenn ja, sollte dieser Scheck über den Vergleich seinif (wnd > IntPtr.Zero){ .... }
- In deiner
WndProc
, Sie verwenden diethis
Variable für dielock
Welches ist eine schlechte Sache ... Sie sollten so etwas tunprivate readonly object objLock = new object();
und in deinemWndProc
benutze daslock (objLock){....}
und prüfen Sie, ob dies das Problem behebt ....