So I'm using the CommonOpenFileDialog from the windowsAPICodepack. In a previous version of the application I'm writing, the CommonOpenFileDialog worked without any problems. In the current version targeted at a higher version of the .Net Framework, I get Cross-thread operation not valid exceptions even though the dialog is called from the main UI thread trough a toolstripMenuItem click event from the main form. Before it used to be called in a similar way from a button clickhandler of the main form.
Explicitly Invoking the same code that shows the CommonOpenFileDialog on the same form solves this, but the usual dialog behavior gets lost this way.
This works, but without the this.Invoke it does not.
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){}
}
Furthermore, although the while(wait) is there, the UI is still responsive, while usually that is not the case when a blocking call is made from a button press. Not sure what is going on here...
EDIT: more extensive stacktrace at the bottom.
Call stack when this.Invoke is called:
Edit - this is what the ShowfolderChooser does (returns true when ok, false on cancel):
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;
}
Full exception:
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
But wait...! There's more...
So I've tried putting that code in a separate application and there it works flawlessly. So I guess it is something with my application. One big difference I see is that my Stack frame on the loadWorkspaceToolStripMenuItem_Click
has a stack entry user32.dll![Frames...
Any idea where that one comes from? I assume it has something to do with that...
EDIT2 More extensive stacktrace:
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
From this stacktrace I can confirm that the call is done on the main thread of the UI, since it is called by the MessageLoop as it should be. It seems that there is an issue with the getHandle that goes wrong somewhere. But this is in the API pack it should work...
So after a few hours of work, and digging through the issues of the package on github I found an answer here.
Although the issue is different the cause is the same: problems in getting the window handle. By calling the dialog with the handle of the current form specified there was no cross-thread access anymore in getting the handle.
Final solution: Adding a pointer to the function call. And passing the handle of the form to the dialog.
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;
}