注意:问题已经改写,以便更清楚地区分它:this
我从Windows shell上下文菜单中触发了一些代码,它会显示一个表单以向用户显示进度。
代码实现如下:
资源管理器界面:
public void InvokeCommand(IntPtr pici)
{
CMINVOKECOMMANDINFO ici = (CMINVOKECOMMANDINFO)Marshal.PtrToStructure(
pici, typeof(CMINVOKECOMMANDINFO));
// Identify what was clicked etc...
TransferSalesProject(ici.hwnd);
}
[ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214e4-0000-0000-c000-000000000046")]
internal interface IContextMenu
{
void InvokeCommand(IntPtr pici);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct CMINVOKECOMMANDINFO
{
public uint cbSize;
public CMIC fMask;
public IntPtr hwnd;
public IntPtr verb;
[MarshalAs(UnmanagedType.LPStr)]
public string parameters;
[MarshalAs(UnmanagedType.LPStr)]
public string directory;
public int nShow;
public uint dwHotKey;
public IntPtr hIcon;
}
The code calling the form:
async void TransferSalesProject(IntPtr hWnd)
{
try
{
string basePath = @"C:\Folder1";
string destFolder = @"P:\Folder1"
// Check that project hasn't already been transferred.
if (!Directory.Exists(destFolder))
{
// Move folder to projects current folder.
DirectoryMove dirMove = new DirectoryMove(basePath, destFolder);
dirMove.ShowDialog();
}
else
{
MessageBox.Show("Destination Folder already exists.", "Error");
}
}
catch (Exception e)
{
MessageBox.Show(e.Message, "Exception");
}
}
表格中的代码:
public partial class DirectoryMove : Form
{
public string BaseFolder { get; set; }
public string DestFolder { get; set; }
public Progress<string[]> progress { get; set; }
public DirectoryMove(string baseFolder, string destFolder)
{
InitializeComponent();
this.BaseFolder = baseFolder;
this.DestFolder = destFolder;
progress = new Progress<string[]>();
progress.ProgressChanged += (sender, progressValue) =>
{
if (!this.IsDisposed && this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
this.BringToFront();
this.progressBar.PerformStep();
this.Text = progressValue[0];
this.progressText.Text = progressValue[1];
});
}
else
{
this.BringToFront();
this.progressBar.PerformStep();
this.Text = progressValue[0];
this.progressText.Text = progressValue[1];
}
};
}
private async void DirectoryMove_Load(object sender, EventArgs e)
{
try
{
await Task.Run(() =>
{
try
{
DirectoryCopy(BaseFolder, DestFolder);
}
catch
{
throw;
}
});
}
catch (Exception exception)
{
MessageBox.Show(this, exception.Message, "Exception");
}
finally
{
this.Close();
}
}
private void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs = true)
{
// Get the subdirectories for the specified directory.
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
DirectoryInfo[] dirs = dir.GetDirectories();
// If the destination directory doesn't exist, create it.
if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}
// Get the files in the directory and copy them to the new location.
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string temppath = Path.Combine(destDirName, file.Name);
((IProgress<string[]>)progress).Report(new string[] { "Copying Files", file.FullName });
file.CopyTo(temppath, false);
}
// If copying subdirectories, copy them and their contents to new location.
if (copySubDirs)
{
foreach (DirectoryInfo subdir in dirs)
{
string temppath = Path.Combine(destDirName, subdir.Name);
DirectoryCopy(subdir.FullName, temppath, copySubDirs);
}
}
}
当我解雇InvalidOperationException
时,我似乎得到this.Close()
(DirectoryMove是我的表单的名称):
跨线程操作无效:控制&#39; DirectoryMove&#39;从创建它的线程以外的线程访问。
然而,此异常仅在调试中出现,并且似乎在触发时不一致。当我触发其他上下文菜单项单击事件时,它似乎主要出现,然后触发此事件。
我把它从堆栈中解放出来:
在System.Windows.Forms.Control.get_Handle()
正如错误所示,它的原因是我试图从非UI线程访问该表单。但是form_load事件似乎应该在UI线程上运行。 Philippe Paré建议Invoke
我的表单关闭,这似乎已经解决了。
但它导致了一个问题,为什么我的表单没有在UI线程上运行。我现在能够看到的唯一原因是,当我调用表单时,资源管理器本身正在做某种奇怪的线程。 Phillip建议,当我触发Task.Run时,由于我的代码更改的上下文,但我试图通过简单的表单调用具有相同结构的表单来模拟这个问题,并且还没有能够复制问题。
任何能够解释原因的人都会受到赞赏。