在UI线程

时间:2016-03-21 01:53:09

标签: c# multithreading winforms task-parallel-library windows-shell

注意:问题已经改写,以便更清楚地区分它: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时,由于我的代码更改的上下文,但我试图通过简单的表单调用具有相同结构的表单来模拟这个问题,并且还没有能够复制问题。

任何能够解释原因的人都会受到赞赏。

0 个答案:

没有答案