§ ¶File extension madness, Windows Vista / 7 style
Last time I wrote about the old way of associating file types and file extensions with a program. I've since tried Default Programs, the new way of handling file associations starting with Windows Vista. You still have to register the file types in HKEY_CLASSES_ROOT in order to set the defaults (or more specifically HKLM\Software\Classes or HKCU\Software\Classes as required), but afterward you use the Default Programs mechanism to select the user mapping. This has the following advantages:
- You get a UI for free to do this: Use CoCreateInstance() and invoke IApplicationAssociationRegistrationUI::LaunchAdvancedAssociationUI(). Not bad.
- Adding default programs only requires writing to the registry. This means that you can dump an entire folder of portable apps into a directory and then apply one registry patch to set up Default Programs entries for them, even if the programs don't support it themselves.
- There is a programmatic interface to apply defaults per-extension, and better yet, an easy way to query if the app is the current default. Doing this check the old way can actually be a bit hairy.
And... that's about it. Now for the bad parts:
- The Default Programs mechanism sets user-local defaults only, but for some reason, it requires you to set up your application mapping in HKEY_LOCAL_MACHINE. This means that any programs that install per-user in order to allow installation without requiring administrator access are SOL.
- There is no way in the UI or API to remove an association. This means that if a program offers an association for the generic .bin extension and you click it, you're stuck with that association and all the default APIs and UIs allow you to do is periodically select another program to accidentally open binary files with.
- There is an API to remove user associations for all file types (IApplicationAssociationRegistration::ClearUserAssocations). As far as I can tell, the purpose of this is to nominate your program to appear on The Daily WTF, since for any other scenario it would be like swatting a fly with a nuclear bomb.
- You can only associate one predefined programmatic identifier (ProgID) with each extension. You can't, for instance, have an option to switch ProgIDs to choose whether a file type opens in a new window or not or to multiplex one extension across multiple file types.
- You must statically predefine the set of extensions for your program in the Registry. This is inadequate if your program accepts different file types based on dynamically loaded plugins or filters. I don't even know how this would work for programs that use DirectShow for playback, which has its own extension registry.
- MSDN tells you how to get an instance of IApplicationAssociationRegistration via SHCreateAssociationRegistration(), but it doesn't seem to tell you how to get an object that implements IApplicationAssociationRegistrationUI. CoCreateInstance() with CLSID_ApplicationAssociationRegistrationUI works.
- If there is no program currently associated with an extension, there is no way for the user to opt out of associating it in the UI: the checkboxes are grayed out enabled and the user is forced to associate your program with it. This is not great if some of the extensions are rarely used and not recommended for general use.
- Guidance and support for uninstallation is lacking. The documentation for File Types recommends that you should delete the programmatic identifiers (ProgIDs) but leave the extension mappings to the ProgIDs in place, which is the best you can do anyway with Explorer locking down the UserChoice keys. Explorer does indeed handle this and display the Open With dialog if the file is launched. What the docs don't tell you is that Explorer displays the missing ProgID as the file type name, so the file that used to appear as "HLSL Shader File" now shows up as "FooEditor.hlslfx.5", which just makes it look like your program didn't uninstall properly.
As such, I'd say the new Default Programs system is not an improvement over the old way of doing file type associations. Sadly, the way things are, there isn't much choice unless you're willing to start hacking the Explorer registry keys, which I couldn't recommend as a good choice.
(Read more....)§ ¶File extension madness
Ever hit the problem in Windows where you tell a program you want it to handle some file types, and only some of them "take"?
I've got a pretty good idea now why this happens, and it's a mess.
The MSDN Library has a section on how to associate file types in code. There are some omissions -- I knew what the positive and negative numbers meant on icon references, although that doesn't actually seem to be documented -- but in all it's actually not bad. If you follow the documentation you can indeed add a UI to your program to associate file types, and if you spend another hour in the Vista UAC section you can even get your program to properly relaunch itself as elevated as needed. Then you send it out to your users, and you find out it inexplicably doesn't work on some users' systems.
Well, long story short, the culprit turns out to be yet another overlay on the file type mechanism located at HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts. This is a bit odd, because there is already a user-local overlay over the main class database at HKCU\Software\Classes. Within the key for each extension here there can be a subkey called UserChoice, which is created when the user specifically overrides the current association with the Open With... option and has the "always use this type" checked. This then overrides the ProgID selected through in HKCR. Frustratingly, I wasn't able to find any documentation on this in MSDN, but it wasn't until I tried to delete the subkey for testing in Registry Editor and got an Access Denied error that I had a clue why it wasn't documented. Let's take a look at the security settings:
A Deny entry for the current user, for only Special permissions? That's strange. What exactly are they?
Mmmhmm, the shell explicitly set a Deny entry for the Set Value permission on the key. That makes this Microsoft's latest salvo in the War Against the Coolest Most Amazing Program Ever Written, especially since bypassing this requires mucking around in security APIs that are mazes of SIDs and ACEs. While I appreciate the intent -- having had to manually clean up the mess from programs that thought it funny to claim .ZIP and .PDB as media types -- I'm not sure that the resultant game of Open With whack-a-mole that results is any better, where first I have to manually switch AVI, then MPG, then MP4, and every other extension still bound to the ugly-as-sin Windows Media Player. It would be nice if the file type documentation actually mentioned this user override too, even if it didn't include storage details, because this appears to make the documented HKCR method useless for any sort of custom association UI.
Digging around the MSDN links a bit reveals an IApplicationAssociationRegistration interface added with Vista which might work, but I haven't tried that yet. Annoyingly, it requires you to register your application in HKLM, which seems backward given the moves toward storing file associations per-user. That it offers a programmatic interface to override associations also doesn't make me hopeful since that also goes against the bunker Microsoft's been trying to build around the file type settings.
(Read more....)§ ¶Screen recording with WDDM 1.2
While browsing through some of the changes to the Windows 8 Developer Preview, I happened upon the list of improvements in DXGI 1.2. I have to say, the list looks pretty good. After fixing the most glaring problems with the transition to WDDM and DWM -- memory usage and unaccelerated GDI rendering -- Microsoft has started evolving DXGI further, and there's some good stuff here.
Especially desktop duplication.
Formerly, the options for doing screen recording in Windows weren't that great. These were the options I knew of:
- Call GetDC(NULL) to get a screen HDC and BitBlt() back to system memory. Simple, easy, and reliable. Unfortunately, also slooooooooooowww, as you couldn't get more than a postage stamp at decent frame rates. It also caused the whole system to chug quite a bit. This method improved substantially in performance starting with Windows Vista but still could not handle full screen + full frame rate.
- Call Direct3D's GetFrontBufferData() function. Or not, since it uses both an unaccelerated readback and an unoptimized blit routine to give abysmal performance. The docs warn that the function is slow, but they're an understatement, trust me.
- Use a mirror display driver to intercept drawing commands. This allowed for very fast, low bandwidth capture as the drawing primitive stream could be captured and was a popular method for remoting applications. The main downsides were the need to write a kernel driver, inability to handle 3D applications, and disabling desktop composition starting with Vista. I believe this method is now completely dead with Windows 8 now that XPDM support is gone.
- Use a OpenGL front buffer hack to read the screen. This is the fast path I have in VirtualDub and is very fast, since it allows the screen to be preprocessed on the GPU before the slow read back. The disadvantages are that it's unreliable on some OpenGL drivers and no longer works at all with WDDM due to front buffer redirection.
- Hook Direct3D and intercept Present() to do the capture. Not a general solution, but allows for full hardware acceleration of capture and is a popular solution for capturing full screen games. I believe this is also possible with DWM to grab the desktop, although I haven't heard if someone's tried it and it involves evil system process injection hacking in that case.
To sum it up, there were no good global solutions on Windows XP, and only one decent one on Windows Vista and up. Well, desktop duplication in WDDM 1.2 finally has a good option. The process for capturing the desktop with the new APIs is surprisingly simple:
(Read more....)§ ¶Recompiling code for Windows 8 ARM
I've been a bit busy on non-coding stuff as of late, but I did manage to get some time to install the Windows 8 Developer Preview natively. Previously I had been running it on a VirtualBox VM, which worked but only supported non-accelerated video and was prone to heavy freezing for minutes at a time. After a few tries at installing before I figured out that the BIOS had to be forced to boot the DVD in UEFI mode to install on a GPT-formatted disk -- strangely, a problem I didn't have with Windows 7 x64 -- I got it installed and happily it runs much more smoothly on real hardware.
The last time I tried the Developer Preview, I compiled some small snippets with the Visual Studio 11 Express preview that was included. Loading a full Win32 project revealed a big problem: the VS11 Express preview doesn't have support for loading non-Metro C++ projects. I ended up installing the full Visual Studio 11 Developer Preview to fix that. After a few hours of download and installation -- and a bit of Disgaea 4 levelling in the meantime -- I loaded the solution I had previously converted to VS2010, upgraded it to the v110 toolset, and started an ARM build.
ARM build?
Truth be told, I don't know if I'll bother actually compiling anything for Windows 8 ARM. Between the current signed-only requirement for Metro apps, the uncertainly around whether Windows ARM will support desktop apps and whether WinRT can be used from desktop apps, what APIs will be able for ARM, and simply whether any of the Windows ARM devices will be worth buying it's not clear whether it'll be a hospitable environment for what I do. There's also the issue of actually having a device to test on, since code you can't run is virtually guaranteed broken. However, building for ARM has another beneficial side effect, which is to flush out unguarded x86-isms in a code base, and being able to do so with Visual C++ is much easier than doing so with another compiler or on another platform. I've done a decent amount of x64 and also some PPC, so I did have a good idea of what to expect going in. For the most part it went smoothly, but some problems I ran into:
- Installing VC11 DP over VC11 Express did something bad to Makefile project support: the IDE couldn't find nmake.exe even though it was installed. The VC11 DP installation I have on Win7 doesn't have this problem. Oh well, unload the projects and keep going.
- I had a bunch of code in the system library that assumed that !AMD64 => X86. Oops. Time to fix up a bunch of #ifdefs.
- Ran into a weird problem where the project system kept assuming that the build location was at the platform default location (solutiondir/platform/configuration) even though I had overridden it in the project settings in the IDE and the macro expansion for $(OutDir) in the UI was correct. The true nature of the problem didn't reveal itself until I jacked up the MSBuild verbosity and saw the old value showing through. It eventually resolved itself after some voodoo involving restarts and multiple builds.
- The Windows 8 Developer Preview comes with a link library for d3dx9_44.dll but doesn't actually include that DLL, and that version doesn't seem to be released yet -- the latest download on the DXSDK site has v43. I copied d3dx9_43.dll as d3dx9_44.dll into the build directory to get the build tool working. No, this is not a recommended fix.
- dxguid.lib was missing. That's fine, I expected to have to jettison the old DirectX lib usage at some point anyway and it can be worked around later.
The showstopper was when I got link errors on comctl32.lib, and then after fixing that shlwapi.lib. Game over, missing essential libs for a desktop app. Oh well. At least it only took a few minutes to get the main codebase building, since I had already done a lot of the gruntwork for AMD64/x64. What this tells me is that if you've done reasonable diligence to keep x86/x64isms low in your code, that rebuilding it for ARM with VC11 is going to be pretty painless. Availablility of APIs could potentially be a lot more painful but we won't know how much for sure until the official API list for Windows 8 ARM is released.
(Read more....)