Reputation:
I'm working on a Win32 application and am currently trying to implement multi-display features. For this, I want to use EnumDisplayMonitors
, a feature that is only present in Windows 2000-upwards. I've done my best to maintain backwards compatibility up to Windows 95 thus far.
I want to, therefore, ignore this related section of code when the program runs on older versions of Windows. However, its mere presence anywhere in the code, even if it's not executed, crashes the program. I want to use #ifdef
to ignore this section of code, however I can't seem to figure out a way to get the current Windows version from the Preprocessor. I've seen suggestions to use WINVER
or _WIN32_WINNT
but both need to be set by me as far as I can understand, defeating the whole point. Any attempts to use them predictably did not work as intended.
Is there a way to get the current version of Windows from the preprocessor? If not, is there another way to disable this function completely depending on the OS? Again, regardless whether it's used, its mere presence crashes on Windows 95.
Upvotes: 1
Views: 487
Reputation: 598011
The correct way to handle this is not with the preprocessor at all. You need to use the Win32 API GetProcAddress()
function at runtime instead, eg:
typedef BOOL (WINAPI *LPFN_EDM)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
LPFN_EDM lpEnumDisplayMonitors = (LPFN_EDM) GetProcAddress(GetModuleHandle("user32.dll"), "EnumDisplayMonitors");
if (lpEnumDisplayMonitors)
{
// use lpEnumDisplayMonitors as needed...
}
else
{
// use something else...
}
This is even described in Microsoft's documentation:
Identifying the current operating system is usually not the best way to determine whether a particular operating system feature is present. This is because the operating system may have had new features added in a redistributable DLL. Rather than using the Version API Helper functions to determine the operating system platform or version number, test for the presence of the feature itself.
To determine the best way to test for a feature, refer to the documentation for the feature of interest. The following list discusses some common techniques for feature detection:
You can test for the presence of the functions associated with a feature. To test for the presence of a function in a system DLL, call the
LoadLibrary
function to load the DLL. Then call theGetProcAddress
function to determine whether the function of interest is present in the DLL. Use the pointer returned byGetProcAddress
to call the function. Note that even if the function is present, it may be a stub that just returns an error code such asERROR_CALL_NOT_IMPLEMENTED
.You can determine the presence of some features by using the
GetSystemMetrics
function. For example, you can detect multiple display monitors by callingGetSystemMetrics(SM_CMONITORS)
. There are several versions of the redistributable DLLs that implement shell and common control features. For information about determining which versions are present on the system your application is running on, see the topic Shell and Common Controls Versions.
If your compiler toolchain supports Delay Loading, then you can use that instead of calling GetProcAddress()
manually. Set your project to delay-load user32.dll
and then call EnumDisplayMonitors()
normally after first validating the OS supports what you need, such as checking if Windows is Win2K or later via GetVersionEx()
, or checking if multiple monitors are installed, eg:
OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionEx(&osvi);
if (osvi.dwMajorVersion >= 5) // Win2K = 5.0
{
// use EnumDisplayMonitors as needed...
}
else
{
// use something else...
}
if (GetSystemMetrics(SM_CMONITORS) > 1)
{
// use EnumDisplayMonitors as needed...
}
else
{
// use something else...
}
Upvotes: 7
Reputation: 26196
Compile targeting the lowest Windows you support with WINVER
/_WIN32_WINNT
(whatever your SDK supports), then load whatever functions you need dynamically at runtime (LoadLibrary
etc.) after knowing where you are actually running.
Upvotes: 1