|
1.
There are a couple of ways to correctly access the GUI from another thread; I'm strongly in favor of the one I call the "canonical form", as it works on all versions of C# and VB.NET
2.
yes your FormClosing needs to check your app's state and, when necessary, communicate with the user. How exactly is a design choice: "busy" and wait ( ); "busy" + cancel button; "busy" and ignore closing; simply ignore closing ( which makes it look like a bug).
my preference is to save files synchronously, without a thread; the one exception is when you can and often will start saving long before the user is likely to close the form. Anyway I as a user want to make sure things got saved before I drastically change the GUI state.
Rationale: saving what I entered/changed isn't taking as long as me entering/changing it, and I'm willing to spend that time to make sure and avoid the possibility I have to redo it.
|
|
|
|
|
Uhmm.. Ok I think I am going to go with a couple of you guys ideas put together. I think I might use a background worker that supports cancelling. If the bw is busy then I will ask the user if they want to continuing waiting until it is finished or stop the current process and close.
The reason I want to use it on a seperate thread is because it is a inventory screen. Often we must go in and enter updated information (which can be a lot) and save files. I don't really want saving a file to freeze the GUI when they could start saving the file and continue working on something else.
|
|
|
|
|
Things to keep in mind:
- cancelling a BGW (or any threaded action) requires cooperation; if there is only one big action (one SQL statement executing), then cancelling means waiting for completion.
- the "continue working on something else" may encounter a DB table that is being updated and possibly inconsistent.
Also I'm not convinced saving should take that long; if anything it tells me you are probably loading way too much data in your Form to be handy (yielding long Form load times as well then).
A paging system (such as the CP forums themselves) might be more appropriate.
|
|
|
|
|
the other thng you may want to do.. And it might be conceptually easier is to
thread[0] : run Main -> all your saving loading etc..
thread[1] : run Form
thread[2] : other(background worker from Form)
to the user this will look like the application "Quit" however it will continue running in the background until it completes it's task.
|
|
|
|
|
It sounds like your thread is accessing the child form - maybe the progress control? - after the form has closed and been disposed.
Are you sure you want to cancel the thread when the form closes? Wouldn't it be better to allow the thread to complete? If you do want to cancel, you will have to add cancel logic to your thread method. Don't use Thread.Abort() ! If you want to let the thread complete, you will have to make it check if the form still exists before trying to access it.
BTW, Thread.IsBackground just means the thread will be killed when the main form is closed instead of keeping the process running until it has completed.
Nick
----------------------------------
Be excellent to each other
|
|
|
|
|
I used Thread.IsBackground because I was thinking I wanted it to quit when that form was closed. It is a inventory application so if a user started saving a file that was 10MB and it was taking to long I was thinking I wanted to let them close it and I was trying to make it stop (not upload the file).
But I think I might go with the suggestion above and just refuse closing the form until it has completed. Just for my information if I wanted to do something like above, would it be easy to do? Stopping a cmd.ExecuteNonQuery(); that is uploading a file?
|
|
|
|
|
There is an IDbCommand.Cancel() method, but I've never used it.
Nick
----------------------------------
Be excellent to each other
|
|
|
|
|
I would do things a bit differently, in three ways...
1) Use a BackgroundWorker instead of a Thread (System.ComponentModel namespace). This can be set to raise ProgressChanged events on the GUI thread, so you don't have to deal with the invokes. It also has an IsBusy property that you can use when deciding whether to allow the user to close the form.
2a) Declare the worker at the form/window level, so you can kill it when the form is closing (Or not allow the close if it's running)
OR
2b) Create an IsBusy property on the form. Set it to true just before starting the thread/worker, and set it back to false when the worker completes (BackgroundWorkers have a RunWorkerCompleted event you can catch)
3) Instead of having the thread access form-level variables (Particularly if you use (2b) above, create a quick struct with the two strings and bool in it, and pass that as an argument to the thread/worker. It's preferable to have background threads be as self-contained as possible.
|
|
|
|
|
Ahh that is a great idea to.. using a struct.. so you are talking about this:
struct UploadFileInfo
{
public string TAG;
public string fileName;
public string safeFileName;
public bool fileMR;
}
private void saveFiles(object information)
{
UploadFileInfo fileInfo = (UploadFileInfo)information;
.............code.............
}
Thread fileThread = new Thread(new ParameterizedThreadStart(saveFiles));
fileThread.IsBackground = true;
fileThread.Start(upload);
|
|
|
|
|
|
Now that I think about it... the reason I am not using a background worker is because there are two different spots to save data. One is for normal attachments and the other is for M&R part of our inventory. I worked it into the same method to work both.
If I use a background worker I would hvae to create two seperate ones and create two seperate DoWork's. I think I am going to stick with the threading but not let the user close until their upload is complete.
Thanks again!
|
|
|
|
|
Jacob Dixon wrote: the reason I am not using a background worker is because there are two different spots to save data
there is a flaw in that logic. Just make a method (or even a separate class) that does it all (in any way you see fit, Thread, BGW, whatever), then call it from anywhere you like, and take precautions for it not to run twice at the same time.
|
|
|
|
|
In the form's Closing event handler I'd check on the thread and pop up a message that it can't close yet.
You could also have the thread disable the form's close buttons[^] until it completes, but that may be a bit heavy-handed.
|
|
|
|
|
I wanted to look up how the function TimeDiff is implemented exactly (a function to calculate how many hours there are between a start time and an end time) and found this. I'm not that experienced yet, so tell me... is there a way in which this might make sense or is this simply redundant and... well, stupid?
public static XDateTime TimeDiff(XDateTime d1, XDateTime d2)
{
#region Local Variables
#endregion
#region Actions
return APTimeDiff(d1, d2);
#endregion
}
APTimeDiff returns the same type and does the actual calculation (and formatting). Why put it into another function?
|
|
|
|
|
Well, its just a guess but one reason for doing this might be to avoid changing the public API of the class. So, once upon a time method TimeDiff did something different. Then it was decided to use APTimeDiff instead (maybe it's more efficient, maybe it handles some corner condition more effectively). But it might be that this is a library class which is used in a number of places, and for whatever reason we can't go through and change all the calls to TimeDiff to call APTimeDiff instead. So, just leave the old method in place but change the internal implementation. Any new code should now be using APTimeDiff, but old code that uses TimeDiff will continue to function as is and does not need changed.
It's a fairly common thing to do as part of a Refactoring process. Eventually, the aim would be to work through and replace all calls to TimeDiff with APTimeDiff.
|
|
|
|
|
It looks like half completed code, or post-refactored code which has not been cleaned up.
In the former case, the completion of that method would perhaps put some validation in to check the inputs.
In the latter case, the method may have been left as there are many places around the application calling TimeDiff and changing them all to APTimeDiff may be too time consuming at the current time.
|
|
|
|
|
Thanks guys, that makes sense. It is source code that has been passed on for many versions and many years, so it may very well be a change that has been made without cleaning the older parts up properly.
|
|
|
|
|
Is there something that I'm not understanding properly here? I've written a DLL that our customers can use to connect to our server and exchange certain info. On the one hand it makes it easier for them, not having to code a bunch of comms stuff and on the other hand it hides our exact protocol from them (or so I hoped).
When I use the DLL in my own test application and I set breakpoints in my code I can step right into the functions in the DLL. Surely it should be possible to avoid this? Any help please?
|
|
|
|
|
You can step into the DLL in your test application because it is debug compiled. You will provide a release compiled DLL to your customer. If you want to "hide" your code even more you can run it through an obfuscator, for instance dotfuscator.
|
|
|
|
|
Thanks for the reply. I will have a look at Dotfuscator.
Just to be clear, my DLL was indeed compiled with RELEASE configuration, not DEBUG. Now I'm not even sure what the difference is anymore as I always thought this was one of the key differences between the two configurations.
|
|
|
|
|
You can never entirely avoid leaking the code though. You can not depend on your code being secret. The CPU can still run the code, and everything the CPU does to be able to read the code is something the user could do as well. There is no solution.
A protocol is even harder to hide since they could capture their own internet traffic and analyse it.
But you can make it harder for your customer to read your code by using an obfuscator as indicated by Calla
|
|
|
|
|
It makes sense, thanks. I can understand that you can never prevent someone from decompiling your code but I would like for it to be a bit harder than this! It looks like Dotfuscator might do the job, I'll look into it, thanks.
|
|
|
|
|
Dewald wrote: Surely it should be possible to avoid this?
If it was possible to avoid, then there coudn't be any cracs and Keygens. The fact that reverse enginering is possible, it can be obscured, not prevented. Was that dll compiled with debug info? Is the Dll project in your solution with app project? If both question is yes, then you can easly debug dll and see it's source
you could compress the dll with UPX or other packer.
also look at this article: Strong Names Explained[^]
|
|
|
|
|
Thanks for the response. No, the DLL was not compiled in DEBUG configuration and no, the DLL project is not part of my app sollution. I have two sollutions, one for the DLL and one for my test app. The DLL sollution is compiled in RELEASE configuration and I then manually copied the .dll file into my test app sollution folder and added it as a reference to the project.
When I set a breakpoint on the line where I call a function in the DLL and I step into the line, it steps right into my DLL exposing all of the code.
Thanks for the article, I'll have a look at it and also have a look at UPX.
|
|
|
|
|
Then maybe you copied the PDB file as well? If no, I don't know how you could debug it.. (maybe VS tried to help you along the way in a very MS behind the scences kind of way).
To be sure how your DLL could be (mis)used by the customer I suggest you copy only the DLL file in a RELEASE compiled version to for instance a virtual machine with VS installed and then try and see what happens. From what I've experienced it is not possible to debug the DLL if you don't have any additional files (such as PDB) and you should be safe when it comes to stepping into your code. Then again, totally hiding the source is hard because of reverse engineering tools. Good luck!
|
|
|
|