Suppose I'm writing a multi-purpose dll which includes a function for getting the OS version:
void get_os_version(DWORD *major, DWORD *minor)
{
OSVERSIONINFOEX osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOsVersionInfoSize = sizeof(OSVERSIONINFOEX);
// deprecated but easier to use for this example's sake
GetVersionEx((OSVERSIONINFO*)&osvi);
*major = osvi.dwMajorVersion;
*minor = osvi.dwMinorVersion;
}
For the Windows version to be retrieved correctly for versions higher than Windows 8, it is required to embed a manifest that specifies the supported platforms (see details here).
So I disable automatic generation of a manifest for my dll file using the /MANIFEST:NO
flag when compiling, and instead add the following manifest:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
</application>
</compatibility>
</assembly>
, using the mt tool:
mt -manifest GetOsVersion.dll.manifest -outputresource:GetOsVersion.dll;#2
All good and no errors. Now to use the dll, I create a simple App.exe which loads the dll and calls its function:
int _tmain(int argc, _TCHAR* argv[])
{
DWORD major, minor;
get_os_version(&major, &minor);
printf("%d.%d\n", major, minor);
return 0;
}
But when running App.exe on Windows 10, surprise surprise, the output is:
6.2
, which is the version for Windows 8. If I apply the manifest to the App.exe too:
mt -manifest GetOsVersion.dll.manifest -outputresource:App.exe;#1
, the output is the expected one:
10.0
Why is this happening? And can I solve this problem without adding a manifest to the executable?
I don't have control over the applications that will use my library, but I still want to correctly retrieve the OS version.
An alternative method for determining the actual operating system version is documented on the MSDN page "Getting the System Version":
This will work from a DLL regardless of whether the application has a manifest.
(Of course, there is a reason that GetVersionInfo and friends don't return the actual operating system version: programmers have a nasty tendency to misuse this information. You should seriously consider whether or not providing such a function in your DLL is really a good idea.)
Most of the nodes in a manifest applies to the whole process and is only read from the main .exe module:
You should use
GetProcAddress
andCoCreateInstance
to check if the feature you need is present, not the Windows version.With a little work,
GetProcAddress
can also be used to figure out what version you are on if you really need that information. Look at the minimum OS version of various kernel32 and user32 functions on MSDN...Complementing the accepted answer, here is some starter code for anyone else who wants to implement it:
And the output on Windows 10 is:
App (executable) manifest MSDN page describes this matter rather explicitly:
Compatibility (and other application settings, such as DPI awareness) are completely ignored if manifest is not an executable manifest. This makes sense because otherwise there would be possible manifest conflicts occurring between manifests in different dlls.