My objective is to detect Windows 10 in my code which has to work cross-platform as well as across different versions of Windows (atleast 7 & up). Windows provides IsWindows10OrGreater() to tackle this problem, but there's another issue with it, this function isn't present in previous windows versions.
You'll find countless blogs and SO questions regarding this as well as the manifest madness where functions like this and getversion and others return some different version rather than the correct one.
For example on my machine - the method IsWindows10OrGreater() doesn't compile(I would've to install Win10 SDK), and IsWindowsVersionOrGreater() reports 6 as major version.
So is there a sane multi-version way I could solve this problem?
The most straight-forward way to retrieve the true OS version is to call RtlGetVersion. It is what GetVersionEx and VerifyVersionInfo call, but doesn't employ the compatibility shims.
You can either use the DDK (by #including <ntddk.h> and linking against NtosKrnl.lib from kernel mode, or ntdll.lib from user mode), or use runtime dynamic linking as in the following snippet:
typedef LONG NTSTATUS, *PNTSTATUS;
#define STATUS_SUCCESS (0x00000000)
typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
RTL_OSVERSIONINFOW GetRealOSVersion() {
HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
if (hMod) {
RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
if (fxPtr != nullptr) {
RTL_OSVERSIONINFOW rovi = { 0 };
rovi.dwOSVersionInfoSize = sizeof(rovi);
if ( STATUS_SUCCESS == fxPtr(&rovi) ) {
return rovi;
}
}
}
RTL_OSVERSIONINFOW rovi = { 0 };
return rovi;
}
In case you need additional information you can pass an RTL_OSVERSIONINFOEXW structure in place of the RTL_OSVERSIONINFOW structure (properly assigning the dwOSVersionInfoSize member).
This returns the expected result on Windows 10, even when there is no manifest attached.
As an aside, it is commonly accepted as a better solution to provide different implementations based on available features rather than OS versions.
You can read real build number from the registry, and then infer Windows version from it. Your application does not need to have a manifest for this work: on my machine, it correctly detects OS build number as 10586. For example:
#include <Windows.h>
#include <sstream>
struct HKeyHolder
{
private:
HKEY m_Key;
public:
HKeyHolder() :
m_Key(nullptr)
{
}
HKeyHolder(const HKeyHolder&) = delete;
HKeyHolder& operator=(const HKeyHolder&) = delete;
~HKeyHolder()
{
if (m_Key != nullptr)
RegCloseKey(m_Key);
}
operator HKEY() const
{
return m_Key;
}
HKEY* operator&()
{
return &m_Key;
}
};
bool IsRunningOnWindows10()
{
HKeyHolder currentVersion;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", 0, KEY_QUERY_VALUE, ¤tVersion) != ERROR_SUCCESS)
return false;
DWORD valueType;
BYTE buffer[256];
DWORD bufferSize = 256;
if (RegQueryValueExW(currentVersion, L"CurrentBuild", nullptr, &valueType, buffer, &bufferSize) != ERROR_SUCCESS)
return false;
if (valueType != REG_SZ)
return false;
int version;
std::wistringstream versionStream(reinterpret_cast<wchar_t*>(buffer));
versionStream >> version;
return version > 9800;
}
IsWindows10OrGreater() from VersionHelpers.h
Check the notes at Version Helper functions on MSDN
File VersionHelpers.h is shipped with Windows 10 SDK, but it will work in previous versions, too. Just copy it to your development environment.
It's just a header-only defined small lib, which uses VerSetConditionMask and VerifyVersionInfoW functions, both available in WinAPI since Windows 2000.
Upd If you can not include manifest file with your source code, you can use simple hack: just get a version of any system dll, for example, kernel32.dll using GetFileVersionInfo function.
Related
I am trying to find the build version of Windows Server 2016 machines, for example RS1 or RS3. There was an API to do this - GetVersionEx() - but is now deprecated.
MSDN says to use Version Helper Functions instead.
I want the build version, for ex: 1607 for RS1.
Is there an API to get this?
Option 0: (per RbMm) Use [RtlGetVersion] from the driver development kit.
Option 1: [Updated] Grab the version number of a system DLL like kernel32.dll. MSDN used to bless this approach, saying:
To obtain the full version number for the operating system, call the GetFileVersionInfo function on one of the system DLLs, such as Kernel32.dll, then call VerQueryValue to obtain the \StringFileInfo\\ProductVersion subblock of the file version information. [From an Internet Archive snapshot of MSDN circa 2017]
That would look something like this:
// Quick hack without error checking.
#include <cassert>
#include <iomanip>
#include <iostream>
#include <vector>
#include <Windows.h>
int main() {
const auto system = L"kernel32.dll";
DWORD dummy;
const auto cbInfo =
::GetFileVersionInfoSizeExW(FILE_VER_GET_NEUTRAL, system, &dummy);
std::vector<char> buffer(cbInfo);
::GetFileVersionInfoExW(FILE_VER_GET_NEUTRAL, system, dummy,
buffer.size(), &buffer[0]);
void *p = nullptr;
UINT size = 0;
::VerQueryValueW(buffer.data(), L"\\", &p, &size);
assert(size >= sizeof(VS_FIXEDFILEINFO));
assert(p != nullptr);
auto pFixed = static_cast<const VS_FIXEDFILEINFO *>(p);
std::cout << HIWORD(pFixed->dwFileVersionMS) << '.'
<< LOWORD(pFixed->dwFileVersionMS) << '.'
<< HIWORD(pFixed->dwFileVersionLS) << '.'
<< LOWORD(pFixed->dwFileVersionLS) << '\n';
return 0;
}
Note that the original MSDN link now redirects to a newer documentation set that doesn't mention this approach. I suppose that means this is no longer a supported technique, and, presumably, all the compatibility hacks for older code might prevent an application from getting the actual answer.
Option 2: Query the registry, specifically:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion
which has values for CurrentMajorVersionNumber, CurrentMinorVersionNumber, and CurrentBuildNumber.
I can't find official documentation for these values, so this may not be MSDN-approved or future-proof.
Option 3: Use GetProductInfo if available and fall back to GetVersionInfo if it's not.
Folks
I am in the process up upgrading a legacy application from VS2008 to VS2015. It is extremely dependent on ATL and COM. I have noticed a change in _ATL_COM_MODULE70 in atlbase.h that is causing issues.
In ...\9.0\VC\atlmfc\include\atlbase.h it is defined as:
struct _ATL_COM_MODULE70
{
UINT cbSize;
HINSTANCE m_hInstTypeLib;
_ATL_OBJMAP_ENTRY** m_ppAutoObjMapFirst;
_ATL_OBJMAP_ENTRY** m_ppAutoObjMapLast;
CComCriticalSection m_csObjMap;
};
However in ...\14.0\VC\atlmfc\include\atlcom.h it is defined as:
struct _ATL_COM_MODULE70
{
UINT cbSize;
HINSTANCE m_hInstTypeLib;
_ATL_OBJMAP_ENTRY_EX** m_ppAutoObjMapFirst;
_ATL_OBJMAP_ENTRY_EX** m_ppAutoObjMapLast;
CComCriticalSection m_csObjMap;
};
This causes the following code not to compile:
HRESULT FreeThreadSingletons (void)
{
for (_ATL_OBJMAP_ENTRY** ppEntry = _AtlComModule.m_ppAutoObjMapFirst; ppEntry < _AtlComModule.m_ppAutoObjMapLast; ppEntry++)
{
if (*ppEntry != NULL)
{
_ATL_OBJMAP_ENTRY* pEntry = *ppEntry;
CComClassFactoryThreadSingleton *pThread;
if ((pThread = dynamic_cast<CComClassFactoryThreadSingleton *>(pEntry->pCF)) != NULL)
{
pThread->ReleaseAllObjects ();
}
}
}
return S_OK;
}
The reason is because m_ppAutoObjMapFirst is of type _ATL_OBJMAP_ENTRY_EX in VC14 and _ATL_OBJMAP_ENTRY in VC9.
Microsoft's documentation for VS2015 says that the struct should be the same in VC14 as in VC9:
https://msdn.microsoft.com/en-us/library/ayw1b6h5.aspx
Now for the question. Has anyone seen this issue in the past and is there a workaround that does not involve rewriting everything to the _ATL_OBJMAP_ENTRY_EX interface?
Thanks for the response H. Guijit. Here is Microsoft's response:
As you have already noticed, the documentation in MSDN at _ATL_COM_MODULE70 Structure is inaccurate.
_ATL_OBJMAP_ENTRY and _ATL_OBJMAP_ENTRY_EX are typedefs for two different structures defined in atlbase.h
Unfortunately, you have fallen victim to code that relies on internal structures and implementations that are subject to change.
Not the most helpful thing I have ever read..
I have changed the code to retrieve IUnknown from pCache->pCF
HRESULT FreeThreadSingletons (void)
{
for (_ATL_OBJMAP_ENTRY_EX** ppEntry = _AtlComModule.m_ppAutoObjMapFirst; ppEntry < _AtlComModule.m_ppAutoObjMapLast; ppEntry++)
{
if (*ppEntry != NULL)
{
_ATL_OBJMAP_ENTRY_EX* pEntry = *ppEntry;
CComClassFactoryThreadSingleton *pThread;
if ((pThread = dynamic_cast<CComClassFactoryThreadSingleton *>((pEntry->pCache)->pCF)) != NULL)
{
pThread->ReleaseAllObjects ();
}
}
}
return S_OK;
}
I am not sure this will work yet as I'm still battling build problems but hope to run the application up in the next day or two.
You can rewrite it like this:
for(auto ppEntry = _AtlComModule.m_ppAutoObjMapFirst; ppEntry < _AtlComModule.m_ppAutoObjMapLast; ppEntry++)
This is always correct, no matter the type of the pointer.
I suppose you could avoid modifying your source with some preprocessor magic, but I would consider doing so to be a bit of a disaster for maintainability.
As for having seen this in the past... Microsoft quite often extends existing structs and functions, and when they do the extended struct or function gets 'ex' appended to its name. 'ex' structs are usually (always?) identical to the older version, just with extra fields. Same with 'ex' functions, which usually just get some extra parameters.
I'm using GetProcAddress to gain access to a standard Isapi Filter DLL method - the GetFilterVersion method which takes a pointer to a HTTP_FILTER_VERSION structure.
https://msdn.microsoft.com/en-us/library/ms525822(v=vs.90).aspx
https://msdn.microsoft.com/en-us/library/ms525465(v=vs.90).aspx
I've tested the code against a working Isapi filter that I've written and it works fine. I debug the code against an Isapi filter from a vendor (I don't have access to the source code or anything beyond the dll itself) and I get the exception, "access violation writing location". What could be the issue? (Both Isapi filters work in IIS.)
//Attempted to define function ptr several ways
typedef BOOL(__cdecl * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
//typedef BOOL( * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
//typedef BOOL(WINAPI * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
void arbitraryMethod()
{
HINSTANCE hDLL; // Handle to DLL
TRIRIGAISAPIV lpfnDllFunc2; // Function pointer
DWORD lastError;
BOOL uReturnVal2;
hDLL = LoadLibrary(L"iisWASPlugin_http.dll"); //vendor's dll
//hDLL = LoadLibrary(L"KLTWebIsapi.dll //my dll
if (hDLL != NULL)
{
lpfnDllFunc2 = (TRIRIGAISAPIV)GetProcAddress(hDLL, "GetFilterVersion");
if (!lpfnDllFunc2)
{
lastError = GetLastError();
// handle the error
FreeLibrary(hDLL);
//return 1;
}
else
{
HTTP_FILTER_VERSION pVer = { 6 };
//Call the function via pointer; Works with my dll, fails with vendor's
uReturnVal2 = lpfnDllFunc2(&pVer);
//................ HELP!!!!!!!!!!!!!
}
}
}
One issue that I see is that your function pointer declaration is incorrect.
According to the Microsoft documentation, GetFilterVersion is prototyped as:
BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer);
The WINAPI is a Windows macro that is actually defined as __stdcall, thus you are declaring the function pointer incorrectly when you used __cdecl.
What does WINAPI mean?
Thus, your declaration should be:
typedef BOOL(__stdcall * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
It could be that there are actually some additional structure fields filled by the custom filter.
You can try to increase the size of the structure to see if that will work, like for example:
struct HTTP_FILTER_VERSION_EXTRA {
HTTP_FILTER_VERSION v;
char[1024] extra;
};
HTTP_FILTER_VERSION_EXTRA ver;
ver.v.dwServerFilterVersion = 6;
uReturnVal2 = lpfnDllFunc2(&ver.v);
It is sometimes the case with the WinAPI structures that they allow versioning, so adding fields is possible. If the function doesn't then check (or doesn't know) the actual structure version, it might try to use an extended one which might be different than the one supplied - if the size of the supplied struct is then lesser than the structure version the func tries to use, bad things can happen.
Also check if the DLL is 64-bit or 32-bit. You cannot use 64-bit DLL by 32-bit app and vice versa (but I expect that would already fail during the LoadLibrary call).
I got a dll with the following prototype:
DLL_EXPORT std::list<std::wstring>* c_ExplodeWStringToList(std::wstring in_delimiter, std::wstring in_string, int in_limit);
The application uses this like that:
std::list<std::wstring>* exploded = mydllclass->c_ExplodeWStringToList(L" ", in_command.c_str(), 0);
This works great under XP 32, but when I try this at home with my Vista 64 my program just closes itself. No error and no warning?
Some days ago the DLL was returning the list directly - no pointer. But I switched to VC++ 2010 Express, and I could not compile my DLL without this modification.
Anything I am not seeing here?
Thank you :)
Update:
DLL_EXPORT std::list<std::wstring>* c_ExplodeWStringToList(std::wstring in_delimiter, std::wstring in_string, int in_limit)
{
std::list<std::wstring>* returnlist = new std::list<std::wstring>();
std::list<std::wstring>* stringlist = new std::list<std::wstring>();
UINT pos = 0;
while(true)
{
pos = in_string.find(in_delimiter, 0);
if(pos == std::string::npos)
{
stringlist->push_back(in_string.substr(0, pos));
break;
}
else
{
stringlist->push_back(in_string.substr(0, pos));
in_string = in_string.substr(pos + in_delimiter.length());
}
}
// ****
// Here is missing some code I've commented out while searching for the error.
// ****
returnlist = stringlist;
return returnlist;
}
T
I didn't dig into the code, but a conclusion I came to regarding working with DLLs is to not return anything but primitive types from DLL functions. This is because due to different compilers or different switches or project settings structs and classes are not aligned the same not have the same size in the DLL and in the code calling the DLL.
So returning a list from a DLL might be considered malformed in the caller application.
Same thing regards throwing exceptions from a DLL - the class thrown might be misinterpreted by the catching code.
So, best is export only C functions that return primitive types (to denote error codes).
(This is not so much a problem as an exercise in pedantry, so here goes.)
I've made a nice little program that is native to my linux OS, but I'm thinking it's useful enough to exist on my Windows machine too. Thus, I'd like to access Windows' environment variables, and MSDN cites an example like this:
const DWORD buff_size = 50;
LPTSTR buff = new TCHAR[buff_size];
const DWORD var_size = GetEnvironmentVariable("HOME",buff,buff_size);
if (var_size==0) { /* fine, some failure or no HOME */ }
else if (var_size>buff_size) {
// OK, so 50 isn't big enough.
if (buff) delete [] buff;
buff = new TCHAR[var_size];
const DWORD new_size = GetEnvironmentVariable("HOME",buff,var_size);
if (new_size==0 || new_size>var_size) { /* *Sigh* */ }
else { /* great, we're done */ }
}
else { /* in one go! */ }
This is not nearly as nice (to me) as using getenv and just checking for a null pointer. I'd also prefer not to dynamically allocate memory since I'm just trying to make the program run on Windows as well as on my linux OS, which means that this MS code has to play nicely with nix code. More specifically:
template <class T> // let the compiler sort out between char* and TCHAR*
inline bool get_home(T& val) { // return true if OK, false otherwise
#if defined (__linux) || (__unix)
val = getenv("HOME");
if (val) return true;
else return false;
#elif defined (WINDOWS) || defined (_WIN32) || defined (WIN32)
// something like the MS Code above
#else
// probably I'll just return false here.
#endif
}
So, I'd have to allocate on the heap universally or do a #ifdef in the calling functions to free the memory. Not very pretty.
Of course, I could have just allocated 'buff' on the stack in the first place, but then I'd have to create a new TCHAR[] if 'buff_size' was not large enough on my first call to GetEnvironmentVariable. Better, but what if I was a pedant and didn't want to go around creating superfluous arrays? Any ideas on something more aesthetically pleasing?
I'm not that knowledgeable, so would anyone begrudge me deliberately forcing GetEnvironmentVariable to fail in order to get a string size? Does anyone see a problem with:
const DWORD buff_size = GetEnvironmentVariable("HOME",0,0);
TCHAR buff[buff_size];
const DWORD ret = GetEnvironmentVariable("HOME",buff,buff_size);
// ...
Any other ideas or any suggestions? (Or corrections to glaring mistakes?)
UPDATE:
Lots of useful information below. I think the best bet for what I'm trying to do is to use a static char[] like:
inline const char* get_home(void) { // inline not required, but what the hell.
#if defined (__linux) || (__unix)
return getenv("HOME");
#elif defined (WINDOWS) || defined (WIN32) || defined (_WIN32)
static char buff[MAX_PATH];
const DWORD ret = GetEnvironmentVariableA("USERPROFILE",buff,MAX_PATH);
if (ret==0 || ret>MAX_PATH)
return 0;
else
return buff;
#else
return 0;
#endif
}
Perhaps it's not the most elegant way of doing it, but it's probably the easiest way to sync up what I want to do between *nix and Windows. (I'll also worry about Unicode support later.)
Thank you to everybody who has helped.
DWORD bufferSize = 65535; //Limit according to http://msdn.microsoft.com/en-us/library/ms683188.aspx
std::wstring buff;
buff.resize(bufferSize);
bufferSize = GetEnvironmentVariableW(L"Name", &buff[0], bufferSize);
if (!bufferSize)
//error
buff.resize(bufferSize);
Of course, if you want ASCII, replace wstring with string and GetEnvironmentVariableW with GetEnvironmentVariableA.
EDIT: You could also create getenv yourself. This works because
The same memory location may be used in subsequent calls to getenv, overwriting the previous content.
const char * WinGetEnv(const char * name)
{
const DWORD buffSize = 65535;
static char buffer[buffSize];
if (GetEnvironmentVariableA(name, buffer, buffSize))
{
return buffer;
}
else
{
return 0;
}
}
Of course, it would probably be a good idea to use the wide character versions of all of this if you want to maintain unicode support.
This wasn't the original question, but it might worth to add the MFC way to this thread for reference:
CString strComSpec;
if (strComSpec.GetEnvironmentVariable(_T("COMSPEC")))
{
//Do your stuff here
}
VC++ implements getenv in stdlib.h, see, for example, here.
The suggestion you made at the end of your post is the right way to do this - call once to get required buffer size and then again to actually get the data. Many of the Win32 APIs work this way, it's confusing at first but common.
One thing you could do is to pass in a best-guess buffer and its size on the first call, and only call again if that fails.
Don't bother. %HOME% is a path on Windows, and should be usable by all reasonable programs. Therefore, it will fit in a WCHAR[MAX_PATH]. You don't need to deal with the edge case where it's longer than that - if it's longer, most file functions will reject it anyway so you might as well fail early.
However, do not assume you can use a TCHAR[MAX_PATH] or a char[MAX_PATH]. You do not have control over the contents of %HOME%; it will contain the users name. If that's "André" (i.e. not ASCII) you must store %HOME% in a WCHAR[MAX_PATH].