So for the last few days I've been going through the final steps of updating Html Help Builder to properly run under Windows Vista without any file permissions, registry or other sort of elevation requirements. In theory the application should run fine after installation without any sort of crutch like Folder Virtualization.
Some may snicker and say - "What took you so long; Windows guidelines have been saying that for years". You know don't store data in Program Files, use a Documents folder for default data locations and AppData for 'application specific' data. And you'd be right <g>...
But then again Html Help Builder is a pretty old application that predates the strict Windows guidelines (I think those came about with Win2000 maybe NT 4.0) which really didn't get enforced seriously until XP (via policy) and now by default in Vista. So it took me a while to be concerned about the issues. Underlying it all though is the fact that I really dislike the 'Scatter files all over the place mentality that ensues if you really follow the Windows guidelines: A folder for the user in Documents, 'Application configuration data' into a seperate AppData folder, Shared data yet somewhere else. Before you know it you have files scattered in 5 different locations. Messy if the application ever has to move...
But anyway it needed to be done and the process took much longer than I had planned. Html Help Builder is pretty well organized internally and pushing things into new directories is not really a big issue. But it turned out there were lots of little issues that I hadn't planned on at all. For example:
- Local file generation for Html Previews
- Local file generation for Compiled Scripts (if no project is yet open)
- The Html Help Builder Auto-Update functionality
- The 'global' configuration file for Html Help Builder
Testing this stuff and trying to isolate file 'violations' can be tricky under Vista, because Vista - with UAC on - automatically has Folder Virtualization enabled. Folder Virtualization attempts to detect a non allowed file operation like a write operation in the install directory and automatically copies the file to a virtual folder (in your Documents/AppData store) and updates the file there.
Folder virtualization sounds like a nice workaround, but it has two problems: It seems that it really slows file operations down noticably and it can cause big problems if more than one user needs to use an application and share the data in in the install directory (I'll come back to this in a minute in regards to file updates).
So while testing your app for restricted rights you probably want to do one of two things to check for invalid file access:
- Turn Folder Virtualization off
- Leave Folder Virtualization on and check the virtualization store to see what got updated
While checking this stuff out you can turn Folder Virtualization off by typing Local Security Policy into the start menu and choose Local Policies, Security Options, then User Account Control: Virtualize File And Registry...:
Make sure to log off and log back on which is required before security policy changes are applied.
This allows you to run your app and have it blow up (or whatever it will do) whenever it tries to write local data. While checking out Html Help Builder I ran into a number of the small issues mentioned above, like writing configuration information and creating on the fly compiled code for a couple of odd man operations (usually this stuff happens in actual project locations which are in the Documents folder, but if no project is open <s>).
The other option is go ahead and leave Folder Virtualization enabled and go ahead and run your application. Test your application thouroughly or simply run it as you normally would for a few days. Then check out the virtual store and see what actually got updated there. The virtual store lives in your Users\<yourAccount> directory in AppData/VirtualStore/Program Files/<yourAppName>:
Unlike the picture above if you've cleaned up your app properly there shouldn't actually be a virtualization folder at all! If you do see files in this location it means there are still some parts of your application writing locally where they shouldn't!
Some things just won't work with Low Rights
Try as you might there might be some things that simply won't work in the low rights scenario. For example, in Html Help Builder I have an Auto-Update mode that downloads the latest version to a local location, then shuts down Html Help Builder, updates the downloaded files and then restarts Html Help Builder. There's no good way that I could think of to make this process work without requiring administrative rights - and maybe that's not such a bad thing because ultimately this is very viral behavior if it weren't for the prompts that occur and ask you whether you actually want to perform the update <s>...
So how do I get around this issue? Not very gracefully - I let the user know and prompt with a detailed error message.
Detailed enough for 'ya? This is a nuisance for sure but I think it's vital that the user is given enough information to proceed at this point so this dialog pops up explaining what options are available.
The code checks whether the user is an Administrator with the IsUserAnAdmin Win32 API call which correctly identifies the user's 'real' Administrator status in Vista. So even with Admin Approval mode enabled and running as a 'pseudo admin' user this function will return false and cause this dialog to be raised.
In case you're wondering - no that's not a standard Vista dialog but a custom dialog window I have in my library that uses a Web Browser control to generically display longer messages to the user along with the ability to add arbitrary buttons to the dialog. <s> It comes in very handy for displaying longer messages that would be overkill for a message box - the additional formatting that even simple HTML provides really helps (hey this is one thing nice in WPF - that sort of thing is native!).
COM and .NET Com Interop Registration
If the user chooses to update now and run As Administrator the update process works correctly by downloading the update into a temporary folder then launching the updater which inherits the Administrative Rights because it's a child process, and then then updates the original application and restarts.
There are a few other things that get similar treatment in Html Help Builder. For example, you can choose to re-register various components by re-registering the Html Help Builder COM component or the .NET Interop assembly used for importing .NET assemblies. Another scenario is where the user doesn't have the .NET runtimes installed at time of installation so COM Interop registration has no effect (no RegAsm available at install time) then later installs .NET (or a different version of .NET). Although .NET is now installed Html Help Builder does not have the COM Interop assembly registered. Internally Html Help Builder checks for registration and tries to register if it's not already registered. This also no longer works in Vista with UAC because COM registration requires Admin rights.
The same approach with the dialog is taken for this now, although it's not as flexible as before - previously you could just use a feature and Html Help Builder would check if installed and if not do it right then and there. Now an explicit step is required to make this happen.
So from an application perspective a lot of little changes like this were required, and while it all feels a lot more restrictive providing a less friendly user experience at the same time I also realize that these are not make or break issues and for the sake of security it's probably Ok to have to give them up.
I'll be at DevIntersection in Vegas this fall giving sessions on ASP.NET Core with Angular and Localization. Thinking of coming? Use discount code STRAHL and save a few bucks. If you do be sure to stop by and say hello!
Other Posts you might also like