仅当应用程序作为RDS中的已发布应用程序运行时,Dispatcher.Run才会生成Win32Exception

时间:2015-07-01 20:27:50

标签: c# wpf multithreading legacy win32gui

我有一个传统的模式对话框,我需要从Windows WPF / C#应用程序中呈现。它通常运行良好,但是在RDS中已发布应用程序的特定情况下,如果用户在主应用程序之间等待几分钟然后调用对话框,它将会崩溃并出现相当神秘的错误。

我想知道如何获取调度程序正在运行的消息列表:如果我可以分析正在处理的消息,那么我就可以了解基础问题。

实际的异常是Win32Exception。消息是"系统找不到指定的文件"。 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

我已经在它获取窗口文本的唯一地方放置了断点但是没有产生任何有用的东西。

实际的遗留线程在这里:

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

1 个答案:

答案 0 :(得分:0)

答案是可以使用DispatcherHookEventHandler和HwndSourceHook来做到这一点。 Dispatcher for SDK类型窗口消息处理的消息不是一对一的对应关系,正如我原先希望的那样。但是,可以通过在目标窗口上专门添加一个钩子来达到这一点。

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

这主要是为了找出哪个窗口消息对我的特定问题有贡献。但是通过这种方式,可以在WPF应用程序中处理SDK类型消息之前查看它们。