0

I am working on a application that extracts and moves files around to specific directories... At the same time deletes the files it used to extract, and etc etc etc. Just like a basic installer.

Here is what I'm having problems with:

   Dim DirectoryToInstall As String = Browse.TextBox.Text

Private Sub InstallMods()

    If HPV2.Checked = True Then
                My.Computer.FileSystem.MoveFile(appData & "\Svatekl3\Svatekl2\Team HP Idicator\V3\gui\Scaleform\FragCorrelation.swf", (DirectoryToInstall) & "\res_mods\0.9.4\gui\scaleform\FragCorrelation.swf")
                My.Computer.FileSystem.MoveFile(appData & "\Svatekl3\Svatekl2\Team HP Idicator\V3\scripts\client\mods\TeamHPPools.pyc", (DirectoryToInstall) & "\res_mods\0.9.4\scripts\client\mods\TeamHPPools.pyc")
                My.Computer.FileSystem.MoveFile(appData & "\Svatekl3\Svatekl2\Team HP Idicator\V3\scripts\client\gui\scaleform\daapi\view\lobby\settings\settingswindow.pyc", (DirectoryToInstall) & "\res_mods\0.9.4\scripts\client\gui\scaleform\daapi\view\lobby\settings\settingswindow.pyc")
                My.Computer.FileSystem.MoveFile(appData & "\Svatekl3\Svatekl2\Team HP Idicator\V3\ModSettings\ModSettings.cfg", (DirectoryToInstall) & "\res_mods\ModSettings\ModSettings.cfg")
                My.Computer.FileSystem.MoveFile(appData & "\Svatekl3\Svatekl2\Team HP Idicator\V3\ModSettings\MultilinedTankCarousel.cfg", (DirectoryToInstall) & "\res_mods\ModSettings\MultilinedTankCarousel.cfg")
                My.Computer.FileSystem.MoveFile(appData & "\Svatekl3\Svatekl2\Team HP Idicator\V3\ModSettings\Team HP Pools and Healthbar.cfg", (DirectoryToInstall) & "\res_mods\ModSettings\Team HP Pools and Healthbar.cfg")
            End If

End Sub

(This is just a sample code... There is a ton more to it :P ) There are around 200 files that it has to move, depending if the person checks a check mark to move those files or not.

This is how my backgroundworker looks like:

Private Sub BackgroundWorker_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker.DoWork
    Dim DirectoryToInstall As String = Label4.Text

    DeleteTemp()

    CleanMods()

    MoveZip()

    MyExtract()


    CreateDirs()

    InstallMods()

    DeleteTemp()

    Finish.StartPosition = FormStartPosition.CenterScreen
    Finish.Show()
    Me.Hide()
End Sub

It's pretty self-explanatory... The DeleteTemp() delete's files that were created by the program that are not needed.

The CleanMods() deletes files in a game directory selected by the user, to make sure all the mods he installs using the installer work.

The MoveZip() moves a zip file from resources to appData.

MyExtract() Extracts those files

CreateDirs() Creates folders for the files

InstallMods() moves the files into the correct directories

And I do DeleteTemp() again...

I explained this all so you knew what the program is supposed to do...

Now back to my problem... If I use the UI Thread, everything works perfectly! It deletes all the files, installs all the files, extracts everything, and etc.

The only draw-back is the UI get's frozen.

Now I thought of using a secondary thread and nothing really worked out :(

So I decided to give backgroundworker a shot... I put all my code inside the Backgroundworker and ran the program.

Every time the program gets to the part to install the files, it tells me that "This files already exists"..

But at the same time I was watching my folder, and it looks like the backgroundworker has the wrong directory... Because it wasn't installing the files into the directory (nor did it make any changes to that directory at all)

What I mean is... Let's say that Dim DirectoryToInstall As String = TextBox1.Text

The TextBoxt1.Text = Whatever the user selects in the file browser.

For instance I select the folder "Desktop"...

So the program INSTEAD of installing the files onto desktop installs them to some other folder.

Why does this happen?

If I use the UI thread.. There are no issues.

I heard that backgroundworker, since it uses a different thread, cannot receive any information from the UI thread.

So I somehow have to send that information to my backgroundworker myself, but how?

Update:

Looks like backgroundworker doesn't want to run this code:

Private Sub CleanMods()


        Dim appData As String = GetFolderPath(SpecialFolder.ApplicationData)
        If CleanOutFolders.RadioButton1.Checked = True Then

            Try
                Dim path As String = (DirectoryToInstall) & "\res_mods"
                My.Computer.FileSystem.DeleteDirectory(path, FileIO.RecycleOption.DeletePermanently, FileIO.UICancelOption.DoNothing)
            Catch
                MessageBox.Show("res_mods not found!")
            End Try

            Try
                FileSystem.Kill((DirectoryToInstall) & "\res\audio\xvm.fsb")
                FileSystem.Kill((DirectoryToInstall) & "\res\audio\xvm.fev")
                FileSystem.Kill((DirectoryToInstall) & "\res\audio\voice.fsb")
                If File.Exists((DirectoryToInstall) & "\res\audio\voice.fev") Then
                    FileSystem.Kill((DirectoryToInstall) & "\res\audio\voice.fev")
                End If
            Catch
            End Try
        End If
    End Sub
  • You're accessing a UI element within your background worker. You shouldn't be doing that. You should give the background worker the information it needs *separately* from the UI. – Jon Skeet Nov 24 '14 at 18:22
  • That is what I was thinking... That's why I came for help, I tried Googling, I found nothing! How do I do this :( ? –  Nov 24 '14 at 18:22
  • 1
    Well `BackgroundWorker.RunWorkAsync` takes an argument - so consider creating a class with all the information you need, create that, then call `worker.RunWorkerAsync(info)`. If it's just a single string, that's fine - you can just pass that directly. You'll need to move the final few lines into an event handler for `RunWorkerCompleted` though, as they modify the UI. – Jon Skeet Nov 24 '14 at 18:25
  • @JonSkeet Well I will give it a shot! I will get you back in like 5 minutes. –  Nov 24 '14 at 18:28
  • Exceptions need to be properly handled in a background worker, check this answer http://stackoverflow.com/a/258664/130611 – the_lotus Nov 24 '14 at 18:33
  • @JonSkeet O_O I didn't work... So what I did was added a MessageBox.Show(DirectoryToInstall) - to see where the backgroundworker was trying to install the files. It seems like it was trying to install the files into the correct directory. This leads me to a different problem! Looks like my CleanMods() isn't working with the backgroundworker. I will post the code right now on the thread. –  Nov 24 '14 at 18:34
  • @the_lotus That's one way of doing it.. I have a question though, does Backgroundworker.Completed use the UI thread? Or does the code in Backgroundworker.Completed still use a seperate thread? –  Nov 24 '14 at 18:39
  • @the_lotus I just gave your method a shot! It worked!!! Thank you a lot... But before I fully implement it, can you answer me a question, does BackgroundWorker_RunWorkerCompleted use the UI thread or a separate thread? –  Nov 24 '14 at 18:45
  • The event handler is run on the UI thread. – Jon Skeet Nov 24 '14 at 18:49
  • Hmm.. Alrighty! Well I guess it's not a very big deal if the progress bar freezes like 5 seconds before installation is complete O_o thanks man.. Post your comment as an answer, I will give you the green check mark thingy :) –  Nov 24 '14 at 18:55
  • @JonSkeet Put up your "answer" I will give you the green check mark... –  Nov 24 '14 at 18:56
  • Thank you both Jon Skeet and the_lotus... For your help! You helped me a lot. –  Nov 24 '14 at 19:21

1 Answers1

1

Currently, you're accessing the UI in the background worker, which you shouldn't - it's not running on the UI thread.

One overload of BackgroundWorker.RunWorkerAsync has an Object parameter, and this is the value that will available as DoWorkEventArgs.Argument - so that's the best way of communicating the "input" to the task. If you're just passing a single value (e.g. a string) you can do that directly; otherwise, create a class to wrap all the information in a single object.

Additionally, your last three lines access the UI too, so they should be in the handler for the BackgroundWorker.RunWorkerCompleted event, which will be executed on the UI thread.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I just put my install() createdirs() and cleanmods() code into the BackgroundWorker.RunWorkerCompleted it made my UI freeze for around 5 seconds.. But it's fine :) at least I can now make a progress bar work for the 90% of the time :D –  Nov 24 '14 at 19:24
  • @TruthSeeker: But that means those calls will execute on the UI thread - so if they take a long time, your UI will hang. The aim is to get UI code running on the UI thread, and nothing else. – Jon Skeet Nov 24 '14 at 19:25
  • The installation (moving files to the correct directory) barely consumes any time, and if the UI thread hangs for around 5 - 7 seconds is not a big deal for me at all. I mean, I wish I could prevent it, it would be AWESOME. But if I can't :P it's just not a critical problem. –  Nov 24 '14 at 19:31
  • You really should be able to - only the bits that access the UI should be in the UI thread. It really shouldn't cause any problem, unless those methods access the UI - in which case you should stop them from doing so :) Of course it's up to you whether you bother to fix this, but it's important to understand the principle, at least. – Jon Skeet Nov 24 '14 at 19:33
  • Well in this case how am I supposed to get a directory that is on the UI thread, to appear in my backgroundworker thread? Oh and the Dim appData as application.speicalenviornment.applicationdata ? –  Nov 24 '14 at 19:38
  • I tried doing the BackgroundWorker.RunWorkerAsync(DirectoryToInstall) but it still would still not delete the files it is supposed to. –  Nov 24 '14 at 19:41
  • @TruthSeeker: You pass the information using the argument to `RunWorkerAsync`, as I said. Then you need to use `e.Argument` to get the value out again, of course. `If you're already doing that, it's not clear what the issue is - you should debug to find out what file it's *actually* trying to delete. – Jon Skeet Nov 24 '14 at 19:47
  • I only tried doing this code: BackgroundWorker.RunWorkerAsync(DirectoryToInstall) when you press the "Install" button. I didn't try the e.Argument! Can you please guide me through :( ? It's my first time working with the background worker. A code example / sample would probably help me a lot. P.S here is how it looks like in my Vb.net code: http://pastebin.com/w3gJ61a5 –  Nov 24 '14 at 19:53
  • @TruthSeeker: Well there's no point in passing the information to the worker code if it's going to ignore it... the point is to remove the `Dim DirectoryToInstall As String = Label4.Text` code which accesses the UI... instead, you should use `Dim DirectoryToInstall As String = DirectCast(e.Argument, String)` or whatever the rather way of casting is in VB. – Jon Skeet Nov 24 '14 at 19:55
  • okay now my code in the button and BGW looks like this: http://pastebin.com/BrjDnPfN but this caused the problem to come back. I really can't think of why it's not working D: –  Nov 24 '14 at 20:01
  • @TruthSeeker: Your code isn't actually *using* `DirectoryToInstall` anywhere... shouldn't you be passing it to the methods to tell them what to do? My guess is that those methods are accessing the UI directly, which they shouldn't be... – Jon Skeet Nov 24 '14 at 20:03
  • Seems legit! And how do I do that? I don't think it will work if I just place Dim DirectoryToInstall As String = DirectCast(e.Argument, String) in all the methods that use the DirectoryToInstall in first place. –  Nov 24 '14 at 20:11
  • @TruthSeeker: No, so you should give each of the methods that needs the directory name a parameter, and then pass it in when you call it... just regular "passing information from one method to another". Either than, or create a *class* with a field of "the directory you want to install" which then uses that field for all its methods. – Jon Skeet Nov 24 '14 at 20:12
  • @TruthSeeker: Yes, although it's slightly unusual that your other methods don't need any arguments. Anyway, at this point we've gone rather too long in comments... – Jon Skeet Nov 24 '14 at 20:51
  • I don't think I am able to chat yet :o –  Nov 24 '14 at 21:01