Find an installed app with the Win32 Registry
From QtCentreWiki
There are some cases, when building a project, where you might need to find the location of an installed application or package in order to add it to your LIBPATH or INCLUDEPATH. This is especially true if you need to use Platform SDK headers or the DDK.
This can pose a very real challenge under Win32, which doesn't generally have a nice command-line-driven "find" feature. At least on NT platforms however, with a little bit of (ugly) QMake code, one can use the REG command to look up a package's location.
Most packages install the path they were installed to (in some form) somewhere in the HKEY_LOCAL_MACHINE\Software hierarchy in the registry. You'll have to hunt around in there to try and find the particular package that you're after, though. I'm going to use the SDK and the DDK as my examples, especially because the DDK poses a special challenge. In my build system, I've created these two snippets as "mssdk.pri" and "msddk.pri" so that I can include() them in whatever QMake project I need them in.
Finding the SDK
# Find the SDK if it's not in an environment var
SDKPATH = $$(SDKPATH)
!isEmpty(SDKPATH) {
SDKPATH = \$(SDKPATH)
!exists($${SDKPATH}):SDKPATH =
}
isEmpty(SDKPATH) {
SYSTEM_CMD = reg query HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
MicrosoftSDK\Directories /v "Install Dir"
SYSTEM_RET = $$system($${SYSTEM_CMD})
FORLOOP = 8 9 10 11 12 13 14 15 16
for(i, FORLOOP):SDKPATH += $$member(SYSTEM_RET, $${i})
}
isEmpty(SDKPATH):error("SDKPATH not set correctly, and can't find in registry.")
INCLUDEPATH *= "$${SDKPATH}/include"
(Note that lines 8 and 9 should be combined — they've been split for the sake of screen display.)
Line 2 checks the environment to see if the user has been nice and set the path in an environment variable. It sets the QMake variable SDKPATH if it has been set on line 3. Line 4 makes sure that the path the environment variable specifies really exists (in case the user is lying to us!) and clears the variable back out if it doesn't.
We can't count on the environment variable, though, so in the block starting from line 7 we try and find it in the Registry.
Line 8 (and 9) sets up the correct parameters to REG in order to fetch the installation path. Line 10 actually executes the command, and assigns the text that REG prints to the variable SYSTEM_RET, which will look something like this:
! REG.EXE VERSION 3.0
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MicrosoftSDK\Directories
Install Dir REG_SZ C:\Program Files\Microsoft SDK
We only care about the very last part. If we count the "words" (i.e., items separated by spaces) before this, we'll see that the path begins at word 8 (starting at 0). But the path itself can contain spaces, and thus be more than one entry from $member()! So lines 11 and 12 are a loop to add all "words" from 8 on to the SDKPATH variable.
Because the SDK path can contain spaces, we need to put the whole include path in quotes when we add it to INCLUDEPATH on line 15.
Finding the DDK
The Microsoft DDK poses a special problem because the user could have more than one DDK installed simultaneously. How can we figure out which one to use?
# Find the newest DDK containing headers/libs for $${DDK_TARGET}
# if it's not in an environment var
DDKPATH = $$(DDKPATH)
!isEmpty(DDKPATH) {
DDKPATH = \$(DDKPATH)
!exists($${DDKPATH}):DDKPATH =
}
isEmpty(DDKPATH) {
DDK_TARGET = w2k
SYSTEM_CMD = reg query HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WINDDK
SYSTEM_RET = $$system($${SYSTEM_CMD})
FORLOOP = 5 6 7 8 9 10 11 12
for(i, FORLOOP) {
REG_CURENTRY = $$member(SYSTEM_RET, $${i})
!isEmpty(REG_CURENTRY) {
SYSTEM_CMD2 = reg query $${REG_CURENTRY} /v "LFNDirectory"
SYSTEM_RET2 = $$system($${SYSTEM_CMD2})
FORLOOP2 = 7 8 9 10 11 12 13 14 15
CURDDKPATH =
for(j, FORLOOP2):CURDDKPATH += $$member(SYSTEM_RET2, $${j})
exists($${CURDDKPATH}/inc/$${DDK_TARGET}):DDKPATH = $${CURDDKPATH}
}
}
}
isEmpty(DDKPATH):error("DDKPATH not set correctly, and can't find in registry.")
The basis of this code is the same, but there are some important differences. On line 9, we specify the OS of DDK that we need for this project (based on the name of the subdirectory in the DDK install). The first REG command on line 10 queries the Registry for registry subkeys for each DDK; on my system, the return of that command looks like this:
! REG.EXE VERSION 3.0 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WINDDK HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WINDDK\2600.1106 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WINDDK\3790
What we need begins at "word" 5 (again, starting at 0). Lines 12 through 14 set up a for() loop to query each registry key starting from word 5. (Note that this depends on being able to assume that this Registry key does not contain spaces!)
At line 16, each of those Registry keys is queried for the value named "LFNDirectory", which contains its filesystem path. That command's return looks like this:
! REG.EXE VERSION 3.0
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WINDDK\2600.1106
LFNDirectory REG_SZ C:\WINDDK\2600.1106
We want the item starting at word 7. Again, the path could contain spaces (though it doesn't here), so lines 18 through 20 store the return data starting from this point into CURDDKPATH. Line 21 checks to see if that path contains a directory that matches our target DDK ("w2k", in this case) and if so sets DDKPATH for us.
I hope these examples make it easier for you to add platform-specific packages to your QMake projects!

