1

所以我正在使用 windowsAPICodepack 中的 CommonOpenFileDialog。在我正在编写的应用程序的早期版本中,CommonOpenFileDialog 没有任何问题。在针对更高版本的 .Net Framework 的当前版本中,即使通过主窗体中的工具条菜单项单击事件从主 UI 线程调用对话框,我也会得到跨线程操作无效异常。之前它曾经以类似的方式从主窗体的按钮单击处理程序中调用。

显式调用在同一表单上显示 CommonOpenFileDialog 的相同代码可以解决此问题,但通常的对话框行为会以这种方式丢失。

这有效,但没有 this.Invoke 则无效。

   private void loadWorkspaceToolStripMenuItem_Click(object sender, EventArgs e)
    {
        bool wait = true;
        this.Invoke((MethodInvoker)delegate ()
        {
            string startPath = LastUsedPath;
            if (FileFolderTools.ShowFolderChooser(ref startPath))
            {
                workspace.LoadWorkspace(startPath);
                LastUsedPath = startPath;
            }

            wait = false;
        });

        while(wait){}

    }

此外,虽然 while(wait) 存在,但 UI 仍然是响应式的,而当按下按钮进行阻塞调用时,通常情况并非如此。不知道这里发生了什么......

编辑:底部更广泛的堆栈跟踪。

调用 this.Invoke 时调用堆栈: 调用 this.Invoke 时的堆栈帧

编辑 - 这是 ShowfolderChooser 所做的(正常时返回 true,取消时返回 false):

 public static bool ShowFolderChooser(ref string path){
        CommonOpenFileDialog dialog = new CommonOpenFileDialog();
        dialog.InitialDirectory = path;
        dialog.IsFolderPicker = true;
        CommonFileDialogResult res = dialog.ShowDialog();
        if (res == CommonFileDialogResult.Ok)
        {
            path = dialog.FileName; //set new path on OK
            return true;
        }
        return false;
    }

完全例外:

This exception was originally thrown at this call stack:
[External Code]
DataManagement.DataManagementCore.Helpers.FileFolderTools.ShowFolderChooser(ref string) in FileFolderTools.cs
Camera._Camera.btn_exportAll_Click(object, System.EventArgs) in Camera.cs
[External Code]
Camera.Program.Main() in Program.cs

可是等等...!还有更多...

所以我尝试将该代码放在一个单独的应用程序中,并且它可以完美地工作。所以我想这与我的应用程序有关。我看到的一个很大不同是我的堆栈框架上loadWorkspaceToolStripMenuItem_Click有一个堆栈条目user32.dll![Frames...知道那个来自哪里吗?估计跟那个有关系...

EDIT2更广泛的堆栈跟踪:

at System.Windows.Forms.Control.get_Handle()
at Microsoft.WindowsAPICodePack.Dialogs.CommonFileDialog.ApplyNativeSettings(IFileDialog dialog)
at Microsoft.WindowsAPICodePack.Dialogs.CommonFileDialog.ShowDialog()
at Camera._Camera.btn_exportAll_Click(Object sender, EventArgs e) in D:\XXXXXXXXXXXXXX\TestProjects\DataManagementTestProject\Camera\Mainscreen\Camera.cs:line 661
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ButtonBase.WndProc(Message& m)
at System.Windows.Forms.Button.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at DataManagementTestProject.Program.Main() in D:\XXXXXXXXXXXXXX\TestProjects\DataManagementTestProject\Program.cs:line 20

从这个堆栈跟踪中,我可以确认调用是在 UI 的主线程上完成的,因为它应该是由 MessageLoop 调用的。似乎 getHandle 有问题在某处出错。但这是在 API 包中它应该可以工作......

4

1 回答 1

1

因此,经过几个小时的工作,并在 github 上挖掘包的问题,​​我在这里找到了答案。

尽管问题不同,但原因是相同的:获取窗口句柄时出现问题。通过使用指定的当前表单的句柄调用对话框,在获取句柄时不再有跨线程访问。

最终解决方案:添加指向函数调用的指针。并将表单的句柄传递给对话框。

string path = LastUsedPath;
if (FileFolderTools.ShowFolderChooser(ref path, this.Handle)){
      workspace.LoadWorkspace(path);
      LastUsedPath = path;
}
    public static bool ShowFolderChooser(ref string path, IntPtr handle)
        {
            CommonOpenFileDialog dialog = new CommonOpenFileDialog();
            dialog.InitialDirectory = path;
            dialog.IsFolderPicker = true;
            CommonFileDialogResult res = dialog.ShowDialog(handle);
            if (res == CommonFileDialogResult.Ok)
            {
                path = dialog.FileName; //set new path on OK
                return true;
            }
            return false;
        }
于 2020-10-26T16:44:06.177 回答