As if I had time to waste I spend a day wrestling with C++ to create something that should have been pretty damn simple: A file copy utility with some minimal progress UI to be used as part of an application updater. I needed a fully self contained file swapper that can be launched from a running application to run an extraction utility (or any command line) and then restart the original application.
I’d been using a VB Script file for this in the past in a number of my applications, but with the recent security restrictions on machine most Anti-Virus software wouldn’t even let the script run or at the very least pop up a nasty message.
This updater has been for a couple of my VFP application and it’s based on an article I wrote a long while back that uses a self-contained class to handle checking for update files, downloading them and finally swapping files and updating the application.
The main requirement for this update utility is that it should be fully self-contained – no dependencies on any runtimes (in case the runtimes themselves need to be swapped) or anything else. The file also needs to be short. It should also have some minimal UI – letting the user know what’s happening by simply displaying messages as files are unpacked and updated.
What’s a good choice for this? I actually had put this off for a long while because of a choice of tool to do this rather simple job.
.NET is not an option – not enough deployment penetration and dependence on a specific runtime. A Fox EXE is also not a great option even though the applications that are to be updated are Fox apps because of runtime requirements. If the runtime changes the EXE would have to be recompiled and you couldn’t as part of the application update the runtimes.
So this leaves native code. Delphi was something I thought about, but I couldn’t find my ancient copy of Delphi 4 … in retrospect it probably would have been a hell of lot easier in Delphi to do this. But I thought how hard could it be to do this with C++?
Well, I found out yesterday. Writing the actual copy and update and restart code was trivial enough, but the UI stuff is nothing short of a nightmare. Neil Tonkin actually pointed me at some raw C code to use the WinAPI to pop up a Dialog, but I got quickly lost in the details of trying to communicate with the dialog. After a while of trying to find more details of that I gave up and decided, heck, I’ll just use MFC. V7 of the MFC runtime would be installed with my apps anyway so this should be no problem from a dist POV.
Now remember I know enough C++ to be dangerous and I can do fine with API style programming, but I’ve never done any C++ based UI programming at least not with MS tools.
This was an eye-opening exercise and as I mentioned it took me a better part of the day to create a dialog that doesn’t really work like a dialog (ie. it starts does its thing and shuts down). I created an MFC dialog and 'simply' eliminated some of the dialog functionality (buttons) just starting the operation and shutting down the dialog when through. Finding the right messages to overload was a real pain or even understanding the order of events and what they actually accomplish. Eventually I was able to hook my routine to the Activate event and explicitly forced the form to display itself. Activate doesn't actually have a window active though so it took a while to realize that I have to manually show the window first. Grr... nothing's automatic in MFC apparently.
But that was only the beginning of my problems. When I finally hooked up this EXE to my application which spawned the process (in this case from Visual FoxPro) using the venerable RUN /N command, the damn app would not run in the background, but instead run modally. The updater requires that the new app is spawned and the original app then shuts down. But the RUN command (using WinExec beneath the covers I think) simply sat there waiting for the spawned EXE to finish. You can probably understand my panic at this point - I just wasted several hours to create this thing and then it doesn't work in the context of my grand plan!
It turns out that the problem was the Sleep() calls I used in the spawned EXE to wait for the shut down of the application. RUN apparently looks for some activity in the spawned applications before continuing on, picking up Windows Messages from the child process. Sleep() prevents this apparently. If I put up a message box, everything worked fine, but Sleep() didn’t. You can imagine that this was not a problem that you find in a few minutes …
Next I had to figure out a way to wait without Sleep(). MFC doesn’t have anything like a DoEvents() so I had to fake it with lovely code like this:
void CCodeUpdateExeDlg::DoEvents()
{
MSG oMSG;
while(::PeekMessage(&oMSG, NULL, 0, 0, PM_NOREMOVE))
{
if(::GetMessage(&oMSG, NULL, 0, 0))
{
::TranslateMessage(&oMSG);
::DispatchMessage(&oMSG);
}
else
{
break;
}
Sleep(100); // Try to minimize CPU usage
}
}
void CCodeUpdateExeDlg::SleepLocal(int Millisecs)
{
long Ticks = GetTickCount();
while( GetTickCount() < Ticks + Millisecs)
this->DoEvents();
}
Talk about ugly and this code (besides the Sleep inside of the loop) really works the processor to close to a 100%. Not quite sure why. But using SleepLocal() at least got the whole thing to work.
It’s embarrassing how much time it took to write this little thing, and even more embarrassing how disorganized my coded ended up being (totally linear inside of a single handler method). But after I finally got it to work I was too frustrated to think about refactoring.
It's a humbling experience to write code in C++ - it always is to me. I've done my share of coding in C++ but I never felt like I was in control. I can't tell you how glad I am about writing code in C# or VFP instead of C++. Life’s too short to code in C++ to waste on such low level stuff, but sometiimes it solves a problem that simply can't be solved in more highlevel environments.
Maybe it's worth this learning experience, because there are a few other utility routines that could also use some minor display UI. Now at least I have a template to work with. Or maybe I'll dig up that copy of Delphi after all. <g>