Dispatcher.Run 仅当应用程序 运行 作为 RDS 中已发布的应用程序时才生成 Win32Exception
Dispatcher.Run generates Win32Exception only when application is run as published application in RDS
我有一个遗留模式对话框,需要从 windows WPF/C# 应用程序中显示。它通常运行良好,但在 RDS 中已发布应用程序的特定情况下,如果用户在主应用程序之间等待几分钟,然后调用对话框,它将崩溃并出现一个相当神秘的错误。
我想知道如何获取调度程序的消息列表运行:如果我可以分析正在处理的消息,那么我就可以了解潜在的问题。
实际异常是 Win32Exception。消息是 "The system cannot find the file specified"。 HResult 是 x80004005。
完整的错误文本是:
System.ComponentModel.Win32Exception (0x80004005): The system cannot find the file specified
at MS.Win32.UnsafeNativeMethods.GetWindowText(HandleRef hWnd, StringBuilder lpString, Int32 nMaxCount)
at System.Windows.Automation.Peers.WindowAutomationPeer.GetNameCore()
at System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree()
at System.Windows.Automation.Peers.AutomationPeer.UpdatePeer(Object arg)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Threading.DispatcherOperation.Invoke()
at System.Windows.Threading.Dispatcher.ProcessQueue()
at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.Disp
我已经在它获得 window 文本的唯一地方放置了断点,但没有产生任何有用的东西。
实际的遗留线程在这里:
public void InitLegacyThread(string config, string userName, string userPswd, double userLOC)
{
_legacyThread = new Thread(() =>
{
try
{
Application app = new Application();
app.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri(XamlResourcesLocation, UriKind.Relative) });
ClientServiceLocator.GetInstance<ILegacyMLDispatchThread>().Initialize(Dispatcher.CurrentDispatcher);
if (Thread.CurrentThread.ThreadState != ThreadState.Aborted)
{
Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;
}
RegisterAppServicesAndEvents();
_initializationSuccessful = ServiceProvider.Instance.GetService<IInteropAdapter>().FinishInitializing(config, userName, userPswd, userLOC);
if (_initializationSuccessful)
{
BringPopupDialogsToFront();
ClientServiceLocator.GetInstance<IEventAggregator>().Subscribe(this);
InitializeCommonShellWindow();
CreateBindings();
if (Thread.CurrentThread.ThreadState != ThreadState.Aborted)
{
Thread.CurrentThread.Priority = ThreadPriority.Normal;
}
// Signal the that legacy thread is now ready
ClientServiceLocator.GetInstance<ILegacyMLDispatchThread>().SignalThreadReady();
try
{
Dispatcher.Run();
}
catch (Win32Exception e)
{
ExceptionHandler.ShowException(e, e.Message);
}
}
else
{
AbortInitialization();
try
{
Dispatcher.Run();
}
catch (Win32Exception e)
{
ExceptionHandler.ShowException(e, e.Message);
}
}
}
catch (Exception e)
{
ExceptionHandler.ShowException(e, e.Message);
}
});
_legacyThread.SetApartmentState(ApartmentState.STA);
_legacyThread.Name = MLThreadName;
_legacyThread.Start();
}
答案是可以通过使用 DispatcherHookEventHandler 和 HwndSourceHook 来做到这一点。由 Dispatcher for SDK type windows messages 处理的消息并不像我最初希望的那样是一对一的对应关系。但是,可以通过在目标 window.
上专门添加一个钩子来达到这一点
public static IntPtr MessageReader(IntPtr hwnd, int message, IntPtr lParam, IntPtr wParam, ref bool result)
{
_log.Error(string.Format("MessageReader - {0}, {1}, {2}, {3}", hwnd, message, lParam, wParam));
return IntPtr.Zero;
}
public static IntPtr MessageReader(IntPtr hwnd, int message, IntPtr lParam, IntPtr wParam, ref bool result)
....
FieldInfo ArgsField = typeof(DispatcherOperation).GetField("_args", BindingFlags.NonPublic | BindingFlags.Instance);
Dispatcher.CurrentDispatcher.Hooks.OperationStarted += new DispatcherHookEventHandler((obj, args) =>
{
System.Windows.Interop.HwndSource source = ArgsField.GetValue(args.Operation) as System.Windows.Interop.HwndSource;
if (source != null)
{
source.AddHook(new System.Windows.Interop.HwndSourceHook(MessageReader));
}
});
Dispatcher.Run();
这完全是为了找出哪条 window 消息导致了我的特定问题。但是通过这种方式,可以在 WPF 应用程序中处理 SDK 类型消息之前查看它们。
我有一个遗留模式对话框,需要从 windows WPF/C# 应用程序中显示。它通常运行良好,但在 RDS 中已发布应用程序的特定情况下,如果用户在主应用程序之间等待几分钟,然后调用对话框,它将崩溃并出现一个相当神秘的错误。
我想知道如何获取调度程序的消息列表运行:如果我可以分析正在处理的消息,那么我就可以了解潜在的问题。
实际异常是 Win32Exception。消息是 "The system cannot find the file specified"。 HResult 是 x80004005。
完整的错误文本是:
System.ComponentModel.Win32Exception (0x80004005): The system cannot find the file specified
at MS.Win32.UnsafeNativeMethods.GetWindowText(HandleRef hWnd, StringBuilder lpString, Int32 nMaxCount)
at System.Windows.Automation.Peers.WindowAutomationPeer.GetNameCore()
at System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree()
at System.Windows.Automation.Peers.AutomationPeer.UpdatePeer(Object arg)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Threading.DispatcherOperation.Invoke()
at System.Windows.Threading.Dispatcher.ProcessQueue()
at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.Disp
我已经在它获得 window 文本的唯一地方放置了断点,但没有产生任何有用的东西。
实际的遗留线程在这里:
public void InitLegacyThread(string config, string userName, string userPswd, double userLOC)
{
_legacyThread = new Thread(() =>
{
try
{
Application app = new Application();
app.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri(XamlResourcesLocation, UriKind.Relative) });
ClientServiceLocator.GetInstance<ILegacyMLDispatchThread>().Initialize(Dispatcher.CurrentDispatcher);
if (Thread.CurrentThread.ThreadState != ThreadState.Aborted)
{
Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;
}
RegisterAppServicesAndEvents();
_initializationSuccessful = ServiceProvider.Instance.GetService<IInteropAdapter>().FinishInitializing(config, userName, userPswd, userLOC);
if (_initializationSuccessful)
{
BringPopupDialogsToFront();
ClientServiceLocator.GetInstance<IEventAggregator>().Subscribe(this);
InitializeCommonShellWindow();
CreateBindings();
if (Thread.CurrentThread.ThreadState != ThreadState.Aborted)
{
Thread.CurrentThread.Priority = ThreadPriority.Normal;
}
// Signal the that legacy thread is now ready
ClientServiceLocator.GetInstance<ILegacyMLDispatchThread>().SignalThreadReady();
try
{
Dispatcher.Run();
}
catch (Win32Exception e)
{
ExceptionHandler.ShowException(e, e.Message);
}
}
else
{
AbortInitialization();
try
{
Dispatcher.Run();
}
catch (Win32Exception e)
{
ExceptionHandler.ShowException(e, e.Message);
}
}
}
catch (Exception e)
{
ExceptionHandler.ShowException(e, e.Message);
}
});
_legacyThread.SetApartmentState(ApartmentState.STA);
_legacyThread.Name = MLThreadName;
_legacyThread.Start();
}
答案是可以通过使用 DispatcherHookEventHandler 和 HwndSourceHook 来做到这一点。由 Dispatcher for SDK type windows messages 处理的消息并不像我最初希望的那样是一对一的对应关系。但是,可以通过在目标 window.
上专门添加一个钩子来达到这一点 public static IntPtr MessageReader(IntPtr hwnd, int message, IntPtr lParam, IntPtr wParam, ref bool result)
{
_log.Error(string.Format("MessageReader - {0}, {1}, {2}, {3}", hwnd, message, lParam, wParam));
return IntPtr.Zero;
}
public static IntPtr MessageReader(IntPtr hwnd, int message, IntPtr lParam, IntPtr wParam, ref bool result)
....
FieldInfo ArgsField = typeof(DispatcherOperation).GetField("_args", BindingFlags.NonPublic | BindingFlags.Instance);
Dispatcher.CurrentDispatcher.Hooks.OperationStarted += new DispatcherHookEventHandler((obj, args) =>
{
System.Windows.Interop.HwndSource source = ArgsField.GetValue(args.Operation) as System.Windows.Interop.HwndSource;
if (source != null)
{
source.AddHook(new System.Windows.Interop.HwndSourceHook(MessageReader));
}
});
Dispatcher.Run();
这完全是为了找出哪条 window 消息导致了我的特定问题。但是通过这种方式,可以在 WPF 应用程序中处理 SDK 类型消息之前查看它们。