/*
* win32.cxx
*
* Miscellaneous implementation of classes for Win32
*
* Portable Windows Library
*
* Copyright (c) 1993-1998 Equivalence Pty. Ltd.
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Portions are Copyright (C) 1993 Free Software Foundation, Inc.
* All Rights Reserved.
*
* Contributor(s): ______________________________________.
*
* $Log: win32.cxx,v $
* Revision 1.156 2005/11/30 12:47:42 csoutheren
* Removed tabs, reformatted some code, and changed tags for Doxygen
*
* Revision 1.155 2005/11/17 22:54:26 csoutheren
* Fixed missed functions in de-consting PMutex functions
*
* Revision 1.154 2005/11/14 22:29:13 csoutheren
* Reverted Wait and Signal to non-const - there is no way we can guarantee that all
* descendant classes everywhere will be changed over, so we have to keep the
* original API
*
* Revision 1.153 2005/11/09 09:19:10 csoutheren
* Now actually remove the call :)
*
* Revision 1.152 2005/11/09 09:11:39 csoutheren
* Moved Windows-specific AttachThreadInput callsto seperate member function
* on PThread. This removes a linearly increasing delay in creating new threads
*
* Revision 1.151 2005/11/04 06:34:20 csoutheren
* Added new class PSync as abstract base class for all mutex/sempahore classes
* Changed PCriticalSection to use Wait/Signal rather than Enter/Leave
* Changed Wait/Signal to be const member functions
* Renamed PMutex to PTimedMutex and made PMutex synonym for PCriticalSection.
* This allows use of very efficient mutex primitives in 99% of cases where timed waits
* are not needed
*
* Revision 1.150 2005/09/23 15:30:46 dominance
* more progress to make mingw compile nicely. Thanks goes to Julien Puydt for pointing out to me how to do it properly. ;)
*
* Revision 1.149 2005/09/18 13:01:43 dominance
* fixed pragma warnings when building with gcc.
*
* Revision 1.148 2005/07/13 12:48:32 csoutheren
* Backported fix from isvo branch
*
* Revision 1.147 2005/06/07 07:41:42 csoutheren
* Applied patch 1176459 for PocketPC. Thanks to Matthias Weber
*
* Revision 1.146.2.1 2005/04/25 13:12:39 shorne
* Fixed OSConfigDir for win32/NT/XP
*
* Revision 1.146 2005/02/02 23:21:16 csoutheren
* Fixed problem with race condition in HousekeepingThread
* Thanks to an idea from Auri Vizgaitis
*
* Revision 1.145 2005/01/25 11:28:25 csoutheren
* Changed parms to CreateEvent to be more clear
*
* Revision 1.144 2005/01/16 23:00:36 csoutheren
* Fixed problem when calling WaitForTermination from within the same thread
*
* Revision 1.143 2005/01/11 06:57:15 csoutheren
* Fixed namespace collisions with plugin starup factories
*
* Revision 1.142 2005/01/04 07:44:04 csoutheren
* More changes to implement the new configuration methodology, and also to
* attack the global static problem
*
* Revision 1.141 2004/11/17 12:50:44 csoutheren
* Win32 DCOM support, thanks to Simon Horne
*
* Revision 1.140 2004/10/31 22:22:06 csoutheren
* Added pragma to include ole32.lib for static builds
*
* Revision 1.139 2004/10/23 10:50:28 ykiryanov
* Added ifdef _WIN32_WCE for PocketPC 2003 SDK port
*
* Revision 1.138 2004/09/17 04:05:12 csoutheren
* Changed Windows PDirectory semantics to be the same as Unix
*
* Revision 1.137 2004/06/09 13:35:11 csoutheren
* Disabled "wait for key" at end of program unless in debug mode or PMEMORY_CHECK
* is enabled
*
* Revision 1.136 2004/05/21 00:28:40 csoutheren
* Moved PProcessStartup creation to PProcess::Initialise
* Added PreShutdown function and called it from ~PProcess to handle PProcessStartup removal
*
* Revision 1.135 2004/04/09 06:52:18 rjongbloed
* Removed #pargma linker command for /delayload of DLL as documentations sais that
* you cannot do this.
*
* Revision 1.134 2004/04/03 06:54:30 rjongbloed
* Many and various changes to support new Visual C++ 2003
*
* Revision 1.133 2004/02/23 23:52:20 csoutheren
* Added pragmas to avoid every Windows application needing to include libs explicitly
*
* Revision 1.132 2003/11/10 20:52:26 dereksmithies
* add fix from Diego Tartara to recognize win XP and 2003 Server. Many thanks.
*
* Revision 1.131 2003/11/08 01:43:05 rjongbloed
* Fixed race condition that could start two housekeeping threads, thanks Ted Szoczei
*
* Revision 1.130 2003/11/05 05:56:08 csoutheren
* Added #pragma to include required libs
*
* Revision 1.129 2003/10/27 03:29:11 csoutheren
* Added support for QoS
* Thanks to Henry Harrison of AliceStreet
*
* Revision 1.128 2003/09/17 05:45:10 csoutheren
* Removed recursive includes
*
* Revision 1.127 2003/02/26 01:12:52 robertj
* Fixed race condition where thread can terminatebefore an IsSuspeded() call
* occurs and cause an assert, thanks Sebastian Meyer
*
* Revision 1.126 2002/12/11 22:25:04 robertj
* Added ability to set user identity temporarily and permanently.
* Added get and set users group functions.
*
* Revision 1.125 2002/11/20 02:38:38 robertj
* Fixed file path parsing for common unix/dos path error.
*
* Revision 1.124 2002/11/20 00:58:58 robertj
* Made file path parsing slightly smarter for common unix/dos path error.
*
* Revision 1.123 2002/11/19 10:28:50 robertj
* Changed PFilePath so can be empty string, indicating illegal path.
*
* Revision 1.122 2002/09/23 07:17:24 robertj
* Changes to allow winsock2 to be included.
*
* Revision 1.121 2002/06/04 00:25:31 robertj
* Fixed incorrectly initialised trace indent, thanks Artis Kugevics
*
* Revision 1.120 2002/04/24 01:11:05 robertj
* Fixed problem with PTRACE_BLOCK indent level being correct across threads.
*
* Revision 1.119 2002/01/26 15:05:35 yurik
* Removed extra ifdefs
*
* Revision 1.118 2002/01/23 04:45:50 craigs
* Added copy Constructors for PSemaphore, PMutex and PSyncPoint
*
* Revision 1.117 2001/12/08 00:22:37 robertj
* Prevented assert if doing SetUserName() with empty string.
*
* Revision 1.116 2001/11/23 06:59:00 robertj
* Added PProcess::SetUserName() function for effective user changes.
*
* Revision 1.115 2001/10/26 04:20:25 craigs
* Changed housekeeping thread to be Normal priority to avoide starvation
* of PTimer dependent threads
*
* Revision 1.114 2001/10/23 05:42:48 robertj
* Fixed bug in retry loop waiting for termination, applies only to heavily
* laoded Win98 class machines.
*
* Revision 1.113 2001/10/07 16:05:59 yurik
* Removed MFC dependency
*
* Revision 1.112 2001/09/11 03:27:46 robertj
* Improved error processing on high level protocol failures, usually
* caused by unexpected shut down of a socket.
*
* Revision 1.111 2001/09/10 02:51:23 robertj
* Major change to fix problem with error codes being corrupted in a
* PChannel when have simultaneous reads and writes in threads.
*
* Revision 1.110 2001/08/07 03:20:39 robertj
* Fixed close of DLL so flagged as closed, thanks Stefan Ditscheid.
*
* Revision 1.109 2001/07/09 01:35:48 robertj
* Added name to housekeeper thread.
*
* Revision 1.108 2001/06/01 04:03:05 yurik
* Removed dependency on obsolete function
*
* Revision 1.107 2001/05/29 00:49:18 robertj
* Added ability to put in a printf %x in thread name to get thread object
* address into user settable thread name.
*
* Revision 1.106 2001/05/10 15:21:30 yurik
* Fixed bug in PSemaphore::Signal(), courtesy of Dave Cassel, dcassel@cyberfone.com.
* Also Refined thread priorities for WinCE.
*
* Revision 1.105 2001/05/04 05:42:23 yurik
* GetUserName for Pocket PC (Windows CE 3.0) implemented
*
* Revision 1.104 2001/04/26 06:07:34 yurik
* UI improvements
*
* Revision 1.103 2001/04/15 03:38:42 yurik
* Removed shutdown flag. Use IsTerminated() instead
*
* Revision 1.102 2001/04/14 04:54:03 yurik
* Added process shutdown flag
*
* Revision 1.101 2001/03/24 05:52:42 robertj
* Added Windows 98 and ME to GetOSName()
* Added build number to GetOSVersion()
*
* Revision 1.100 2001/03/03 00:55:02 yurik
* Proper fix for filetime routines used in guid calc done for WinCE
*
* Revision 1.99 2001/02/13 06:55:21 robertj
* Fixed problem with operator= in PDirectory class, part of larger change previously made.
*
* Revision 1.98 2001/01/30 06:37:52 yurik
* Modification submitted by Dave Cassel, dcassel@cyberfone.com
* DC: ARM, x86em were content without this change, but SH4 insisted
*
* Revision 1.97 2001/01/29 01:19:32 robertj
* Fixed Win32 compile broken by WinCE changes.
*
* Revision 1.96 2001/01/28 01:50:46 yurik
* WinCE port-related. System version check and new semaphore code
*
* Revision 1.95 2001/01/24 06:44:35 yurik
* Windows CE port-related changes
*
* Revision 1.94 2000/11/02 01:31:11 robertj
* Fixed problem with PSemaphore::WillBlock actually locking semaphore.
*
* Revision 1.93 2000/10/20 05:31:53 robertj
* Added function to change auto delete flag on a thread.
*
* Revision 1.92 2000/08/25 08:07:48 robertj
* Added Windows 2000 to operating system reporting.
*
* Revision 1.91 2000/04/05 02:50:18 robertj
* Added microseconds to PTime class.
*
* Revision 1.90 2000/03/29 04:31:59 robertj
* Removed assertion on terminating terminated thread, this is really OK.
*
* Revision 1.89 2000/03/04 08:07:27 robertj
* Fixed problem with window not appearing when assert on GUI based win32 apps.
*
* Revision 1.88 2000/02/29 12:26:15 robertj
* Added named threads to tracing, thanks to Dave Harvey
*
* Revision 1.87 2000/01/06 14:09:42 robertj
* Fixed problems with starting up timers,losing up to 10 seconds
*
* Revision 1.86 1999/11/18 02:22:53 robertj
* Fixed bug in GetErrorText() occasionally returning incorrect empty string, thanks Ulrich Findeisen
*
* Revision 1.85 1999/07/06 13:37:07 robertj
* Fixed bug in PThread::IsSuspended(), returned exactly the opposite state!
*
* Revision 1.84 1999/07/06 04:46:01 robertj
* Fixed being able to case an unsigned to a PTimeInterval.
* Improved resolution of PTimer::Tick() to be millisecond accurate.
*
* Revision 1.83 1999/03/09 10:30:19 robertj
* Fixed ability to have PMEMORY_CHECK on/off on both debug/release versions.
*
* Revision 1.82 1999/03/09 08:19:15 robertj
* Adjustment found during documentation frenzy.
*
* Revision 1.81 1999/02/12 01:01:57 craigs
* Fixed problem with linking static versions of libraries
*
* Revision 1.80 1999/01/30 14:28:25 robertj
* Added GetOSConfigDir() function.
*
* Revision 1.79 1999/01/16 02:00:29 robertj
* Added hardware description funtion.
*
* Revision 1.78 1998/12/04 10:10:47 robertj
* Added virtual for determining if process is a service. Fixes linkage problem.
*
* Revision 1.77 1998/11/30 07:31:18 robertj
* New directory structure
* Fission of file into pipe.cxx, winserial.cxx and wincfg.cxx
*
* Revision 1.76 1998/11/26 10:35:08 robertj
* Improved support of FAT32 and large NTFS volumes in GetFreeSpace().
*
* Revision 1.75 1998/11/20 03:17:19 robertj
* Added thread WaitForTermination() function.
*
* Revision 1.74 1998/11/19 05:19:53 robertj
* Bullet proofed WaitForMultipleObjects under 95.
*
* Revision 1.73 1998/11/02 10:07:20 robertj
* Added capability of pip output to go to stdout/stderr.
*
* Revision 1.72 1998/10/31 12:50:47 robertj
* Removed ability to start threads immediately, race condition with vtable (Main() function).
* Rearranged PPipChannel functions to help with multi-platform-ness.
*
* Revision 1.71 1998/10/29 11:29:20 robertj
* Added ability to set environment in sub-process.
*
* Revision 1.70 1998/10/28 00:59:12 robertj
* Fixed problem when reading standard error from pipe channel, no terminating null on string.
*
* Revision 1.69 1998/10/26 09:11:31 robertj
* Added ability to separate out stdout from stderr on pipe channels.
*
* Revision 1.68 1998/10/15 02:20:26 robertj
* Added message for connection aborted error.
*
* Revision 1.67 1998/10/13 14:13:36 robertj
* Removed uneeded heap allocation.
*
* Revision 1.66 1998/09/24 03:30:59 robertj
* Added open software license.
*
* Revision 1.65 1998/09/18 13:56:20 robertj
* Added support of REG_BINARY registry types in PConfig class.
*
* Revision 1.64 1998/08/20 06:05:28 robertj
* Allowed Win32 class to be used in other compilation modules
*
* Revision 1.63 1998/04/01 01:52:42 robertj
* Fixed problem with NoAutoDelete threads.
*
* Revision 1.62 1998/03/29 06:16:56 robertj
* Rearranged initialisation sequence so PProcess descendent constructors can do "things".
*
* Revision 1.61 1998/03/27 10:52:39 robertj
* Fixed crash bug in win95 OSR2 GetVolumeSpace().
* Fixed error 87 problem with threads.
* Fixed GetVolumeSpace() when UNC used.
*
* Revision 1.60 1998/03/20 03:19:49 robertj
* Added special classes for specific sepahores, PMutex and PSyncPoint.
*
* Revision 1.59 1998/03/17 10:17:09 robertj
* Fixed problem with viewing registry entries where the section ends with a \.
*
* Revision 1.58 1998/03/09 11:17:38 robertj
* FAT32 compatibility
*
* Revision 1.57 1998/03/05 12:48:37 robertj
* Fixed bug in get free space on volume.
* Added cluster size.
* MemCheck fixes.
*
* Revision 1.56 1998/02/16 00:10:45 robertj
* Added function to open a URL in a browser.
* Added functions to validate characters in a filename.
*
* Revision 1.55 1998/01/26 00:57:09 robertj
* Fixed uninitialised source in PConfig when getting environment.
*
* Revision 1.54 1997/08/28 12:50:21 robertj
* Fixed race condition in cleaning up threads on application termination.
*
* Revision 1.53 1997/08/21 13:27:41 robertj
* Attempt to fix very slight possibility of endless loop in housekeeping thread.
*
* Revision 1.52 1997/08/21 12:44:56 robertj
* Removed extension from DLL "short" name.
*
* Revision 1.51 1997/08/07 11:57:42 robertj
* Added ability to get registry data from other applications and anywhere in system registry.
*
* Revision 1.50 1997/08/04 10:38:43 robertj
* Fixed infamous error 87 assert failure in housekeeping thread.
*
* Revision 1.49 1997/07/14 11:47:22 robertj
* Added "const" to numerous variables.
*
* Revision 1.48 1997/06/16 13:15:53 robertj
* Added function to get a dyna-link libraries name.
*
* Revision 1.47 1997/06/08 04:42:41 robertj
* Added DLL file extension string function.
*
* Revision 1.46 1997/03/28 04:36:30 robertj
* Added assert for error in thread cleanup wait.
*
* Revision 1.45 1997/02/05 11:50:58 robertj
* Changed current process function to return reference and validate objects descendancy.
*
* Revision 1.44 1997/01/12 04:24:16 robertj
* Added function to get disk size and free space.
*
* Revision 1.43 1997/01/01 11:17:06 robertj
* Added implementation for PPipeChannel::GetReturnCode and PPipeChannel::IsRunning
*
* Revision 1.44 1996/12/29 13:05:03 robertj
* Added wait and abort for pipe channel commands.
* Added setting of error codes on status error.
*
* Revision 1.43 1996/12/29 02:53:13 craigs
* Added implementation for PPipeChannel::GetReturnCode and
* PPipeChannel::IsRunning
*
* Revision 1.42 1996/12/17 13:13:05 robertj
* Fixed win95 support for registry security code,
*
* Revision 1.41 1996/12/17 11:00:28 robertj
* Fixed register entry security access control lists.
*
* Revision 1.40 1996/11/16 10:52:48 robertj
* Fixed bug in PPipeChannel test for open channel, win95 support.
* Put time back to C function as run time library bug fixed now.
*
* Revision 1.39 1996/11/04 03:36:31 robertj
* Added extra error message for UDP packet truncated.
*
* Revision 1.38 1996/10/26 01:42:51 robertj
* Added more translations for winsock error codes to standard error codes.
*
* Revision 1.37 1996/10/14 03:11:25 robertj
* Changed registry key so when reading only opens in ReadOnly mode.
*
* Revision 1.36 1996/10/08 13:03:47 robertj
* Added new error messages.
*
* Revision 1.35 1996/08/08 10:03:43 robertj
* Fixed static error text returned when no osError value.
*
* Revision 1.34 1996/07/27 04:05:31 robertj
* Created static version of ConvertOSError().
* Created static version of GetErrorText().
* Changed thread creation to use C library function instead of direct WIN32.
* Fixed bug in auto-deleting the housekeeping thread.
*
* Revision 1.33 1996/07/20 05:34:05 robertj
* Fixed order of registry section tree traversal so can delete whole trees.
*
* Revision 1.32 1996/06/28 13:24:33 robertj
* Fixed enumeration of sections to recurse into registry tree.
*
* Revision 1.31 1996/06/17 11:38:58 robertj
* Fixed memory leak on termination of threads.
*
* Revision 1.30 1996/06/13 13:32:13 robertj
* Rewrite of auto-delete threads, fixes Windows95 total crash.
*
* Revision 1.29 1996/06/10 09:54:35 robertj
* Fixed Win95 compatibility for semaphores.
*
* Revision 1.28 1996/05/30 11:48:51 robertj
* Fixed error on socket timeout to return "Timed Out".
*
* Revision 1.27 1996/05/23 10:05:36 robertj
* Fixed bug in PConfig::GetBoolean().
* Changed PTimeInterval millisecond access function so can get int64.
* Moved service process code into separate module.
*
* Revision 1.26 1996/04/29 12:23:22 robertj
* Fixed ability to access GDI stuff from subthreads.
* Added function to return process ID.
*
* Revision 1.25 1996/04/17 12:09:30 robertj
* Added service dependencies.
* Started win95 support.
*
* Revision 1.24 1996/04/09 03:33:58 robertj
* Fixed bug in incorrect report of timeout on socket read.
*
* Revision 1.23 1996/04/01 13:33:19 robertj
* Fixed bug in install of service, incorrectly required installation before install.
*
* Revision 1.22 1996/03/31 09:10:33 robertj
* Added use of "CurrentVersion" key in registry.
* Added version display to service process.
* Added another socket error text message.
*
* Revision 1.21 1996/03/12 11:31:39 robertj
* Moved PProcess destructor to platform dependent code.
* Fixed bug in deleting Event Viewer registry entry for service process.
*
* Revision 1.20 1996/03/10 13:16:49 robertj
* Implemented system version functions.
*
* Revision 1.19 1996/03/04 13:07:33 robertj
* Allowed for auto deletion of threads on termination.
*
* Revision 1.18 1996/02/25 11:15:29 robertj
* Added platform dependent Construct function to PProcess.
*
* Revision 1.17 1996/02/25 03:12:48 robertj
* Added consts to all GetXxxx functions in PConfig.
* Fixed bug in PTime::GetTimeZone(), incorrect sign!
* Fixed problem with PConfig get functions and their WIN32 types should be
* able to interchange strings and numbers.
*
* Revision 1.16 1996/02/19 13:53:21 robertj
* Fixed error reporting for winsock classes.
*
* Revision 1.15 1996/02/15 14:54:06 robertj
* Compensated for C library bug in time().
*
* Revision 1.14 1996/02/08 12:30:41 robertj
* Time zone changes.
* Added OS identification strings to PProcess.
*
* Revision 1.13 1996/01/28 02:56:56 robertj
* Fixed bug in PFilePath functions for if path ends in a directory separator.
* Made sure all directory separators are correct character in normalised path.
*
* Revision 1.12 1996/01/23 13:25:21 robertj
* Added time zones.
* Fixed bug if daylight savings indication.
*
* Revision 1.11 1996/01/02 12:58:33 robertj
* Fixed copy of directories.
* Changed process construction mechanism.
* Made service process "common".
*
* Revision 1.10 1995/12/10 12:05:48 robertj
* Changes to main() startup mechanism to support Mac.
* Moved error code for specific WIN32 and MS-DOS versions.
* Added WIN32 registry support for PConfig objects.
* Added asserts in WIN32 semaphores.
*
* Revision 1.9 1995/11/21 11:53:24 robertj
* Added timeout on semaphore wait.
*
* Revision 1.8 1995/10/14 15:13:04 robertj
* Fixed bug in WIN32 service command line parameters.
*
* Revision 1.7 1995/08/24 12:42:33 robertj
* Changed PChannel so not a PContainer.
* Rewrote PSerialChannel::Read yet again so can break out of I/O.
*
* Revision 1.6 1995/07/02 01:26:52 robertj
* Changed thread internal variables.
* Added service process support for NT.
*
* Revision 1.5 1995/06/17 01:03:08 robertj
* Added NT service process type.
*
* Revision 1.4 1995/06/04 12:48:52 robertj
* Fixed bug in directory path creation.
* Fixed bug in comms channel open error.
*
* Revision 1.3 1995/04/25 11:33:54 robertj
* Fixed Borland compiler warnings.
*
* Revision 1.2 1995/03/22 13:56:18 robertj
* Fixed directory handle value check for closing directory.
*
// Revision 1.1 1995/03/14 12:45:20 robertj
// Initial revision
//
*/
#include <ptlib.h>
#include <process.h>
#include <ptlib/msos/ptlib/debstrm.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#ifndef _WIN32_WCE
#ifdef _MSC_VER
#pragma comment(lib, "mpr.lib")
#endif
#endif
#define new PNEW
#if defined(_WIN32_DCOM)
#include <objbase.h>
#ifdef _MSC_VER
#pragma comment(lib, _OLE_LIB)
#endif
#endif
#include "../common/pglobalstatic.cxx"
///////////////////////////////////////////////////////////////////////////////
// PTime
PTime::PTime()
{
// Magic constant to convert epoch from 1601 to 1970
static const PInt64 delta = ((PInt64)369*365+(369/4)-3)*24*60*60U;
static const PInt64 scale = 10000000;
PInt64 timestamp;
#ifndef _WIN32_WCE
GetSystemTimeAsFileTime((LPFILETIME)×tamp);
#else
SYSTEMTIME SystemTime;
GetSystemTime(&SystemTime);
SystemTimeToFileTime(&SystemTime, (LPFILETIME)×tamp);
#endif
theTime = (time_t)(timestamp/scale - delta);
microseconds = (long)(timestamp%scale/10);
}
#ifdef UNICODE
static void PWIN32GetLocaleInfo(LCID Locale,LCTYPE LCType,LPSTR lpLCData,int cchData)
{
TCHAR* pw = new TCHAR[cchData+1];
GetLocaleInfo(Locale,LCType,pw,cchData);
lpLCData[0]=0;
WideCharToMultiByte(GetACP(), 0, pw, -1, lpLCData, cchData, NULL, NULL);
}
#else
#define PWIN32GetLocaleInfo GetLocaleInfo
#endif
PString PTime::GetTimeSeparator()
{
PString str;
PWIN32GetLocaleInfo(GetUserDefaultLCID(), LOCALE_STIME, str.GetPointer(100), 99);
str.MakeMinimumSize();
return str;
}
BOOL PTime::GetTimeAMPM()
{
char str[2];
PWIN32GetLocaleInfo(GetUserDefaultLCID(), LOCALE_ITIME, str, sizeof(str));
return str[0] == '0';
}
PString PTime::GetTimeAM()
{
PString str;
PWIN32GetLocaleInfo(GetUserDefaultLCID(), LOCALE_S1159, str.GetPointer(100), 99);
str.MakeMinimumSize();
return str;
}
PString PTime::GetTimePM()
{
PString str;
PWIN32GetLocaleInfo(GetUserDefaultLCID(), LOCALE_S2359, str.GetPointer(100), 99);
str.MakeMinimumSize();
return str;
}
PString PTime::GetDayName(Weekdays dayOfWeek, NameType type)
{
PString str;
// Of course Sunday is 6 and Monday is 1...
PWIN32GetLocaleInfo(GetUserDefaultLCID(), (dayOfWeek+6)%7 +
(type == Abbreviated ? LOCALE_SABBREVDAYNAME1 : LOCALE_SDAYNAME1),
str.GetPointer(100), 99);
str.MakeMinimumSize();
return str;
}
PString PTime::GetDateSeparator()
{
PString str;
PWIN32GetLocaleInfo(GetUserDefaultLCID(), LOCALE_SDATE, str.GetPointer(100), 99);
str.MakeMinimumSize();
return str;
}
PString PTime::GetMonthName(Months month, NameType type)
{
PString str;
PWIN32GetLocaleInfo(GetUserDefaultLCID(), month-1 +
(type == Abbreviated ? LOCALE_SABBREVMONTHNAME1 : LOCALE_SMONTHNAME1),
str.GetPointer(100), 99);
str.MakeMinimumSize();
return str;
}
PTime::DateOrder PTime::GetDateOrder()
{
char str[2];
PWIN32GetLocaleInfo(GetUserDefaultLCID(), LOCALE_IDATE, str, sizeof(str));
return (DateOrder)(str[0] - '0');
}
BOOL PTime::IsDaylightSavings()
{
TIME_ZONE_INFORMATION tz;
DWORD result = GetTimeZoneInformation(&tz);
PAssertOS(result != 0xffffffff);
return result == TIME_ZONE_ID_DAYLIGHT;
}
int PTime::GetTimeZone(TimeZoneType type)
{
TIME_ZONE_INFORMATION tz;
PAssertOS(GetTimeZoneInformation(&tz) != 0xffffffff);
if (type == DaylightSavings)
tz.Bias += tz.DaylightBias;
return -tz.Bias;
}
PString PTime::GetTimeZoneString(TimeZoneType type)
{
TIME_ZONE_INFORMATION tz;
PAssertOS(GetTimeZoneInformation(&tz) != 0xffffffff);
return (const WORD *)(type == StandardTime ? tz.StandardName : tz.DaylightName);
}
///////////////////////////////////////////////////////////////////////////////
// PTimeInterval
static unsigned GetDivisor()
{
LARGE_INTEGER frequency;
if (QueryPerformanceFrequency(&frequency))
return (unsigned)frequency.QuadPart/1000;
return 0;
}
PTimeInterval PTimer::Tick()
{
static unsigned divisor = GetDivisor();
if (divisor == 0)
return (int)(GetTickCount()&0x7fffffff);
LARGE_INTEGER count;
QueryPerformanceCounter(&count);
return count.QuadPart/divisor;
}
unsigned PTimer::Resolution()
{
LARGE_INTEGER frequency;
if (QueryPerformanceFrequency(&frequency) && frequency.QuadPart >= 1000)
return 1;
DWORD err = GetLastError();
#ifndef _WIN32_WCE
DWORD timeAdjustment;
DWORD timeIncrement;
BOOL timeAdjustmentDisabled;
if (GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, &timeAdjustmentDisabled))
return timeIncrement/10000;
err = GetLastError();
#endif
return 55;
}
///////////////////////////////////////////////////////////////////////////////
// Directories
void PDirectory::Construct()
{
hFindFile = INVALID_HANDLE_VALUE;
fileinfo.cFileName[0] = '\0';
PCaselessString::AssignContents(CreateFullPath(*this, TRUE));
}
void PDirectory::CopyContents(const PDirectory & dir)
{
scanMask = dir.scanMask;
hFindFile = INVALID_HANDLE_VALUE;
fileinfo = dir.fileinfo;
}
BOOL PDirectory::Open(int newScanMask)
{
scanMask = newScanMask;
#ifdef UNICODE
USES_CONVERSION;
hFindFile = FindFirstFile(A2T(operator+("*.*")), &fileinfo);
#else
hFindFile = FindFirstFile(operator+("*.*"), &fileinfo);
#endif
if (hFindFile == INVALID_HANDLE_VALUE)
return FALSE;
return Filtered() ? Next() : TRUE;
}
BOOL PDirectory::Next()
{
if (hFindFile == INVALID_HANDLE_VALUE)
return FALSE;
do {
if (!FindNextFile(hFindFile, &fileinfo))
return FALSE;
} while (Filtered());
return TRUE;
}
PCaselessString PDirectory::GetEntryName() const
{
return fileinfo.cFileName;
}
BOOL PDirectory::IsSubDir() const
{
return (fileinfo.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) != 0;
}
PCaselessString PDirectory::GetVolume() const
{
#ifdef _WIN32_WCE
return PCaselessString("\\");
#else
char volName[100];
PAssertOS(GetVolumeInformation(NULL, volName, sizeof(volName), NULL, NULL, NULL, NULL, 0));
return PCaselessString(volName);
#endif
}
void PDirectory::Close()
{
if (hFindFile != INVALID_HANDLE_VALUE) {
FindClose(hFindFile);
hFindFile = INVALID_HANDLE_VALUE;
}
}
PString PDirectory::CreateFullPath(const PString & path, BOOL isDirectory)
{
if (path.IsEmpty() && !isDirectory)
return path;
#ifdef _WIN32_WCE //doesn't support Current Directory so the path suppose to be full
PString fullpath=path;
PINDEX len = fullpath.GetLength();
#else
PString partialpath = path;
// Look for special case of "\c:\" at start of string as some generalised
// directory processing algorithms have a habit of adding a leading
// PDIR_SEPARATOR as it would be for Unix.
if (partialpath.NumCompare("\\\\\\") == EqualTo ||
(partialpath.GetLength() > 3 &&
partialpath[0] == PDIR_SEPARATOR &&
partialpath[2] == ':'))
partialpath.Delete(0, 1);
LPSTR dummy;
PString fullpath;
PINDEX len = (PINDEX)GetFullPathName(partialpath,
_MAX_PATH, fullpath.GetPointer(_MAX_PATH), &dummy);
#endif
if (isDirectory && len > 0 && fullpath[len-1] != PDIR_SEPARATOR)
fullpath += PDIR_SEPARATOR;
PINDEX pos = 0;
while ((pos = fullpath.Find('/', pos)) != P_MAX_INDEX)
fullpath[pos] = PDIR_SEPARATOR;
return fullpath;
}
typedef BOOL (WINAPI *GetDiskFreeSpaceExType)(LPCTSTR lpDirectoryName,
PULARGE_INTEGER lpFreeBytesAvailableToCaller,
PULARGE_INTEGER lpTotalNumberOfBytes,
PULARGE_INTEGER lpTotalNumberOfFreeBytes);
BOOL PDirectory::GetVolumeSpace(PInt64 & total, PInt64 & free, DWORD & clusterSize) const
{
clusterSize = 512;
total = free = ULONG_MAX;
PString root;
if ((*this)[1] == ':')
root = Left(3);
else if (theArray[0] == '\\' && theArray[1] == '\\') {
PINDEX backslash = Find('\\', 2);
if (backslash != P_MAX_INDEX) {
backslash = Find('\\', backslash+1);
if (backslash != P_MAX_INDEX)
root = Left(backslash+1);
}
}
if (root.IsEmpty())
return FALSE;
#ifndef _WIN32_WCE
BOOL needTotalAndFree = TRUE;
static GetDiskFreeSpaceExType GetDiskFreeSpaceEx =
(GetDiskFreeSpaceExType)GetProcAddress(LoadLibrary("KERNEL32.DLL"), "GetDiskFreeSpaceExA");
if (GetDiskFreeSpaceEx != NULL) {
ULARGE_INTEGER freeBytesAvailableToCaller;
ULARGE_INTEGER totalNumberOfBytes;
ULARGE_INTEGER totalNumberOfFreeBytes;
if (GetDiskFreeSpaceEx(root,
&freeBytesAvailableToCaller,
&totalNumberOfBytes,
&totalNumberOfFreeBytes)) {
total = totalNumberOfBytes.QuadPart;
free = totalNumberOfFreeBytes.QuadPart;
needTotalAndFree = FALSE;
}
}
clusterSize = 0;
char fsName[100];
if (GetVolumeInformation(root, NULL, 0, NULL, NULL, NULL, fsName, sizeof(fsName))) {
if (stricmp(fsName, "FAT32") == 0) {
clusterSize = 4096; // Cannot use GetDiskFreeSpace() results for FAT32
if (!needTotalAndFree)
return TRUE;
}
}
DWORD sectorsPerCluster; // address of sectors per cluster
DWORD bytesPerSector; // address of bytes per sector
DWORD numberOfFreeClusters; // address of number of free clusters
DWORD totalNumberOfClusters; // address of total number of clusters
if (!GetDiskFreeSpace(root,
§orsPerCluster,
&bytesPerSector,
&numberOfFreeClusters,
&totalNumberOfClusters))
{
if (root[0] != '\\' || ::GetLastError() != ERROR_NOT_SUPPORTED)
return FALSE;
PString drive = "A:";
while (WNetAddConnection(root, NULL, drive) != NO_ERROR) {
if (GetLastError() != ERROR_ALREADY_ASSIGNED)
return FALSE;
drive[0]++;
}
BOOL ok = GetDiskFreeSpace(drive+'\\',
§orsPerCluster,
&bytesPerSector,
&numberOfFreeClusters,
&totalNumberOfClusters);
WNetCancelConnection(drive, TRUE);
if (!ok)
return FALSE;
}
if (needTotalAndFree) {
free = numberOfFreeClusters*sectorsPerCluster*bytesPerSector;
total = totalNumberOfClusters*sectorsPerCluster*bytesPerSector;
}
if (clusterSize == 0)
clusterSize = bytesPerSector*sectorsPerCluster;
return TRUE;
#elif _WIN32_WCE < 300
USES_CONVERSION;
ULARGE_INTEGER freeBytesAvailableToCaller;
ULARGE_INTEGER totalNumberOfBytes;
ULARGE_INTEGER totalNumberOfFreeBytes;
if (GetDiskFreeSpaceEx(A2T(root),
&freeBytesAvailableToCaller,
&totalNumberOfBytes,
&totalNumberOfFreeBytes))
{
total = totalNumberOfBytes.QuadPart;
free = totalNumberOfFreeBytes.QuadPart;
clusterSize = 512; //X3
return TRUE;
}
return FALSE;
#else
return FALSE;
#endif
}
///////////////////////////////////////////////////////////////////////////////
// PFilePath
static PString IllegalFilenameCharacters =
"\\/:*?\"<>|"
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\0x10"
"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f";
BOOL PFilePath::IsValid(char c)
{
return IllegalFilenameCharacters.Find(c) == P_MAX_INDEX;
}
BOOL PFilePath::IsValid(const PString & str)
{
return str != "." && str != ".." &&
str.FindOneOf(IllegalFilenameCharacters) == P_MAX_INDEX;
}
///////////////////////////////////////////////////////////////////////////////
// PChannel
PString PChannel::GetErrorText(Errors lastError, int osError)
{
if (osError == 0) {
if (lastError == NoError)
return PString();
static int const errors[NumNormalisedErrors] = {
0, ENOENT, EEXIST, ENOSPC, EACCES, EBUSY, EINVAL, ENOMEM, EBADF, EAGAIN, EINTR,
WSAEMSGSIZE|PWIN32ErrorFlag, EIO, 0x1000000|PWIN32ErrorFlag
};
osError = errors[lastError];
}
#ifndef _WIN32_WCE
if (osError > 0 && osError < _sys_nerr && _sys_errlist[osError][0] != '\0')
return _sys_errlist[osError];
#endif
if ((osError & PWIN32ErrorFlag) == 0)
return psprintf("C runtime error %u", osError);
DWORD err = osError & ~PWIN32ErrorFlag;
static const struct {
DWORD id;
const char * msg;
} win32_errlist[] = {
{ ERROR_FILE_NOT_FOUND, "File not found" },
{ ERROR_PATH_NOT_FOUND, "Path not found" },
{ ERROR_ACCESS_DENIED, "Access denied" },
{ ERROR_NOT_ENOUGH_MEMORY, "Not enough memory" },
{ ERROR_INVALID_FUNCTION, "Invalid function" },
{ WSAEADDRINUSE, "Address in use" },
{ WSAENETDOWN, "Network subsystem failed" },
{ WSAEISCONN, "Socket is already connected" },
{ WSAENETUNREACH, "Network unreachable" },
{ WSAEHOSTUNREACH, "Host unreachable" },
{ WSAECONNREFUSED, "Connection refused" },
{ WSAEINVAL, "Invalid operation" },
{ WSAENOTCONN, "Socket not connected" },
{ WSAECONNABORTED, "Connection aborted" },
{ WSAECONNRESET, "Connection reset" },
{ WSAESHUTDOWN, "Connection shutdown" },
{ WSAENOTSOCK, "Socket closed or invalid" },
{ WSAETIMEDOUT, "Timed out" },
{ WSAEMSGSIZE, "Message larger than buffer" },
{ WSAEWOULDBLOCK, "Would block" },
{ 0x1000000, "High level protocol failure" }
};
for (PINDEX i = 0; i < PARRAYSIZE(win32_errlist); i++)
if (win32_errlist[i].id == err)
return win32_errlist[i].msg;
return psprintf("WIN32 error %u", err);
}
BOOL PChannel::ConvertOSError(int status, Errors & lastError, int & osError)
{
if (status >= 0) {
lastError = NoError;
osError = 0;
return TRUE;
}
if (status != -2)
osError = errno;
else {
osError = GetLastError();
switch (osError) {
case ERROR_INVALID_HANDLE :
case WSAEBADF :
osError = EBADF;
break;
case ERROR_INVALID_PARAMETER :
case WSAEINVAL :
osError = EINVAL;
break;
case ERROR_ACCESS_DENIED :
case WSAEACCES :
osError = EACCES;
break;
case ERROR_NOT_ENOUGH_MEMORY :
osError = ENOMEM;
break;
case WSAEINTR :
osError = EINTR;
break;
case WSAEMSGSIZE :
osError |= PWIN32ErrorFlag;
lastError = BufferTooSmall;
return FALSE;
case WSAEWOULDBLOCK :
case WSAETIMEDOUT :
osError |= PWIN32ErrorFlag;
lastError = Timeout;
return FALSE;
default :
osError |= PWIN32ErrorFlag;
}
}
switch (osError) {
case 0 :
lastError = NoError;
return TRUE;
case ENOENT :
lastError = NotFound;
break;
case EEXIST :
lastError = FileExists;
break;
case EACCES :
lastError = AccessDenied;
break;
case ENOMEM :
lastError = NoMemory;
break;
case ENOSPC :
lastError = DiskFull;
break;
case EINVAL :
lastError = BadParameter;
break;
case EBADF :
lastError = NotOpen;
break;
case EAGAIN :
lastError = Timeout;
break;
case EINTR :
lastError = Interrupted;
break;
default :
lastError = Miscellaneous;
}
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
// PWin32Overlapped
PWin32Overlapped::PWin32Overlapped()
{
memset(this, 0, sizeof(*this));
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
}
PWin32Overlapped::~PWin32Overlapped()
{
if (hEvent != NULL)
CloseHandle(hEvent);
}
void PWin32Overlapped::Reset()
{
Offset = OffsetHigh = 0;
if (hEvent != NULL)
ResetEvent(hEvent);
}
///////////////////////////////////////////////////////////////////////////////
// Threads
UINT __stdcall PThread::MainFunction(void * threadPtr)
{
PThread * thread = (PThread *)PAssertNULL(threadPtr);
thread->SetThreadName(thread->GetThreadName());
PProcess & process = PProcess::Current();
/*
* Removed this code because it causes a linear increase
* in thread startup time when there are many (< 500) threads.
* If this functionality is needed, call Win32AttachThreadInput
* after the thread has been started
*
#ifndef _WIN32_WCE
AttachThreadInput(thread->threadId, ((PThread&)process).threadId, TRUE);
AttachThreadInput(((PThread&)process).threadId, thread->threadId, TRUE);
#endif
*/
process.activeThreadMutex.Wait();
process.activeThreads.SetAt(thread->threadId, thread);
process.activeThreadMutex.Signal();
process.SignalTimerChange();
#if defined(_WIN32_DCOM)
::CoInitializeEx(NULL, COINIT_MULTITHREADED);
#endif
thread->Main();
#if defined(_WIN32_DCOM)
::CoUninitialize();
#endif
return 0;
}
void PThread::Win32AttachThreadInput()
{
#ifndef _WIN32_WCE
PProcess & process = PProcess::Current();
::AttachThreadInput(threadId, ((PThread&)process).threadId, TRUE);
::AttachThreadInput(((PThread&)process).threadId, threadId, TRUE);
#endif
}
PThread::PThread(PINDEX stackSize,
AutoDeleteFlag deletion,
Priority priorityLevel,
const PString & name)
: threadName(name)
{
PAssert(stackSize > 0, PInvalidParameter);
originalStackSize = stackSize;
autoDelete = deletion == AutoDeleteThread;
#ifndef _WIN32_WCE
threadHandle = (HANDLE)_beginthreadex(NULL, stackSize, MainFunction,
this, CREATE_SUSPENDED, &threadId);
#else
threadHandle = CreateThread(NULL, stackSize,
(LPTHREAD_START_ROUTINE)MainFunction,
this, CREATE_SUSPENDED, (LPDWORD) &threadId);
#endif
PAssertOS(threadHandle != NULL);
SetPriority(priorityLevel);
traceBlockIndentLevel = 0;
if (autoDelete) {
PProcess & process = PProcess::Current();
process.deleteThreadMutex.Wait();
process.autoDeleteThreads.Append(this);
process.deleteThreadMutex.Signal();
}
}
PThread::~PThread()
{
if (originalStackSize <= 0)
return;
PProcess & process = PProcess::Current();
process.activeThreadMutex.Wait();
process.activeThreads.SetAt(threadId, NULL);
process.activeThreadMutex.Signal();
if (!IsTerminated())
Terminate();
if (threadHandle != NULL)
CloseHandle(threadHandle);
}
void PThread::Restart()
{
PAssert(IsTerminated(), "Cannot restart running thread");
#ifndef _WIN32_WCE
threadHandle = (HANDLE)_beginthreadex(NULL,
originalStackSize, MainFunction, this, 0, &threadId);
#else
threadHandle = CreateThread(NULL, originalStackSize,
(LPTHREAD_START_ROUTINE) MainFunction,
this, 0, (LPDWORD) &threadId);
#endif
PAssertOS(threadHandle != NULL);
}
void PThread::Terminate()
{
PAssert(originalStackSize > 0, PLogicError);
if (Current() == this)
ExitThread(0);
else
TerminateThread(threadHandle, 1);
}
BOOL PThread::IsTerminated() const
{
if (this == PThread::Current())
return FALSE;
return WaitForTermination(0);
}
void PThread::WaitForTermination() const
{
WaitForTermination(PMaxTimeInterval);
}
BOOL PThread::WaitForTermination(const PTimeInterval & maxWait) const
{
if ((this == PThread::Current()) || threadHandle == NULL) {
PTRACE(2, "WaitForTermination short circuited");
return TRUE;
}
DWORD result;
PINDEX retries = 10;
while ((result = WaitForSingleObject(threadHandle, maxWait.GetInterval())) != WAIT_TIMEOUT) {
if (result == WAIT_OBJECT_0)
return TRUE;
if (::GetLastError() != ERROR_INVALID_HANDLE) {
PAssertAlways(POperatingSystemError);
return TRUE;
}
if (retries == 0)
return TRUE;
retries--;
}
return FALSE;
}
void PThread::Suspend(BOOL susp)
{
PAssert(!IsTerminated(), "Operation on terminated thread");
if (susp)
SuspendThread(threadHandle);
else
Resume();
}
void PThread::Resume()
{
PAssert(!IsTerminated(), "Operation on terminated thread");
ResumeThread(threadHandle);
}
BOOL PThread::IsSuspended() const
{
SuspendThread(threadHandle);
return ResumeThread(threadHandle) > 1;
}
void PThread::SetAutoDelete(AutoDeleteFlag deletion)
{
PAssert(deletion != AutoDeleteThread || this != &PProcess::Current(), PLogicError);
PProcess & process = PProcess::Current();
if (autoDelete && deletion != AutoDeleteThread) {
process.deleteThreadMutex.Wait();
process.autoDeleteThreads.DisallowDeleteObjects();
process.autoDeleteThreads.Remove(this);
process.autoDeleteThreads.AllowDeleteObjects();
process.deleteThreadMutex.Signal();
}
else if (!autoDelete && deletion == AutoDeleteThread) {
process.deleteThreadMutex.Wait();
process.autoDeleteThreads.Append(this);
process.deleteThreadMutex.Signal();
}
autoDelete = deletion == AutoDeleteThread;
}
#if !defined(_WIN32_WCE) || (_WIN32_WCE < 300)
#define PTHREAD_PRIORITY_LOWEST THREAD_PRIORITY_LOWEST
#define PTHREAD_PRIORITY_BELOW_NORMAL THREAD_PRIORITY_BELOW_NORMAL
#define PTHREAD_PRIORITY_NORMAL THREAD_PRIORITY_NORMAL
#define PTHREAD_PRIORITY_ABOVE_NORMAL THREAD_PRIORITY_ABOVE_NORMAL
#define PTHREAD_PRIORITY_HIGHEST THREAD_PRIORITY_HIGHEST
#else
#define PTHREAD_PRIORITY_LOWEST 243
#define PTHREAD_PRIORITY_BELOW_NORMAL 245
#define PTHREAD_PRIORITY_NORMAL 247
#define PTHREAD_PRIORITY_ABOVE_NORMAL 249
#define PTHREAD_PRIORITY_HIGHEST 251
#endif
void PThread::SetPriority(Priority priorityLevel)
{
PAssert(!IsTerminated(), "Operation on terminated thread");
static int const priorities[NumPriorities] = {
PTHREAD_PRIORITY_LOWEST,
PTHREAD_PRIORITY_BELOW_NORMAL,
PTHREAD_PRIORITY_NORMAL,
PTHREAD_PRIORITY_ABOVE_NORMAL,
PTHREAD_PRIORITY_HIGHEST
};
SetThreadPriority(threadHandle, priorities[priorityLevel]);
}
PThread::Priority PThread::GetPriority() const
{
PAssert(!IsTerminated(), "Operation on terminated thread");
switch (GetThreadPriority(threadHandle)) {
case PTHREAD_PRIORITY_LOWEST :
return LowestPriority;
case PTHREAD_PRIORITY_BELOW_NORMAL :
return LowPriority;
case PTHREAD_PRIORITY_NORMAL :
return NormalPriority;
case PTHREAD_PRIORITY_ABOVE_NORMAL :
return HighPriority;
case PTHREAD_PRIORITY_HIGHEST :
return HighestPriority;
}
PAssertAlways(POperatingSystemError);
return LowestPriority;
}
void PThread::Yield()
{
::Sleep(0);
}
void PThread::InitialiseProcessThread()
{
originalStackSize = 0;
autoDelete = FALSE;
threadHandle = GetCurrentThread();
threadId = GetCurrentThreadId();
((PProcess *)this)->activeThreads.DisallowDeleteObjects();
((PProcess *)this)->activeThreads.SetAt(threadId, this);
traceBlockIndentLevel = 0;
}
PThread * PThread::Current()
{
PProcess & process = PProcess::Current();
process.activeThreadMutex.Wait();
PThread * thread = process.activeThreads.GetAt(GetCurrentThreadId());
process.activeThreadMutex.Signal();
return thread;
}
///////////////////////////////////////////////////////////////////////////////
// PProcess::TimerThread
PProcess::HouseKeepingThread::HouseKeepingThread()
: PThread(1000, NoAutoDeleteThread, NormalPriority, "PWLib Housekeeper")
{
Resume();
}
void PProcess::HouseKeepingThread::Main()
{
PProcess & process = PProcess::Current();
for (;;) {
// collect a list of thread handles to check, and clean up
// handles for threads that disappeared without telling us
process.deleteThreadMutex.Wait();
HANDLE handles[MAXIMUM_WAIT_OBJECTS];
DWORD numHandles = 1;
DWORD dwFlags;
handles[0] = breakBlock.GetHandle();
for (PINDEX i = 0; i < process.autoDeleteThreads.GetSize(); i++) {
PThread & thread = process.autoDeleteThreads[i];
if (thread.IsTerminated())
process.autoDeleteThreads.RemoveAt(i--);
else {
handles[numHandles] = thread.GetHandle();
// make sure we don't put invalid handles into the list
#ifndef _WIN32_WCE
if (GetHandleInformation(handles[numHandles], &dwFlags) == 0) {
PTRACE(2, "Refused to put invalid handle into wait list");
}
else
#endif
// don't put the handle for the current process in the list
if (handles[numHandles] != process.GetHandle()) {
numHandles++;
if (numHandles >= MAXIMUM_WAIT_OBJECTS)
break;
}
}
}
process.deleteThreadMutex.Signal();
PTimeInterval nextTimer = process.timers.Process();
DWORD delay;
if (nextTimer == PMaxTimeInterval)
delay = INFINITE;
else if (nextTimer > 1000)
delay = 1000;
else
delay = nextTimer.GetInterval();
DWORD result;
int retries = 100;
while ((result = WaitForMultipleObjects(numHandles, handles, FALSE, delay)) == WAIT_FAILED) {
// if we get an invalid handle error, than assume this is because a thread ended between
// creating the handle list and testing it. So, cleanup the list before calling
// WaitForMultipleObjects again
if (::GetLastError() == ERROR_INVALID_HANDLE)
break;
// sometimes WaitForMultipleObjects fails. No idea why, so allow some retries
else {
retries--;
if (retries <= 0)
break;
}
}
}
}
void PProcess::SignalTimerChange()
{
deleteThreadMutex.Wait();
if (houseKeeper == NULL)
houseKeeper = new HouseKeepingThread;
else
houseKeeper->breakBlock.Signal();
deleteThreadMutex.Signal();
}
///////////////////////////////////////////////////////////////////////////////
// PProcess
PProcess::~PProcess()
{
// do whatever needs to shutdown
PreShutdown();
Sleep(100); // Give threads time to die a natural death
// Get rid of the house keeper (majordomocide)
delete houseKeeper;
// OK, if there are any left we get really insistent...
activeThreadMutex.Wait();
for (PINDEX i = 0; i < activeThreads.GetSize(); i++) {
PThread & thread = activeThreads.GetDataAt(i);
if (this != &thread && !thread.IsTerminated())
TerminateThread(thread.GetHandle(), 1); // With extreme prejudice
}
activeThreadMutex.Signal();
deleteThreadMutex.Wait();
autoDeleteThreads.RemoveAll();
deleteThreadMutex.Signal();
#if PMEMORY_CHECK || _DEBUG
extern void PWaitOnExitConsoleWindow();
PWaitOnExitConsoleWindow();
#endif
}
PString PProcess::GetOSClass()
{
return "Windows";
}
PString PProcess::GetOSName()
{
OSVERSIONINFO info;
info.dwOSVersionInfoSize = sizeof(info);
GetVersionEx(&info);
switch (info.dwPlatformId) {
case VER_PLATFORM_WIN32s :
return "32s";
#ifdef VER_PLATFORM_WIN32_CE
case VER_PLATFORM_WIN32_CE :
return "CE";
#endif
case VER_PLATFORM_WIN32_WINDOWS :
if (info.dwMinorVersion < 10)
return "95";
if (info.dwMinorVersion < 90)
return "98";
return "ME";
case VER_PLATFORM_WIN32_NT :
if (info.dwMajorVersion < 5)
return "NT";
else if (info.dwMinorVersion == 0)
return "2000";
else if (info.dwMinorVersion == 1)
return "XP";
else
return "Server 2003";
}
return "?";
}
PString PProcess::GetOSHardware()
{
SYSTEM_INFO info;
GetSystemInfo(&info);
switch (info.wProcessorArchitecture) {
case PROCESSOR_ARCHITECTURE_INTEL :
switch (info.dwProcessorType) {
case PROCESSOR_INTEL_386 :
return "i386";
case PROCESSOR_INTEL_486 :
return "i486";
case PROCESSOR_INTEL_PENTIUM :
return "i586";
}
return "iX86";
case PROCESSOR_ARCHITECTURE_MIPS :
return "mips";
case PROCESSOR_ARCHITECTURE_ALPHA :
return "alpha";
case PROCESSOR_ARCHITECTURE_PPC :
return "ppc";
}
return "?";
}
PString PProcess::GetOSVersion()
{
OSVERSIONINFO info;
info.dwOSVersionInfoSize = sizeof(info);
GetVersionEx(&info);
WORD wBuildNumber = (WORD)info.dwBuildNumber;
return psprintf(wBuildNumber > 0 ? "v%u.%u.%u" : "v%u.%u",
info.dwMajorVersion, info.dwMinorVersion, wBuildNumber);
}
PDirectory PProcess::GetOSConfigDir()
{
#ifdef _WIN32_WCE
return PString("\\Windows");
#else
OSVERSIONINFO info;
info.dwOSVersionInfoSize = sizeof(info);
GetVersionEx(&info);
char dir[_MAX_PATH];
if (info.dwPlatformId != VER_PLATFORM_WIN32_NT) {
PAssertOS(GetWindowsDirectory(dir, sizeof(dir)) != 0);
return dir;
}
PAssertOS(GetSystemDirectory(dir, sizeof(dir)) != 0);
PDirectory sysdir = dir;
return sysdir; //+ "drivers\\etc";
#endif
}
PString PProcess::GetUserName() const
{
PString username;
unsigned long size = 50;
#ifndef _WIN32_WCE
::GetUserName(username.GetPointer((PINDEX)size), &size);
#else
TCHAR wcsuser[50] = {0};
HKEY hKeyComm, hKeyIdent;
RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Comm"), 0, 0, &hKeyComm);
RegOpenKeyEx(hKeyComm, _T("Ident"), 0, 0, &hKeyIdent);
DWORD dwType = REG_SZ; DWORD dw = 50;
if( ERROR_SUCCESS != RegQueryValueEx(
hKeyIdent, _T("Username"), NULL, &dwType, (LPBYTE) wcsuser, &dw)
|| !*wcsuser )
{
RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Ident"), 0, 0, &hKeyIdent);
dw = 50L;
if( ERROR_SUCCESS == RegQueryValueEx(
hKeyIdent, _T("Name"), NULL, &dwType, (LPBYTE) wcsuser, &dw))
wcscat( wcsuser, _T(" user") ); // like "Pocket_PC User"
}
USES_CONVERSION;
username = T2A(wcsuser);
#endif
username.MakeMinimumSize();
return username;
}
BOOL PProcess::SetUserName(const PString & username, BOOL)
{
if (username.IsEmpty())
return FALSE;
PAssertAlways(PUnimplementedFunction);
return FALSE;
}
PString PProcess::GetGroupName() const
{
return "Users";
}
BOOL PProcess::SetGroupName(const PString & groupname, BOOL)
{
if (groupname.IsEmpty())
return FALSE;
PAssertAlways(PUnimplementedFunction);
return FALSE;
}
DWORD PProcess::GetProcessID() const
{
return GetCurrentProcessId();
}
BOOL PProcess::IsServiceProcess() const
{
return FALSE;
}
BOOL PProcess::IsGUIProcess() const
{
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
// PSemaphore
PSemaphore::PSemaphore(HANDLE h)
{
handle = h;
PAssertOS(handle != NULL);
}
PSemaphore::PSemaphore(unsigned initial, unsigned maxCount)
{
initialVal = initial;
maxCountVal = maxCount;
if (initial > maxCount)
initial = maxCount;
handle = CreateSemaphore(NULL, initial, maxCount, NULL);
PAssertOS(handle != NULL);
}
PSemaphore::PSemaphore(const PSemaphore & sem)
{
initialVal = sem.GetInitialVal();
maxCountVal = sem.GetMaxCountVal();
if (initialVal > maxCountVal)
initialVal = maxCountVal;
handle = CreateSemaphore(NULL, initialVal, maxCountVal, NULL);
PAssertOS(handle != NULL);
}
PSemaphore::~PSemaphore()
{
if (handle != NULL)
PAssertOS(CloseHandle(handle));
}
void PSemaphore::Wait()
{
PAssertOS(WaitForSingleObject(handle, INFINITE) != WAIT_FAILED);
}
BOOL PSemaphore::Wait(const PTimeInterval & timeout)
{
DWORD result = WaitForSingleObject(handle, timeout.GetInterval());
PAssertOS(result != WAIT_FAILED);
return result != WAIT_TIMEOUT;
}
void PSemaphore::Signal()
{
if (!ReleaseSemaphore(handle, 1, NULL))
PAssertOS(GetLastError() != ERROR_INVALID_HANDLE);
SetLastError(ERROR_SUCCESS);
}
BOOL PSemaphore::WillBlock() const
{
PSemaphore * unconst = (PSemaphore *)this;
if (!unconst->Wait(0))
return TRUE;
unconst->Signal();
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
// PTimedMutex
PTimedMutex::PTimedMutex()
: PSemaphore(::CreateMutex(NULL, FALSE, NULL))
{
}
PTimedMutex::PTimedMutex(const PTimedMutex &)
: PSemaphore(::CreateMutex(NULL, FALSE, NULL))
{
}
void PTimedMutex::Signal()
{
PAssertOS(::ReleaseMutex(handle));
}
///////////////////////////////////////////////////////////////////////////////
// PSyncPoint
PSyncPoint::PSyncPoint()
: PSemaphore(::CreateEvent(NULL, FALSE, FALSE, NULL))
{
}
PSyncPoint::PSyncPoint(const PSyncPoint &)
: PSemaphore(::CreateEvent(NULL, FALSE, FALSE, NULL))
{
}
void PSyncPoint::Signal()
{
PAssertOS(::SetEvent(handle));
}
///////////////////////////////////////////////////////////////////////////////
// PDynaLink
PDynaLink::PDynaLink()
{
_hDLL = NULL;
}
PDynaLink::PDynaLink(const PString & name)
{
Open(name);
}
PDynaLink::~PDynaLink()
{
Close();
}
PString PDynaLink::GetExtension()
{
return ".DLL";
}
BOOL PDynaLink::Open(const PString & name)
{
#ifdef UNICODE
USES_CONVERSION;
_hDLL = LoadLibrary(A2T(name));
#else
_hDLL = LoadLibrary(name);
#endif
return _hDLL != NULL;
}
void PDynaLink::Close()
{
if (_hDLL != NULL) {
FreeLibrary(_hDLL);
_hDLL = NULL;
}
}
BOOL PDynaLink::IsLoaded() const
{
return _hDLL != NULL;
}
PString PDynaLink::GetName(BOOL full) const
{
PFilePathString str;
if (_hDLL != NULL)
{
#ifdef UNICODE
TCHAR path[_MAX_PATH];
GetModuleFileName(_hDLL, path, _MAX_PATH-1);
str=PString(path);
#else
GetModuleFileName(_hDLL, str.GetPointer(_MAX_PATH), _MAX_PATH-1);
#endif
if (!full)
{
str.Delete(0, str.FindLast('\\')+1);
PINDEX pos = str.Find(".DLL");
if (pos != P_MAX_INDEX)
str.Delete(pos, P_MAX_INDEX);
}
}
str.MakeMinimumSize();
return str;
}
BOOL PDynaLink::GetFunction(PINDEX index, Function & func)
{
if (_hDLL == NULL)
return FALSE;
#ifndef _WIN32_WCE
FARPROC p = GetProcAddress(_hDLL, (LPSTR)(DWORD)LOWORD(index));
#else
FARPROC p = GetProcAddress(_hDLL, (LPTSTR)(DWORD)LOWORD(index));
#endif
if (p == NULL)
return FALSE;
func = (Function)p;
return TRUE;
}
BOOL PDynaLink::GetFunction(const PString & name, Function & func)
{
if (_hDLL == NULL)
return FALSE;
#ifdef UNICODE
USES_CONVERSION;
FARPROC p = GetProcAddress(_hDLL, A2T(name));
#else
FARPROC p = GetProcAddress(_hDLL, name);
#endif
if (p == NULL)
return FALSE;
func = (Function)p;
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
// PDebugStream
PDebugStream::PDebugStream()
: ostream(&buffer)
{
}
PDebugStream::Buffer::Buffer()
{
setg(buffer, buffer, &buffer[sizeof(buffer)-2]);
setp(buffer, &buffer[sizeof(buffer)-2]);
}
int PDebugStream::Buffer::overflow(int c)
{
int bufSize = pptr() - pbase();
if (c != EOF) {
*pptr() = (char)c;
bufSize++;
}
if (bufSize != 0) {
char * p = pbase();
setp(p, epptr());
p[bufSize] = '\0';
#ifdef UNICODE
USES_CONVERSION;
OutputDebugString(A2T(p));
#else
OutputDebugString(p);
#endif
}
return 0;
}
int PDebugStream::Buffer::underflow()
{
return EOF;
}
int PDebugStream::Buffer::sync()
{
return overflow(EOF);
}
// End Of File ///////////////////////////////////////////////////////////////
syntax highlighted by Code2HTML, v. 0.9.1