Почему GetManifestResourceStream возвращает нуль, если используется внутри метода asyn c? - PullRequest
0 голосов
/ 28 марта 2020

В моей программе я записываю файл ресурсов в какое-то местоположение, выбранное пользователем. Для этого я использую GetManifestResourceStream. Все работало нормально

Далее я хотел, чтобы моя операция записи не блокировала пользовательский интерфейс. Поэтому я изменил код, используя asyn c await. К сожалению, GetManifestRresourceStream теперь возвращает значение null. Возврат без использования asyn c ждите, все снова работает нормально.

Что я делаю не так и как я могу решить эту проблему, чтобы я мог скопировать файл и пользовательский интерфейс не был заблокирован.

Вот как это работает без асинхронного / ожидающего (пожалуйста, не путайте названия методов)

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

private void InstallProgrammAsyc()
        {
            try
            {
                FinalMessage = "";
                PreInstallationBlock.Visibility = Visibility.Collapsed;
                DuringInstallationBlock.Visibility = Visibility.Visible;
                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 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 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();
        }

1 Ответ

2 голосов
/ 29 марта 2020

Это интересный крайний случай, из-за того, как работает asyn c.

Давайте рассмотрим эти методы тестирования:

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». Что происходит?

Если вы установите точку останова в ShowAssembly и ShowAssemblyAsync в Visual Studio, вы получите следующие стеки вызовов:

DesktopClient.exe!DesktopClient.MainWindow.ShowAssembly() Line 119  C#
DesktopClient.exe!DesktopClient.MainWindow.Button_Click(object sender, System.Windows.RoutedEventArgs e) Line 109   C#
DesktopClient.exe!DesktopClient.MainWindow.ShowAssemblyAsync() Line 128 C#
DesktopClient.exe!DesktopClient.MainWindow.Button_Click(object sender, System.Windows.RoutedEventArgs e) Line 110   C#

Ничего подозрительного там нет , Тем не менее, Visual Studio лжет вам. Если вместо этого я использую отладчик более низкого уровня (в данном случае WinDbg), я получаю следующие стеки вызовов:

00efe58c 07cf423b DesktopClient.MainWindow.ShowAssembly() 
00efe5a8 07cf41dc DesktopClient.MainWindow.Button_Click(System.Object, System.Windows.RoutedEventArgs) 
00efe4d8 07cf43c6 DesktopClient.MainWindow+d__10.MoveNext() 
00efe51c 5ddaa48d System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Start[[System.__Canon, mscorlib]](System.__Canon ByRef)
00efe574 07cf4317 DesktopClient.MainWindow.ShowAssemblyAsync()
00efe5a8 07cf41e6 DesktopClient.MainWindow.ButtonPlus_Click(System.Object, System.Windows.RoutedEventArgs) 

Вы видите, что стэк вызовов из ShowAssemblyAsync полностью изменился после применения ключевое слово asyn c (Visual Studio скрывает это, чтобы упростить отладку). Одним из следствий этого является то, что метод теперь вызывается System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Start, и поэтому мы получаем «mscorlib» при извлечении вызывающей сборки.

В качестве исправления вы можете использовать Assembly.GetExecutingAssembly (при условии, что ваш CopyFileToDestinationAsync метод находится в правильной сборке) или явно указывайте целевую сборку: typeof(MainWindow).Assembly. Или, как и вы, просто извлеките поток перед вызовом метода.

Еще один способ - ввести дополнительный не асинхронный метод c, чтобы просто получить сборку перед вызовом реальной асинхронной реализации c. :

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))
    {
        // ...
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...