|
From: Alessandro M. <ale...@ho...> - 2015-04-06 15:22:32
|
Il 21/03/2015 23:13, Scott ha scritto:
> Hi Core Developers,
>
> I’ve built SimGear/FlightGear from source and encountered a few portability issues with MSVC. Attached are some patches for your consideration.
>
> [...]
> [SimGear] Portability: Implemented Known Folders for Windows
Hi Scott,
a forum member testing a nightly build has encountered a problem while
running FG under Windows XP [1] - I think it might be related to your
"Known Folders" patch.
Specifically, it seems you're using WINVER to check if FG is run under
Windows XP or under a later version. However, the WINVER preprocessor
definition can be used only to check the minimum Windows SDK target
version on the system FG is compiled on - not the Windows version
available at runtime. As the build server has a fairly recent version
of the SDK, SHGetKnownFolderPath gets linked to the executable every
time; Windows XP correctly detects that the function is not available
on that OS and refuses to launch fgfs.exe.
You should detect the Windows version using the Version Helper API [2]
API and call SHGetKnownFolderPath dynamically (so that it is not
present in the linker table) [3].
Here's a sketch of the proposed solution (apologies for not sending
a fully working patch, I'm still trying to compile FG on my system):
-----
// Note: I assume the FG builders have the latest (8.1) Windows SDK
// installed. I use versionhelpers.h because GetVersion has been marked
// deprecated in Windows 8.1 and later, see [2].
#include <versionhelpers.h>
typedef void (STDAPI *PSHGKFP)(REFKNOWNFOLDERID, DWORD, HANDLE, PWSTR*);
static SGPath pathForKnownFolder(REFKNOWNFOLDERID folderId, const SGPath& def)
{
PSHGKFP pSHGetKnownFolderPath;
// system call will allocate dynamic memory... which we must release when done
wchar_t* localFolder = 0;
// Note: I suppose FG is already linked to shell32.dll. If it is not, a call to
// LoadLibrary() should be made before this one.
pSHGetKnownFolderPath = (PSHGKFP) GetProcAddress(GetModuleHandle(TEXT("shell32.dll")), "SHGetKnownFolderPath");
if (NULL != pSHGetKnownFolderPath) {
if (pSHGetKnownFolderPath(folderId, KF_FLAG_DEFAULT_PATH, NULL, &localFolder) == S_OK) {
// copy into local memory
char path[MAX_PATH];
size_t len;
if (wcstombs_s(&len, path, localFolder, MAX_PATH) != S_OK) {
path[0] = '\0';
SG_LOG(SG_GENERAL, SG_WARN, "WCS to MBS failed");
}
SGPath folder_path = SGPath(path, def.getPermissionChecker());
// release dynamic memory
CoTaskMemFree(static_cast<void*>(localFolder));
return folder_path;
}
}
// If the call to SHGetKnownFolderPath was unsuccesful or the function
// was not found (even though we were on Windows Vista or later), return
// the default path.
return def;
}
// Then replace the WINVER detection with:
#ifdef _WIN32
if (IsWindowsVistaOrGreater()) {
case DESKTOP:
return pathForKnownFolder(FOLDERID_Desktop, def);
[...]
} else {
case DESKTOP:
return pathForCSIDL(CSIDL_DESKTOPDIRECTORY, def);
[...]
}
#endif
-----
An alternative might be unifying the pathForKnownFolder and pathForCSIDL
functions and performing the Windows version check in there
(SHGetKnownFolderPath should still be called dynamically, though).
Cheers,
Alessandro Menti
[1] http://forum.flightgear.org/viewtopic.php?f=11&t=25831
[2] https://msdn.microsoft.com/en-us/library/windows/desktop/dn424972%28v=vs.85%29.aspx
[3] https://msdn.microsoft.com/en-us/library/windows/desktop/ms686944%28v=vs.85%29.aspx
|