Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
wutil.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2012 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 /*
24  * various Windows-specific utilities
25  */
26 
27 #include "precompiled.h"
29 
30 #include <stdio.h>
31 #include <stdlib.h> // __argc
32 
33 #include "lib/file/file.h"
34 #include "lib/posix/posix.h"
35 #include "lib/sysdep/sysdep.h"
36 #include "lib/sysdep/os/win/win.h"
37 #include "lib/sysdep/os/win/wdbg.h" // wdbg_assert
39 
40 #include <shlobj.h> // SHGetFolderPath
41 
42 
45 
46 
47 //-----------------------------------------------------------------------------
48 // safe allocator
49 
50 // may be used independently of libc malloc
51 // (in particular, before _cinit and while calling static dtors).
52 // used by wpthread critical section code.
53 
54 void* wutil_Allocate(size_t size)
55 {
56  const DWORD flags = HEAP_ZERO_MEMORY;
57  return HeapAlloc(GetProcessHeap(), flags, size);
58 }
59 
60 void wutil_Free(void* p)
61 {
62  const DWORD flags = 0;
63  HeapFree(GetProcessHeap(), flags, p);
64 }
65 
66 
67 //-----------------------------------------------------------------------------
68 // locks
69 
70 // several init functions are before called before _cinit.
71 // POSIX static mutex init may not have been done by then,
72 // so we need our own lightweight functions.
73 
74 static CRITICAL_SECTION cs[NUM_CS];
75 static bool cs_valid;
76 
78 {
79  if(!cs_valid)
80  return;
81  EnterCriticalSection(&cs[id]);
82 }
83 
85 {
86  if(!cs_valid)
87  return;
88  LeaveCriticalSection(&cs[id]);
89 }
90 
92 {
93  if(!cs_valid)
94  return false;
95  const BOOL successfullyEntered = TryEnterCriticalSection(&cs[id]);
96  if(!successfullyEntered)
97  return true; // still locked
98  LeaveCriticalSection(&cs[id]);
99  return false; // probably not locked
100 }
101 
102 
103 static void InitLocks()
104 {
105  for(int i = 0; i < NUM_CS; i++)
106  InitializeCriticalSection(&cs[i]);
107 
108  cs_valid = true;
109 }
110 
111 static void ShutdownLocks()
112 {
113  cs_valid = false;
114 
115  for(int i = 0; i < NUM_CS; i++)
116  DeleteCriticalSection(&cs[i]);
117  memset(cs, 0, sizeof(cs));
118 }
119 
120 
121 //-----------------------------------------------------------------------------
122 // error codes
123 
124 // only call after a Win32 function indicates failure.
126 {
127  switch(GetLastError())
128  {
129  case ERROR_BUSY:
130  case WAIT_TIMEOUT:
131  return ERR::AGAIN;
132  case ERROR_OPERATION_ABORTED:
133  return ERR::ABORTED;
134 
135  case ERROR_INVALID_HANDLE:
136  return ERR::INVALID_HANDLE;
137  case ERROR_INSUFFICIENT_BUFFER:
138  return ERR::INVALID_SIZE;
139  case ERROR_INVALID_PARAMETER:
140  case ERROR_BAD_ARGUMENTS:
141  return ERR::INVALID_PARAM;
142 
143  case ERROR_OUTOFMEMORY:
144  case ERROR_NOT_ENOUGH_MEMORY:
145  return ERR::NO_MEM;
146  case ERROR_NOT_SUPPORTED:
147  case ERROR_CALL_NOT_IMPLEMENTED:
148  case ERROR_PROC_NOT_FOUND:
149  return ERR::NOT_SUPPORTED;
150 
151  case ERROR_FILE_NOT_FOUND:
152  case ERROR_PATH_NOT_FOUND:
153  return ERR::FILE_NOT_FOUND;
154  case ERROR_ACCESS_DENIED:
155  return ERR::FILE_ACCESS;
156 
157  default:
158  return ERR::FAIL;
159  }
160 }
161 
162 
163 //-----------------------------------------------------------------------------
164 // command line
165 
166 // copy of GetCommandLine string. will be tokenized and then referenced by
167 // the argv pointers.
168 static wchar_t* argvContents;
169 
170 int s_argc = 0;
171 wchar_t** s_argv = 0;
172 
173 static void ReadCommandLine()
174 {
175  const wchar_t* commandLine = GetCommandLineW();
176  // (this changes as quotation marks are removed)
177  size_t numChars = wcslen(commandLine);
178  argvContents = (wchar_t*)HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, (numChars+1)*sizeof(wchar_t));
179  wcscpy_s(argvContents, numChars+1, commandLine);
180 
181  // first pass: tokenize string and count number of arguments
182  bool ignoreSpace = false;
183  for(size_t i = 0; i < numChars; i++)
184  {
185  switch(argvContents[i])
186  {
187  case '"':
188  ignoreSpace = !ignoreSpace;
189  // strip the " character
190  memmove(argvContents+i, argvContents+i+1, (numChars-i)*sizeof(wchar_t));
191  numChars--;
192  i--;
193  break;
194 
195  case ' ':
196  if(!ignoreSpace)
197  {
198  argvContents[i] = '\0';
199  s_argc++;
200  }
201  break;
202  }
203  }
204  s_argc++;
205 
206  // have argv entries point into the tokenized string
207  s_argv = (wchar_t**)HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, s_argc*sizeof(wchar_t*));
208  wchar_t* nextArg = argvContents;
209  for(int i = 0; i < s_argc; i++)
210  {
211  s_argv[i] = nextArg;
212  nextArg += wcslen(nextArg)+1;
213  }
214 }
215 
216 
218 {
219  return s_argc;
220 }
221 
222 wchar_t** wutil_argv()
223 {
224  ENSURE(s_argv);
225  return s_argv;
226 }
227 
228 
229 static void FreeCommandLine()
230 {
231  HeapFree(GetProcessHeap(), 0, s_argv);
232  HeapFree(GetProcessHeap(), 0, argvContents);
233 }
234 
235 
236 bool wutil_HasCommandLineArgument(const wchar_t* arg)
237 {
238  for(int i = 0; i < s_argc; i++)
239  {
240  if(!wcscmp(s_argv[i], arg))
241  return true;
242  }
243 
244  return false;
245 }
246 
247 
248 //-----------------------------------------------------------------------------
249 // directories
250 
251 // (NB: wutil_Init is called before static ctors => use placement new)
257 
259 {
260  return *systemPath;
261 }
262 
264 {
265  return *executablePath;
266 }
267 
269 {
270  return *localAppdataPath;
271 }
272 
274 {
275  return *roamingAppdataPath;
276 }
277 
279 {
280  return *personalPath;
281 }
282 
283 // Helper to avoid duplicating this setup
284 static OsPath* GetFolderPath(int csidl)
285 {
286  HWND hwnd = 0; // ignored unless a dial-up connection is needed to access the folder
287  HANDLE token = 0;
288  wchar_t path[MAX_PATH]; // mandated by SHGetFolderPathW
289  const HRESULT ret = SHGetFolderPathW(hwnd, csidl, token, 0, path);
290  ENSURE(SUCCEEDED(ret));
291  if(GetLastError() == ERROR_NO_TOKEN) // avoid polluting last error
292  SetLastError(0);
293  return new(wutil_Allocate(sizeof(OsPath))) OsPath(path);
294 }
295 
296 static void GetDirectories()
297 {
299 
300  // system directory
301  {
302  const UINT length = GetSystemDirectoryW(0, 0);
303  ENSURE(length != 0);
304  std::wstring path(length, '\0');
305  const UINT charsWritten = GetSystemDirectoryW(&path[0], length);
306  ENSURE(charsWritten == length-1);
307  systemPath = new(wutil_Allocate(sizeof(OsPath))) OsPath(path);
308  }
309 
310  // executable's directory
311  executablePath = new(wutil_Allocate(sizeof(OsPath))) OsPath(sys_ExecutablePathname().Parent());
312 
313  // roaming application data
314  roamingAppdataPath = GetFolderPath(CSIDL_APPDATA);
315 
316  // local application data
317  localAppdataPath = GetFolderPath(CSIDL_LOCAL_APPDATA);
318 
319  // my documents
320  personalPath = GetFolderPath(CSIDL_PERSONAL);
321 }
322 
323 
324 static void FreeDirectories()
325 {
326  systemPath->~OsPath();
327  wutil_Free(systemPath);
328  executablePath->~OsPath();
329  wutil_Free(executablePath);
330  localAppdataPath->~OsPath();
331  wutil_Free(localAppdataPath);
332  roamingAppdataPath->~OsPath();
333  wutil_Free(roamingAppdataPath);
334  personalPath->~OsPath();
335  wutil_Free(personalPath);
336 }
337 
338 
339 //-----------------------------------------------------------------------------
340 // user32 fix
341 
342 // HACK: make sure a reference to user32 is held, even if someone
343 // decides to delay-load it. this fixes bug #66, which was the
344 // Win32 mouse cursor (set via user32!SetCursor) appearing as a
345 // black 32x32(?) rectangle. the underlying cause was as follows:
346 // powrprof.dll was the first client of user32, causing it to be
347 // loaded. after we were finished with powrprof, we freed it, in turn
348 // causing user32 to unload. later code would then reload user32,
349 // which apparently terminally confused the cursor implementation.
350 //
351 // since we hold a reference here, user32 will never unload.
352 // of course, the benefits of delay-loading are lost for this DLL,
353 // but that is unavoidable. it is safer to force loading it, rather
354 // than documenting the problem and asking it not be delay-loaded.
355 static HMODULE hUser32Dll;
356 
358 {
359  hUser32Dll = LoadLibraryW(L"user32.dll");
360 }
361 
362 // avoids Boundschecker warning
363 static void FreeUser32Dll()
364 {
365  FreeLibrary(hUser32Dll);
366 }
367 
368 
369 //-----------------------------------------------------------------------------
370 // memory
371 
373 {
374  if(IsDebuggerPresent())
375  {
376  // and the debug heap isn't explicitly disabled,
377  char* var = getenv("_NO_DEBUG_HEAP");
378  if(!var || var[0] != '1')
379  return; // we can't enable the LFH
380  }
381 
382 #if WINVER >= 0x0501
383  WUTIL_FUNC(pHeapSetInformation, BOOL, (HANDLE, HEAP_INFORMATION_CLASS, void*, size_t));
384  WUTIL_IMPORT_KERNEL32(HeapSetInformation, pHeapSetInformation);
385  if(pHeapSetInformation)
386  {
387  ULONG flags = 2; // enable LFH
388  pHeapSetInformation(GetProcessHeap(), HeapCompatibilityInformation, &flags, sizeof(flags));
389  }
390 #endif // #if WINVER >= 0x0501
391 }
392 
393 
394 //-----------------------------------------------------------------------------
395 // Wow64
396 
397 // Wow64 'helpfully' redirects all 32-bit apps' accesses of
398 // %windir%\\system32\\drivers to %windir%\\system32\\drivers\\SysWOW64.
399 // that's bad, because the actual drivers are not in the subdirectory. to
400 // work around this, provide for temporarily disabling redirection.
401 
402 static WUTIL_FUNC(pIsWow64Process, BOOL, (HANDLE, PBOOL));
403 static WUTIL_FUNC(pWow64DisableWow64FsRedirection, BOOL, (PVOID*));
404 static WUTIL_FUNC(pWow64RevertWow64FsRedirection, BOOL, (PVOID));
405 
406 static bool isWow64;
407 
408 static void ImportWow64Functions()
409 {
410  WUTIL_IMPORT_KERNEL32(IsWow64Process, pIsWow64Process);
411  WUTIL_IMPORT_KERNEL32(Wow64DisableWow64FsRedirection, pWow64DisableWow64FsRedirection);
412  WUTIL_IMPORT_KERNEL32(Wow64RevertWow64FsRedirection, pWow64RevertWow64FsRedirection);
413 }
414 
415 static void DetectWow64()
416 {
417  // function not found => running on 32-bit Windows
418  if(!pIsWow64Process)
419  {
420  isWow64 = false;
421  return;
422  }
423 
424  BOOL isWow64Process = FALSE;
425  const BOOL ok = pIsWow64Process(GetCurrentProcess(), &isWow64Process);
426  WARN_IF_FALSE(ok);
427  isWow64 = (isWow64Process == TRUE);
428 }
429 
431 {
432  return isWow64;
433 }
434 
435 
437 {
438  // note: don't just check if the function pointers are valid. 32-bit
439  // Vista includes them but isn't running Wow64, so calling the functions
440  // would fail. since we have to check if actually on Wow64, there's no
441  // more need to verify the pointers (their existence is implied).
442  if(!wutil_IsWow64())
443  return;
444  const BOOL ok = pWow64DisableWow64FsRedirection(&m_wasRedirectionEnabled);
445  WARN_IF_FALSE(ok);
446 }
447 
449 {
450  if(!wutil_IsWow64())
451  return;
452  const BOOL ok = pWow64RevertWow64FsRedirection(m_wasRedirectionEnabled);
453  WARN_IF_FALSE(ok);
454 }
455 
456 
457 //-----------------------------------------------------------------------------
458 
459 Status wutil_SetPrivilege(const wchar_t* privilege, bool enable)
460 {
462 
463  HANDLE hToken;
464  if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken))
465  return ERR::_1;
466 
467  TOKEN_PRIVILEGES tp;
468  if (!LookupPrivilegeValueW(NULL, privilege, &tp.Privileges[0].Luid))
469  return ERR::_2;
470  tp.PrivilegeCount = 1;
471  tp.Privileges[0].Attributes = enable? SE_PRIVILEGE_ENABLED : 0;
472 
473  SetLastError(0);
474  const BOOL ok = AdjustTokenPrivileges(hToken, FALSE, &tp, 0, 0, 0);
475  if(!ok || GetLastError() != 0)
476  return ERR::_3;
477 
478  WARN_IF_FALSE(CloseHandle(hToken));
479  return INFO::OK;
480 }
481 
482 
483 //-----------------------------------------------------------------------------
484 // module handle
485 
486 #ifndef LIB_STATIC_LINK
487 
489 
491 {
492  HMODULE hModule;
493  const DWORD flags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
494  const BOOL ok = GetModuleHandleEx(flags, (LPCWSTR)&wutil_LibModuleHandle, &hModule);
495  // (avoid ENSURE etc. because we're called from debug_DisplayError)
496  wdbg_assert(ok);
497  return hModule;
498 }
499 
500 #else
501 
502 HMODULE wutil_LibModuleHandle()
503 {
504  return GetModuleHandle(0);
505 }
506 
507 #endif
508 
509 
510 
511 //-----------------------------------------------------------------------------
512 // find main window
513 
514 // this is required by the error dialog and clipboard code.
515 // note that calling from wutil_Init won't work, because the app will not
516 // have created its window by then.
517 
518 static HWND hAppWindow;
519 
520 static BOOL CALLBACK FindAppWindowByPid(HWND hWnd, LPARAM UNUSED(lParam))
521 {
522  DWORD pid;
523  DWORD tid = GetWindowThreadProcessId(hWnd, &pid);
524  UNUSED2(tid);
525 
526  if(pid == GetCurrentProcessId())
527  hAppWindow = hWnd;
528 
529  return TRUE; // keep calling
530 }
531 
533 {
534  if(!hAppWindow)
535  {
536  WARN_IF_FALSE(EnumWindows(FindAppWindowByPid, 0));
537  // (hAppWindow may still be 0 if we haven't created a window yet)
538  }
539 
540  return hAppWindow;
541 }
542 
543 
544 //-----------------------------------------------------------------------------
545 
547 {
548  InitLocks();
549 
551 
553 
554  ReadCommandLine();
555 
556  GetDirectories();
557 
559  DetectWow64();
560 
561  return INFO::OK;
562 }
563 
564 
566 {
567  FreeCommandLine();
568 
569  FreeUser32Dll();
570 
571  ShutdownLocks();
572 
573  FreeDirectories();
574 
575  return INFO::OK;
576 }
HWND wutil_AppWindow()
Definition: wutil.cpp:532
static OsPath * GetFolderPath(int csidl)
Definition: wutil.cpp:284
const Status _1
Definition: status.h:441
#define WINIT_REGISTER_LATE_SHUTDOWN(func)
Definition: winit.h:157
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
static void FreeDirectories()
Definition: wutil.cpp:324
static OsPath * roamingAppdataPath
Definition: wutil.cpp:255
void wutil_Unlock(WinLockId id)
Definition: wutil.cpp:84
const Status OK
Definition: status.h:386
some WinAPI functions SetLastError(0) on success, which is bad because it can hide previous errors...
Definition: wutil.h:119
HMODULE wutil_LibModuleHandle()
Definition: wutil.cpp:490
void * wutil_Allocate(size_t size)
Definition: wutil.cpp:54
#define WUTIL_FUNC(varName, ret, params)
Definition: wutil.h:44
static void FreeUser32Dll()
Definition: wutil.cpp:363
static void ForciblyLoadUser32Dll()
Definition: wutil.cpp:357
Path Parent() const
Definition: path.h:150
const Status _3
Definition: status.h:443
static HWND hAppWindow
Definition: wutil.cpp:518
static Status wutil_Shutdown()
Definition: wutil.cpp:565
int wutil_argc()
Definition: wutil.cpp:217
Definition: wutil.h:83
const Status INVALID_HANDLE
Definition: status.h:419
int BOOL
Definition: wgl.h:51
const Status AGAIN
Definition: status.h:427
int wcscpy_s(wchar_t *dst, size_t max_dst_chars, const wchar_t *src)
const Status ABORTED
Definition: status.h:414
wchar_t ** s_argv
Definition: wutil.cpp:171
const Status NOT_SUPPORTED
Definition: status.h:429
bool wutil_IsLocked(WinLockId id)
Definition: wutil.cpp:91
const OsPath & wutil_RoamingAppdataPath()
Definition: wutil.cpp:273
const OsPath & wutil_LocalAppdataPath()
Definition: wutil.cpp:268
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
#define UNUSED2(param)
mark a function local variable or parameter as unused and avoid the corresponding compiler warning...
#define CALLBACK
Definition: wgl.h:39
#define WINIT_REGISTER_EARLY_INIT(func)
Definition: winit.h:140
Definition: path.h:75
void * HANDLE
Definition: wgl.h:62
static OsPath * systemPath
Definition: wutil.cpp:252
Path OsPath
Definition: os_path.h:31
unsigned long DWORD
Definition: wgl.h:56
static OsPath * executablePath
Definition: wutil.cpp:253
int s_argc
Definition: wutil.cpp:170
Status StatusFromWin()
Definition: wutil.cpp:125
const Status INVALID_PARAM
Definition: status.h:423
static wchar_t * argvContents
Definition: wutil.cpp:168
i64 Status
Error handling system.
Definition: status.h:171
static BOOL CALLBACK FindAppWindowByPid(HWND hWnd, LPARAM lParam)
Definition: wutil.cpp:520
const Status INVALID_SIZE
Definition: status.h:421
static CRITICAL_SECTION cs[NUM_CS]
Definition: wutil.cpp:74
const Status FILE_NOT_FOUND
Definition: file.h:36
Status wutil_SetPrivilege(const wchar_t *privilege, bool enable)
Definition: wutil.cpp:459
wchar_t ** wutil_argv()
Definition: wutil.cpp:222
static void ShutdownLocks()
Definition: wutil.cpp:111
static Status wutil_Init()
Definition: wutil.cpp:546
static void ImportWow64Functions()
Definition: wutil.cpp:408
OsPath sys_ExecutablePathname()
Definition: bsd.cpp:33
const OsPath & wutil_ExecutablePath()
Definition: wutil.cpp:263
#define WARN_IF_FALSE(expression)
Definition: status.h:360
static bool cs_valid
Definition: wutil.cpp:75
WinLockId
Definition: wutil.h:78
static void DetectWow64()
Definition: wutil.cpp:415
static HMODULE hUser32Dll
Definition: wutil.cpp:355
const OsPath & wutil_SystemPath()
Definition: wutil.cpp:258
static void EnableLowFragmentationHeap()
Definition: wutil.cpp:372
static OsPath * personalPath
Definition: wutil.cpp:256
unsigned int UINT
Definition: wgl.h:54
const Status FAIL
Definition: status.h:406
static void GetDirectories()
Definition: wutil.cpp:296
static OsPath * localAppdataPath
Definition: wutil.cpp:254
const Status _2
Definition: status.h:442
static bool isWow64
Definition: wutil.cpp:406
const Status FILE_ACCESS
Definition: file.h:35
bool wutil_HasCommandLineArgument(const wchar_t *arg)
Definition: wutil.cpp:236
#define wdbg_assert(expr)
similar to ENSURE but safe to use during critical init or while under the heap or dbghelp locks...
Definition: wdbg.h:43
const Status NO_MEM
Definition: status.h:430
void wutil_Lock(WinLockId id)
Definition: wutil.cpp:77
#define WUTIL_IMPORT_KERNEL32(procName, varName)
Definition: wutil.h:63
const OsPath & wutil_PersonalPath()
Definition: wutil.cpp:278
static void ReadCommandLine()
Definition: wutil.cpp:173
bool wutil_IsWow64()
Definition: wutil.cpp:430
static void FreeCommandLine()
Definition: wutil.cpp:229
static void InitLocks()
Definition: wutil.cpp:103
void wutil_Free(void *p)
Definition: wutil.cpp:60