-1

I'm currently working on a WinForms .NET application on Visual Studio Community 2022. I'm having some trouble with a library I created, called UpdaterLibrary. This library has all the methods necessary to update my application, but because of that, I can't update that file with its own code, cause that means deleting the file and replacing it by the new one. What I did was using all the methods I could from that library and copying some of that code (the delete and copy methods I created for updating the application files) so I can delete and copy the new UpdaterLibrary from the update path. The problem is that if I use that library, the application doesn't close the file even when I dispose the class or when I put it inside a using statement. My question is: Is there any way to close the file / dispose it after using it? Here's the code:

private static async Task CheckForUpdatesForUpdaterLibrary()
        {
            await Task.Run(async () =>
            {
                try
                {
                    UpdaterLibrary.UpdaterClass updater = await UpdaterLibrary.UpdaterClass.CreateInstance();
                    Version localUpdaterLibraryVersion = await updater.GetFileVersion("UpdaterLibrary.dll", false);

                    if (await updater.VersionIsLower("UpdaterLibrary.dll", localUpdaterLibraryVersion))
                    {
                        updater.Dispose();
                        await UpdateUpdaterLibrary();
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"Hubo un error al actualizar la aplicación. Vuelva a intentarlo más tarde. \n{ex.Message} \n\n {ex.StackTrace}");
                }
            });
        }

private static async Task UpdateUpdaterLibrary()
        {
            await Task.Run(async () =>
            {
                if (await UpdaterLibrary.UpdaterClass.IsRunningAsAdmin())
                {
                    if (!Directory.Exists(UpdatePath))
                    {
                        Directory.CreateDirectory(UpdatePath);
                    }
                    MessageBox.Show("Hay una actualización disponible para la librería del actualizador de la aplicación. Espere un momento mientras se instala la actualización.");
                    List<string> updaterLibraryList = new List<string>
                    {
                        "UpdaterLibrary.dll"
                    };
                    await UpdateFiles(updaterLibraryList);
                }
                else
                {
                    MessageBox.Show("Hay una actualización disponible para la librería del actualizador de la aplicación. Se reiniciará la aplicación y se le pediran permisos para poder actualizarlo.");
                    await UpdaterLibrary.UpdaterClass.RunAsAdmin();
                    Environment.Exit(0);
                }
            });
        }

public static async Task UpdateFiles(List<string> filesToUpdate)
        {
            await Task.Run(async () =>
            {
                HttpClient client = await SetClient(new Version("1.0.0"), token);
                await DownloadNewFiles(filesToUpdate, client);
                await DeleteOldFiles(filesToUpdate);
                await CopyFiles(filesToUpdate);
            });
        }

public static async Task DownloadNewFiles(List<string> newFiles, HttpClient client)
        {
            await Task.Run(async () =>
            {
                string updatePath = UpdatePath;

                JArray contentArray = await GetGitHubContents(client);
                foreach (JObject contentItem in contentArray.Cast<JObject>())
                {
                    string name = (string)contentItem["name"];
                    if (newFiles.Contains(name))
                    {
                        string downloadUrl = (string)contentItem["download_url"];
                        byte[] downloadBytes = await client.GetByteArrayAsync(downloadUrl);
                        string filePath = $@"{updatePath}\{name}";
                        File.WriteAllBytes(filePath, downloadBytes);
                    }
                }
            });
        }

public static async Task DeleteOldFiles(List<string> filesToDelete)
        {
            await Task.Run(() =>
            {
                string path = AppPath;

                foreach (string file in filesToDelete)
                {
                    string fileName = $@"{path}\{file}";
                    File.Delete(fileName);
                }
            });
        }

The code inside the UpdaterLibrary doesn't really matter, that works all well. I don't know if it's not disposing maybe has to be with the fact that I'm using only static methods, but I don't think so.

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
  • 1. Load such libraries in their own application domains, so that you can unload those domain objects as well as the libraries from memory. 2. Instead of relying on that (.NET Core does not support extra domain objects), you should put libraries in versioned folders, so that once a new version is downloaded, your main executable can load the libraries from the new folder when restarted (and delete the old version if needed). – Lex Li Sep 25 '22 at 16:08
  • I don't have a solution for the code above, but take a look at [link](https://en.wikipedia.org/wiki/ClickOnce) (ClickOne) – anaconda Sep 25 '22 at 16:10
  • .NET Framework 4.8 and AppDomains are on their way out. Reconsider your approach. Create a single-file executable that checks for updates and then launches your app, or something like that. – CodeCaster Sep 25 '22 at 16:11
  • A little help from SO [link](https://stackoverflow.com/questions/12787761/how-to-automatically-update-an-application-without-clickonce) – anaconda Sep 25 '22 at 16:11

1 Answers1

0

your question: "Is it possible to dispose a library in C#"

answer:

No, because of the garbage collector which is not able to split memory holded by instances of things inside assemblies.

some details:

If the framework would support unloading linked assemblies like in c / c++, the garbarge collecter would have a big problem to keep the integrity of the object dependency tree. That's the (short) reason, why it is inpossible to unload a loaded assembly in a managed application process.

workarounds:

But, as the guy's said in the comments, you can do a App-Domain, which is more or less a sub-process. The drawback is, like in every inter process communication: it's a different process and you need do deal with it if you want to exchange data between them.

Nobody
  • 149
  • 5