C# 如果在异步方法中使用,为什么GetManifestResourceStream返回null?

C# 如果在异步方法中使用,为什么GetManifestResourceStream返回null?,c#,wpf,async-await,.net-assembly,C#,Wpf,Async Await,.net Assembly,在我的程序中,我将资源文件写入用户选择的某个位置。为此,我使用GetManifestResourceStream。一切都很顺利 接下来,我想让我的写操作不阻塞UI。因此,我使用async await更改了代码。 不幸的是,GetManifestRresourceStream现在返回null。在不使用异步的情况下切换回,等待一切再次正常工作 我做错了什么?我如何解决它,以便能够复制文件,并且UI不会被阻止 这就是在没有async/await的情况下它是如何工作的(请不要被方法名称弄糊涂) 这就是我

在我的程序中,我将资源文件写入用户选择的某个位置。为此,我使用GetManifestResourceStream。一切都很顺利

接下来,我想让我的写操作不阻塞UI。因此,我使用async await更改了代码。 不幸的是,GetManifestRresourceStream现在返回null。在不使用异步的情况下切换回,等待一切再次正常工作

我做错了什么?我如何解决它,以便能够复制文件,并且UI不会被阻止

这就是在没有async/await的情况下它是如何工作的(请不要被方法名称弄糊涂)

这就是我用async/await尝试的方法



        private async Task CopyFileToDestinationAsync(string nameSpace,string outDirectory, string internalPath, string resourceName)
        {
            Assembly assembly = Assembly.GetCallingAssembly();
            using (Stream stream = assembly.GetManifestResourceStream(nameSpace + "." + (internalPath == "" ? "" : internalPath + ".") + resourceName))
            {
                using( BinaryReader br = new BinaryReader(stream))
                {
                    using(FileStream fs = new FileStream(outDirectory + "\\" + resourceName, FileMode.Create))
                    {
                        using (BinaryWriter bw = new BinaryWriter(fs))
                        {
                             await Task.Run( ()=> bw.Write(br.ReadBytes((int)stream.Length)));
                          //  bw.Write(br.ReadBytes((int)stream.Length));
                        }
                    }
                }
            }
            //Thread.Sleep(2000);
            //For User Friendliness wait 2 seconds to finish
            await Task.Run(() => Thread.Sleep(2000));
        }


        private void FinishButton_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Application.Current.Shutdown();
        }

        private async void InstallProgrammAsyc()
        {
            try
            {
                FinalMessage = "";
                PreInstallationBlock.Visibility = Visibility.Collapsed;
                DuringInstallationBlock.Visibility = Visibility.Visible;
                await CopyFileToDestinationAsync("MyNameSpace", InstallPath, "Resources", "some.exe");
                PrepareProgrammForFinish();
            }
            catch (Exception ex)
            {
                DuringInstallationBlock.Visibility = Visibility.Collapsed;
                AfterInstallationBlock.Visibility = Visibility.Visible;
                FinalMessage = $"Unexpected Error occured. Please try again. {ex.Message}";
            }
        }

        private void InstallButton_Click(object sender, RoutedEventArgs e)
        {
            InstallProgrammAsyc();
        }

这是一个有趣的边缘情况,因为异步是如何工作的

让我们看看这些测试方法:

private void Button_Click(object sender, RoutedEventArgs e)
{
    ShowAssembly();
    ShowAssemblyAsync();
}

private void ShowAssembly()
{
    Assembly assembly = Assembly.GetCallingAssembly();

    MessageBox.Show(assembly.FullName);
}

private async void ShowAssemblyAsync()
{
    Assembly assembly = Assembly.GetCallingAssembly();

    MessageBox.Show(assembly.FullName);
}
ShowAssembly
中,它将显示程序主程序集的名称。在ShowAssemblyAsync中,它将显示“mscorlib”。发生什么事了

如果在Visual Studio中的
ShowAssembly
ShowAssemblyAsync
中放置断点,您将获得以下调用堆栈:

DesktopClient.exe!DesktopClient.MainWindow.ShowAssembly() Line 119  C#
DesktopClient.exe!DesktopClient.MainWindow.Button_Click(object sender, System.Windows.RoutedEventArgs e) Line 109   C#
00efe58c 07cf423b DesktopClient.MainWindow.ShowAssembly() 
00efe5a8 07cf41dc DesktopClient.MainWindow.Button_Click(System.Object, System.Windows.RoutedEventArgs) 
那里没什么可疑的。然而,VisualStudio正在欺骗您。如果改用较低级别的调试器(在本例中为WinDbg),则会得到以下调用堆栈:

DesktopClient.exe!DesktopClient.MainWindow.ShowAssembly() Line 119  C#
DesktopClient.exe!DesktopClient.MainWindow.Button_Click(object sender, System.Windows.RoutedEventArgs e) Line 109   C#
00efe58c 07cf423b DesktopClient.MainWindow.ShowAssembly() 
00efe5a8 07cf41dc DesktopClient.MainWindow.Button_Click(System.Object, System.Windows.RoutedEventArgs) 
您可以看到,在应用async关键字后,
ShowAssemblyAsync
中的调用堆栈已完全更改(Visual Studio正在隐藏该关键字以使调试更容易)。一个结果是,
System.Runtime.CompilerServices.asynchvoidMethodBuilder.Start
现在调用该方法,这就是为什么在检索调用程序集时会得到“mscorlib”

作为修复,您可以使用
Assembly.getExecutionGassembly
(假设您的
CopyFileToDestinationAsync
方法位于右侧程序集中)或显式地将程序集作为目标:
typeof(MainWindow).Assembly
。或者,就像您所做的那样,在调用方法之前检索流

另一种方法是引入额外的非异步方法,以便在调用真正的异步实现之前检索程序集:

private Task CopyFileToDestinationAsync(string nameSpace,string outDirectory, string internalPath, string resourceName)
{
    Assembly assembly = Assembly.GetCallingAssembly();

    return CopyFileToDestinationAsync(assembly, nameSpace, outDirectory, internalPath, resourceName);
}

private async Task CopyFileToDestinationAsync(Assembly assembly string nameSpace,string outDirectory, string internalPath, string resourceName)
{
    using (Stream stream = assembly.GetManifestResourceStream(nameSpace + "." + (internalPath == "" ? "" : internalPath + ".") + resourceName))
    {
        // ...
    }
}

这是一个有趣的边缘情况,因为异步是如何工作的

让我们看看这些测试方法:

private void Button_Click(object sender, RoutedEventArgs e)
{
    ShowAssembly();
    ShowAssemblyAsync();
}

private void ShowAssembly()
{
    Assembly assembly = Assembly.GetCallingAssembly();

    MessageBox.Show(assembly.FullName);
}

private async void ShowAssemblyAsync()
{
    Assembly assembly = Assembly.GetCallingAssembly();

    MessageBox.Show(assembly.FullName);
}
ShowAssembly
中,它将显示程序主程序集的名称。在ShowAssemblyAsync中,它将显示“mscorlib”。发生什么事了

如果在Visual Studio中的
ShowAssembly
ShowAssemblyAsync
中放置断点,您将获得以下调用堆栈:

DesktopClient.exe!DesktopClient.MainWindow.ShowAssembly() Line 119  C#
DesktopClient.exe!DesktopClient.MainWindow.Button_Click(object sender, System.Windows.RoutedEventArgs e) Line 109   C#
00efe58c 07cf423b DesktopClient.MainWindow.ShowAssembly() 
00efe5a8 07cf41dc DesktopClient.MainWindow.Button_Click(System.Object, System.Windows.RoutedEventArgs) 
那里没什么可疑的。然而,VisualStudio正在欺骗您。如果改用较低级别的调试器(在本例中为WinDbg),则会得到以下调用堆栈:

DesktopClient.exe!DesktopClient.MainWindow.ShowAssembly() Line 119  C#
DesktopClient.exe!DesktopClient.MainWindow.Button_Click(object sender, System.Windows.RoutedEventArgs e) Line 109   C#
00efe58c 07cf423b DesktopClient.MainWindow.ShowAssembly() 
00efe5a8 07cf41dc DesktopClient.MainWindow.Button_Click(System.Object, System.Windows.RoutedEventArgs) 
您可以看到,在应用async关键字后,
ShowAssemblyAsync
中的调用堆栈已完全更改(Visual Studio正在隐藏该关键字以使调试更容易)。一个结果是,
System.Runtime.CompilerServices.asynchvoidMethodBuilder.Start
现在调用该方法,这就是为什么在检索调用程序集时会得到“mscorlib”

作为修复,您可以使用
Assembly.getExecutionGassembly
(假设您的
CopyFileToDestinationAsync
方法位于右侧程序集中)或显式地将程序集作为目标:
typeof(MainWindow).Assembly
。或者,就像您所做的那样,在调用方法之前检索流

另一种方法是引入额外的非异步方法,以便在调用真正的异步实现之前检索程序集:

private Task CopyFileToDestinationAsync(string nameSpace,string outDirectory, string internalPath, string resourceName)
{
    Assembly assembly = Assembly.GetCallingAssembly();

    return CopyFileToDestinationAsync(assembly, nameSpace, outDirectory, internalPath, resourceName);
}

private async Task CopyFileToDestinationAsync(Assembly assembly string nameSpace,string outDirectory, string internalPath, string resourceName)
{
    using (Stream stream = assembly.GetManifestResourceStream(nameSpace + "." + (internalPath == "" ? "" : internalPath + ".") + resourceName))
    {
        // ...
    }
}

我通过在外部调用GetManifestResourceStream函数并将流作为参数传递给异步函数来解决这个问题。但是,我很想知道为什么它没有像我以前尝试的那样工作。很可能
Assembly.GetCallingAssembly
没有返回您期望的程序集是的。我也试着用其他的。但这也没有帮助,我通过在外部调用GetManifestResourceStream函数并将流作为参数传递给异步函数来解决问题。但是,我很想知道为什么它没有像我以前尝试的那样工作。很可能
Assembly.GetCallingAssembly
没有返回您期望的程序集是的。我也试着用其他的。但那也没用。非常感谢。这真的有助于更好地理解。非常感谢。这确实有助于更好地理解。