1 /*
2  * win32.cxx
3  *
4  * Miscellaneous implementation of classes for Win32
5  *
6  * Portable Windows Library
7  *
8  * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
9  *
10  * The contents of this file are subject to the Mozilla Public License
11  * Version 1.0 (the "License"); you may not use this file except in
12  * compliance with the License. You may obtain a copy of the License at
13  * http://www.mozilla.org/MPL/
14  *
15  * Software distributed under the License is distributed on an "AS IS"
16  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17  * the License for the specific language governing rights and limitations
18  * under the License.
19  *
20  * The Original Code is Portable Windows Library.
21  *
22  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
23  *
24  * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
25  * All Rights Reserved.
26  *
27  * Contributor(s): ______________________________________.
28  *
29  * $Revision: 29085 $
30  * $Author: rjongbloed $
31  * $Date: 2013-02-13 14:47:59 -0600 (Wed, 13 Feb 2013) $
32  */
33 
34 #include <ptlib.h>
35 #include <ptlib/pprocess.h>
36 
37 #include <ptlib/msos/ptlib/debstrm.h>
38 #include <winsock2.h>
39 #include <ws2tcpip.h>
40 
41 #ifdef __MINGW32__
42 #include <process.h>
43 #endif
44 
45 #if defined(_MSC_VER) && !defined(_WIN32_WCE)
46   #include <process.h>
47   #pragma comment(lib, "mpr.lib")
48 #endif
49 
50 #include <ptlib/msos/ptlib/ipsock.h>
51 
52 #if defined(P_WIN_COM)
53   #include <objbase.h>
54   #ifdef _MSC_VER
55     #pragma comment(lib, "ole32.lib")
56   #endif
57 #endif
58 
59 
60 #define new PNEW
61 
62 
63 ///////////////////////////////////////////////////////////////////////////////
64 // PTime
65 
SetCurrentTime()66 void PTime::SetCurrentTime()
67 {
68   FILETIME timestamp;
69 
70 #ifndef _WIN32_WCE
71   GetSystemTimeAsFileTime(&timestamp);
72 #else
73   SYSTEMTIME SystemTime;
74   GetSystemTime(&SystemTime);
75   SystemTimeToFileTime(&SystemTime, &timestamp);
76 #endif
77 
78   SetFromFileTime(timestamp);
79 }
80 
81 
PTime(const FILETIME & timestamp)82 PTime::PTime(const FILETIME & timestamp)
83 {
84   SetFromFileTime(timestamp);
85 }
86 
87 
SetFromFileTime(const FILETIME & timestamp)88 void PTime::SetFromFileTime(const FILETIME & timestamp)
89 {
90   // Magic constant to convert epoch from 1601 to 1970
91   static const ULONGLONG delta = ((PInt64)369*365+(369/4)-3)*24*60*60U;
92   static const ULONGLONG scale = 10000000;
93 
94   ULARGE_INTEGER i;
95   i.HighPart = timestamp.dwHighDateTime;
96   i.LowPart = timestamp.dwLowDateTime;
97 
98   theTime = (time_t)(i.QuadPart/scale - delta);
99   microseconds = (long)(i.QuadPart%scale/10);
100 }
101 
102 #ifdef UNICODE
PWIN32GetLocaleInfo(LCID Locale,LCTYPE LCType,LPSTR lpLCData,int cchData)103 static void PWIN32GetLocaleInfo(LCID Locale,LCTYPE LCType,LPSTR lpLCData,int cchData)
104 {
105   TCHAR* pw = new TCHAR[cchData+1];
106   GetLocaleInfo(Locale,LCType,pw,cchData);
107   lpLCData[0]=0;
108   WideCharToMultiByte(GetACP(), 0, pw, -1, lpLCData, cchData, NULL, NULL);
109 }
110 #else
111 
112 #define PWIN32GetLocaleInfo GetLocaleInfo
113 
114 #endif
115 
116 
117 
GetTimeSeparator()118 PString PTime::GetTimeSeparator()
119 {
120   PString str;
121   PWIN32GetLocaleInfo(GetUserDefaultLCID(), LOCALE_STIME, str.GetPointer(100), 99);
122   str.MakeMinimumSize();
123   return str;
124 }
125 
126 
GetTimeAMPM()127 PBoolean PTime::GetTimeAMPM()
128 {
129   char str[2];
130   PWIN32GetLocaleInfo(GetUserDefaultLCID(), LOCALE_ITIME, str, sizeof(str));
131   return str[0] == '0';
132 }
133 
134 
GetTimeAM()135 PString PTime::GetTimeAM()
136 {
137   PString str;
138   PWIN32GetLocaleInfo(GetUserDefaultLCID(), LOCALE_S1159, str.GetPointer(100), 99);
139   str.MakeMinimumSize();
140   return str;
141 }
142 
143 
GetTimePM()144 PString PTime::GetTimePM()
145 {
146   PString str;
147   PWIN32GetLocaleInfo(GetUserDefaultLCID(), LOCALE_S2359, str.GetPointer(100), 99);
148   str.MakeMinimumSize();
149   return str;
150 }
151 
152 
GetDayName(Weekdays dayOfWeek,NameType type)153 PString PTime::GetDayName(Weekdays dayOfWeek, NameType type)
154 {
155   PString str;
156   // Of course Sunday is 6 and Monday is 1...
157   PWIN32GetLocaleInfo(GetUserDefaultLCID(), (dayOfWeek+6)%7 +
158           (type == Abbreviated ? LOCALE_SABBREVDAYNAME1 : LOCALE_SDAYNAME1),
159           str.GetPointer(100), 99);
160   str.MakeMinimumSize();
161   return str;
162 }
163 
164 
GetDateSeparator()165 PString PTime::GetDateSeparator()
166 {
167   PString str;
168   PWIN32GetLocaleInfo(GetUserDefaultLCID(), LOCALE_SDATE, str.GetPointer(100), 99);
169   str.MakeMinimumSize();
170   return str;
171 }
172 
173 
GetMonthName(Months month,NameType type)174 PString PTime::GetMonthName(Months month, NameType type)
175 {
176   PString str;
177   PWIN32GetLocaleInfo(GetUserDefaultLCID(), month-1 +
178       (type == Abbreviated ? LOCALE_SABBREVMONTHNAME1 : LOCALE_SMONTHNAME1),
179       str.GetPointer(100), 99);
180   str.MakeMinimumSize();
181   return str;
182 }
183 
184 
GetDateOrder()185 PTime::DateOrder PTime::GetDateOrder()
186 {
187   char str[2];
188   PWIN32GetLocaleInfo(GetUserDefaultLCID(), LOCALE_IDATE, str, sizeof(str));
189   return (DateOrder)(str[0] - '0');
190 }
191 
192 
IsDaylightSavings()193 PBoolean PTime::IsDaylightSavings()
194 {
195   TIME_ZONE_INFORMATION tz;
196   DWORD result = GetTimeZoneInformation(&tz);
197   PAssertOS(result != 0xffffffff);
198   return result == TIME_ZONE_ID_DAYLIGHT;
199 }
200 
201 
GetTimeZone(TimeZoneType type)202 int PTime::GetTimeZone(TimeZoneType type)
203 {
204   TIME_ZONE_INFORMATION tz;
205   PAssertOS(GetTimeZoneInformation(&tz) != 0xffffffff);
206   if (type == DaylightSavings)
207     tz.Bias += tz.DaylightBias;
208   return -tz.Bias;
209 }
210 
211 
GetTimeZoneString(TimeZoneType type)212 PString PTime::GetTimeZoneString(TimeZoneType type)
213 {
214   TIME_ZONE_INFORMATION tz;
215   PAssertOS(GetTimeZoneInformation(&tz) != 0xffffffff);
216   return (const wchar_t *)(type == StandardTime ? tz.StandardName : tz.DaylightName);
217 }
218 
219 
220 ///////////////////////////////////////////////////////////////////////////////
221 // PTimeInterval
222 
GetDivisor()223 static unsigned GetDivisor()
224 {
225   LARGE_INTEGER frequency;
226   if (QueryPerformanceFrequency(&frequency))
227     return (unsigned)frequency.QuadPart/1000;
228 
229   return 0;
230 }
231 
Tick()232 PTimeInterval PTimer::Tick()
233 {
234   static unsigned divisor = GetDivisor();
235 
236   if (divisor == 0)
237     return (int)(GetTickCount()&0x7fffffff);
238 
239   LARGE_INTEGER count;
240   QueryPerformanceCounter(&count);
241   return count.QuadPart/divisor;
242 }
243 
244 
Resolution()245 unsigned PTimer::Resolution()
246 {
247   LARGE_INTEGER frequency;
248   if (QueryPerformanceFrequency(&frequency) && frequency.QuadPart >= 1000)
249     return 1;
250 
251 #ifndef _WIN32_WCE
252   DWORD timeAdjustment;
253   DWORD timeIncrement;
254   BOOL timeAdjustmentDisabled;
255   if (GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, &timeAdjustmentDisabled))
256     return timeIncrement/10000;
257 #endif
258 
259   return 55;
260 }
261 
262 
263 ///////////////////////////////////////////////////////////////////////////////
264 // Directories
265 
Construct()266 void PDirectory::Construct()
267 {
268   hFindFile = INVALID_HANDLE_VALUE;
269   fileinfo.cFileName[0] = '\0';
270   PCaselessString::AssignContents(CreateFullPath(*this, PTrue));
271 }
272 
273 
CopyContents(const PDirectory & dir)274 void PDirectory::CopyContents(const PDirectory & dir)
275 {
276   scanMask  = dir.scanMask;
277   hFindFile = INVALID_HANDLE_VALUE;
278   fileinfo  = dir.fileinfo;
279 }
280 
281 
Open(int newScanMask)282 PBoolean PDirectory::Open(int newScanMask)
283 {
284   scanMask = newScanMask;
285   PVarString wildcard = *this + "*.*";
286 
287   hFindFile = FindFirstFile(wildcard, &fileinfo);
288   if (hFindFile == INVALID_HANDLE_VALUE)
289     return PFalse;
290 
291   return Filtered() ? Next() : PTrue;
292 }
293 
294 
Next()295 PBoolean PDirectory::Next()
296 {
297   if (hFindFile == INVALID_HANDLE_VALUE)
298     return PFalse;
299 
300   do {
301     if (!FindNextFile(hFindFile, &fileinfo))
302       return PFalse;
303   } while (Filtered());
304 
305   return PTrue;
306 }
307 
308 
GetEntryName() const309 PCaselessString PDirectory::GetEntryName() const
310 {
311   return fileinfo.cFileName;
312 }
313 
314 
IsSubDir() const315 PBoolean PDirectory::IsSubDir() const
316 {
317   return (fileinfo.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) != 0;
318 }
319 
320 
GetVolume() const321 PCaselessString PDirectory::GetVolume() const
322 {
323 #ifdef _WIN32_WCE
324   return PCaselessString("\\");
325 #else
326   char volName[100];
327   PAssertOS(GetVolumeInformation(NULL, volName, sizeof(volName), NULL, NULL, NULL, NULL, 0));
328   return PCaselessString(volName);
329 #endif
330 }
331 
332 
Close()333 void PDirectory::Close()
334 {
335   if (hFindFile != INVALID_HANDLE_VALUE) {
336     FindClose(hFindFile);
337     hFindFile = INVALID_HANDLE_VALUE;
338   }
339 }
340 
341 
CreateFullPath(const PString & path,PBoolean isDirectory)342 PString PDirectory::CreateFullPath(const PString & path, PBoolean isDirectory)
343 {
344   if (path.IsEmpty() && !isDirectory)
345     return path;
346 
347 #ifdef _WIN32_WCE //doesn't support Current Directory so the path suppose to be full
348   PString fullpath=path;
349   PINDEX len = fullpath.GetLength();
350 
351 #else
352   PString partialpath = path;
353 
354   // Look for special case of "\c:\" at start of string as some generalised
355   // directory processing algorithms have a habit of adding a leading
356   // PDIR_SEPARATOR as it would be for Unix.
357   if (partialpath.NumCompare("\\\\\\") == EqualTo ||
358         (partialpath.GetLength() > 3 &&
359          partialpath[0] == PDIR_SEPARATOR &&
360          partialpath[2] == ':'))
361     partialpath.Delete(0, 1);
362 
363   LPSTR dummy;
364   PString fullpath;
365   PINDEX len = (PINDEX)GetFullPathName(partialpath,
366                            _MAX_PATH, fullpath.GetPointer(_MAX_PATH), &dummy);
367 #endif
368   if (isDirectory && len > 0 && fullpath[len-1] != PDIR_SEPARATOR)
369     fullpath += PDIR_SEPARATOR;
370   PINDEX pos = 0;
371   while ((pos = fullpath.Find('/', pos)) != P_MAX_INDEX)
372     fullpath[pos] = PDIR_SEPARATOR;
373   return fullpath;
374 }
375 
376 
377 typedef PBoolean (WINAPI *GetDiskFreeSpaceExType)(LPCTSTR lpDirectoryName,
378                                               PULARGE_INTEGER lpFreeBytesAvailableToCaller,
379                                               PULARGE_INTEGER lpTotalNumberOfBytes,
380                                               PULARGE_INTEGER lpTotalNumberOfFreeBytes);
381 
382 
GetVolumeSpace(PInt64 & total,PInt64 & free,DWORD & clusterSize) const383 PBoolean PDirectory::GetVolumeSpace(PInt64 & total, PInt64 & free, DWORD & clusterSize) const
384 {
385   clusterSize = 512;
386   total = free = ULONG_MAX;
387 
388   PString root;
389   if ((*this)[1] == ':')
390     root = Left(3);
391   else if (theArray[0] == '\\' && theArray[1] == '\\') {
392     PINDEX backslash = Find('\\', 2);
393     if (backslash != P_MAX_INDEX) {
394       backslash = Find('\\', backslash+1);
395       if (backslash != P_MAX_INDEX)
396         root = Left(backslash+1);
397     }
398   }
399 
400   if (root.IsEmpty())
401     return PFalse;
402 
403 #ifndef _WIN32_WCE
404   PBoolean needTotalAndFree = PTrue;
405 
406   static GetDiskFreeSpaceExType GetDiskFreeSpaceEx =
407         (GetDiskFreeSpaceExType)GetProcAddress(LoadLibrary("KERNEL32.DLL"), "GetDiskFreeSpaceExA");
408   if (GetDiskFreeSpaceEx != NULL) {
409     ULARGE_INTEGER freeBytesAvailableToCaller;
410     ULARGE_INTEGER totalNumberOfBytes;
411     ULARGE_INTEGER totalNumberOfFreeBytes;
412     if (GetDiskFreeSpaceEx(root,
413                            &freeBytesAvailableToCaller,
414                            &totalNumberOfBytes,
415                            &totalNumberOfFreeBytes)) {
416       total = totalNumberOfBytes.QuadPart;
417       free = totalNumberOfFreeBytes.QuadPart;
418       needTotalAndFree = PFalse;
419     }
420   }
421 
422   clusterSize = 0;
423   char fsName[100];
424   if (GetVolumeInformation(root, NULL, 0, NULL, NULL, NULL, fsName, sizeof(fsName))) {
425     if (strcasecmp(fsName, "FAT32") == 0) {
426       clusterSize = 4096; // Cannot use GetDiskFreeSpace() results for FAT32
427       if (!needTotalAndFree)
428         return PTrue;
429     }
430   }
431 
432   DWORD sectorsPerCluster;      // address of sectors per cluster
433   DWORD bytesPerSector;         // address of bytes per sector
434   DWORD numberOfFreeClusters;   // address of number of free clusters
435   DWORD totalNumberOfClusters;  // address of total number of clusters
436 
437   if (!GetDiskFreeSpace(root,
438                         &sectorsPerCluster,
439                         &bytesPerSector,
440                         &numberOfFreeClusters,
441                         &totalNumberOfClusters))
442 {
443     if (root[0] != '\\' || ::GetLastError() != ERROR_NOT_SUPPORTED)
444       return PFalse;
445 
446     PString drive = "A:";
447     while (WNetAddConnection(root, NULL, drive) != NO_ERROR) {
448       if (::GetLastError() != ERROR_ALREADY_ASSIGNED)
449         return PFalse;
450       drive[0]++;
451     }
452     PBoolean ok = GetDiskFreeSpace(drive+'\\',
453                                &sectorsPerCluster,
454                                &bytesPerSector,
455                                &numberOfFreeClusters,
456                                &totalNumberOfClusters);
457     WNetCancelConnection(drive, PTrue);
458     if (!ok)
459       return PFalse;
460   }
461 
462   if (needTotalAndFree) {
463     free = numberOfFreeClusters*sectorsPerCluster*bytesPerSector;
464     total = totalNumberOfClusters*sectorsPerCluster*bytesPerSector;
465   }
466 
467   if (clusterSize == 0)
468     clusterSize = bytesPerSector*sectorsPerCluster;
469 
470   return PTrue;
471 #elif _WIN32_WCE < 300
472   USES_CONVERSION;
473     ULARGE_INTEGER freeBytesAvailableToCaller;
474     ULARGE_INTEGER totalNumberOfBytes;
475     ULARGE_INTEGER totalNumberOfFreeBytes;
476     if (GetDiskFreeSpaceEx(A2T(root),
477                            &freeBytesAvailableToCaller,
478                            &totalNumberOfBytes,
479                            &totalNumberOfFreeBytes))
480   {
481     total = totalNumberOfBytes.QuadPart;
482     free = totalNumberOfFreeBytes.QuadPart;
483     clusterSize = 512; //X3
484     return PTrue;
485   }
486   return PFalse;
487 #else
488   return PFalse;
489 #endif
490 }
491 
492 
493 ///////////////////////////////////////////////////////////////////////////////
494 // PFilePath
495 
496 static char const IllegalFilenameCharacters[] =
497   "\\/:*?\"<>|"
498   "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\0x10"
499   "\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f";
500 
IsValid(char c)501 PBoolean PFilePath::IsValid(char c)
502 {
503   return strchr(IllegalFilenameCharacters, c) == NULL;
504 }
505 
506 
IsValid(const PString & str)507 PBoolean PFilePath::IsValid(const PString & str)
508 {
509   return str != "." && str != ".." &&
510          str.FindOneOf(IllegalFilenameCharacters) == P_MAX_INDEX;
511 }
512 
513 
IsAbsolutePath(const PString & path)514 bool PFilePath::IsAbsolutePath(const PString & path)
515 {
516   return path.GetLength() > 2 && (path[1] == ':' || (path[0] == '\\' && path[1] == '\\'));
517 }
518 
519 
520 ///////////////////////////////////////////////////////////////////////////////
521 // PChannel
522 
GetErrorText(Errors lastError,int osError)523 PString PChannel::GetErrorText(Errors lastError, int osError)
524 {
525   if (osError == 0) {
526     if (lastError == NoError)
527       return PString();
528 
529     static int const errors[NumNormalisedErrors] = {
530       0, ENOENT, EEXIST, ENOSPC, EACCES, EBUSY, EINVAL, ENOMEM, EBADF, EAGAIN, EINTR,
531       WSAEMSGSIZE|PWIN32ErrorFlag, EIO, 0x1000000|PWIN32ErrorFlag
532     };
533     osError = errors[lastError];
534   }
535 #ifndef _WIN32_WCE
536   if (osError > 0 && osError < _sys_nerr && _sys_errlist[osError][0] != '\0')
537     return _sys_errlist[osError];
538 #endif
539   if ((osError & PWIN32ErrorFlag) == 0)
540     return psprintf("C runtime error %u", osError);
541 
542   DWORD err = osError & ~PWIN32ErrorFlag;
543 
544   static const struct {
545     DWORD id;
546     const char * msg;
547   } win32_errlist[] = {
548     { ERROR_FILE_NOT_FOUND,     "File not found" },
549     { ERROR_PATH_NOT_FOUND,     "Path not found" },
550     { ERROR_ACCESS_DENIED,      "Access denied" },
551     { ERROR_NOT_ENOUGH_MEMORY,  "Not enough memory" },
552     { ERROR_INVALID_FUNCTION,   "Invalid function" },
553     { WSAEADDRINUSE,            "Address in use" },
554     { WSAENETDOWN,              "Network subsystem failed" },
555     { WSAEISCONN,               "Socket is already connected" },
556     { WSAENETUNREACH,           "Network unreachable" },
557     { WSAEHOSTUNREACH,          "Host unreachable" },
558     { WSAECONNREFUSED,          "Connection refused" },
559     { WSAEINVAL,                "Invalid operation" },
560     { WSAENOTCONN,              "Socket not connected" },
561     { WSAECONNABORTED,          "Connection aborted" },
562     { WSAECONNRESET,            "Connection reset" },
563     { WSAESHUTDOWN,             "Connection shutdown" },
564     { WSAENOTSOCK,              "Socket closed or invalid" },
565     { WSAETIMEDOUT,             "Timed out" },
566     { WSAEMSGSIZE,              "Message larger than buffer" },
567     { WSAEWOULDBLOCK,           "Would block" },
568     { 0x1000000,                "High level protocol failure" }
569   };
570 
571   for (PINDEX i = 0; i < PARRAYSIZE(win32_errlist); i++)
572     if (win32_errlist[i].id == err)
573       return win32_errlist[i].msg;
574 
575   return psprintf("WIN32 error %u", err);
576 }
577 
578 
ConvertOSError(int status,Errors & lastError,int & osError)579 PBoolean PChannel::ConvertOSError(int status, Errors & lastError, int & osError)
580 {
581   if (status >= 0) {
582     lastError = NoError;
583     osError = 0;
584     return PTrue;
585   }
586 
587   if (status != -2)
588     osError = errno;
589   else {
590     osError = ::GetLastError();
591     switch (osError) {
592       case ERROR_INVALID_HANDLE :
593       case WSAEBADF :
594         osError = EBADF;
595         break;
596       case ERROR_INVALID_PARAMETER :
597       case WSAEINVAL :
598         osError = EINVAL;
599         break;
600       case ERROR_ACCESS_DENIED :
601       case WSAEACCES :
602         osError = EACCES;
603         break;
604       case ERROR_NOT_ENOUGH_MEMORY :
605         osError = ENOMEM;
606         break;
607       case WSAEINTR :
608         osError = EINTR;
609         break;
610       case WSAEINPROGRESS :
611         osError = EINPROGRESS;
612         break;
613       case WSAENOTSOCK :
614         osError = ENOTSOCK;
615         break;
616       case WSAEOPNOTSUPP :
617         osError = EOPNOTSUPP;
618         break;
619       case WSAEAFNOSUPPORT :
620         osError = EAFNOSUPPORT;
621         break;
622       case WSAEADDRINUSE :
623         osError = EADDRINUSE;
624         break;
625       case WSAEADDRNOTAVAIL :
626         osError = EADDRNOTAVAIL;
627         break;
628       case WSAENETDOWN :
629         osError = ENETDOWN;
630         break;
631       case WSAENETUNREACH :
632         osError = ENETUNREACH;
633         break;
634       case WSAENETRESET :
635         osError = ENETRESET;
636         break;
637       case WSAECONNABORTED :
638         osError = ECONNABORTED;
639         break;
640       case WSAECONNRESET :
641         osError = ECONNRESET;
642         break;
643       case WSAENOBUFS :
644         osError = ENOBUFS;
645         break;
646       case WSAEISCONN :
647         osError = EISCONN;
648         break;
649       case WSAENOTCONN :
650         osError = ENOTCONN;
651         break;
652       case WSAECONNREFUSED :
653         osError = ECONNREFUSED;
654         break;
655       case WSAEHOSTUNREACH :
656         osError = EHOSTUNREACH;
657         break;
658       case WSAEMSGSIZE :
659         osError = EMSGSIZE;
660         break;
661       case WSAEWOULDBLOCK :
662         osError = EWOULDBLOCK;
663         break;
664       case WSAETIMEDOUT :
665         osError = ETIMEDOUT;
666         break;
667       default :
668         osError |= PWIN32ErrorFlag;
669     }
670   }
671 
672   switch (osError) {
673     case 0 :
674       lastError = NoError;
675       return PTrue;
676     case ENOENT :
677       lastError = NotFound;
678       break;
679     case EEXIST :
680       lastError = FileExists;
681       break;
682     case EACCES :
683       lastError = AccessDenied;
684       break;
685     case ENOMEM :
686       lastError = NoMemory;
687       break;
688     case ENOSPC :
689       lastError = DiskFull;
690       break;
691     case EINVAL :
692       lastError = BadParameter;
693       break;
694     case EBADF :
695       lastError = NotOpen;
696       break;
697     case EAGAIN :
698     case ETIMEDOUT :
699     case EWOULDBLOCK :
700       lastError = Timeout;
701       break;
702     case EMSGSIZE :
703       lastError = BufferTooSmall;
704       break;
705     case EINTR :
706       lastError = Interrupted;
707       break;
708     default :
709       lastError = Miscellaneous;
710   }
711 
712   return PFalse;
713 }
714 
715 
716 ///////////////////////////////////////////////////////////////////////////////
717 // PWin32Overlapped
718 
PWin32Overlapped()719 PWin32Overlapped::PWin32Overlapped()
720 {
721   memset(this, 0, sizeof(*this));
722   hEvent = CreateEvent(NULL, PTrue, PFalse, NULL);
723 }
724 
~PWin32Overlapped()725 PWin32Overlapped::~PWin32Overlapped()
726 {
727   if (hEvent != NULL)
728     CloseHandle(hEvent);
729 }
730 
Reset()731 void PWin32Overlapped::Reset()
732 {
733   Offset = OffsetHigh = 0;
734   if (hEvent != NULL)
735     ResetEvent(hEvent);
736 }
737 
738 
739 ///////////////////////////////////////////////////////////////////////////////
740 // Threads
741 
MainFunction(void * threadPtr)742 UINT __stdcall PThread::MainFunction(void * threadPtr)
743 {
744   PThread * thread = (PThread *)PAssertNULL(threadPtr);
745   thread->SetThreadName(thread->GetThreadName());
746 
747   PProcess & process = PProcess::Current();
748 
749 /*
750  * Removed this code because it causes a linear increase
751  * in thread startup time when there are many (< 500) threads.
752  * If this functionality is needed, call Win32AttachThreadInput
753  * after the thread has been started
754  *
755 #ifndef _WIN32_WCE
756   AttachThreadInput(thread->threadId, ((PThread&)process).threadId, PTrue);
757   AttachThreadInput(((PThread&)process).threadId, thread->threadId, PTrue);
758 #endif
759 */
760 
761   process.m_activeThreadMutex.Wait();
762   process.m_activeThreads[thread->m_threadId] = thread;
763   process.m_activeThreadMutex.Signal();
764 
765   process.SignalTimerChange();
766 
767 #if defined(P_WIN_COM)
768   ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
769 #endif
770 
771   process.OnThreadStart(*thread);
772   thread->Main();
773   process.OnThreadEnded(*thread);
774 
775 #if defined(P_WIN_COM)
776   ::CoUninitialize();
777 #endif
778 
779 #if PTRACING
780   PTrace::Cleanup();
781 #endif
782 
783   return 0;
784 }
785 
786 
Win32AttachThreadInput()787 void PThread::Win32AttachThreadInput()
788 {
789 #ifndef _WIN32_WCE
790   PProcess & process = PProcess::Current();
791   ::AttachThreadInput(m_threadId, ((PThread&)process).m_threadId, PTrue);
792   ::AttachThreadInput(((PThread&)process).m_threadId, m_threadId, PTrue);
793 #endif
794 }
795 
796 
PThread(bool isProcess)797 PThread::PThread(bool isProcess)
798   : m_isProcess(isProcess)
799   , m_autoDelete(!isProcess)
800   , m_originalStackSize(0)
801   , m_threadId(GetCurrentThreadId())
802   , threadHandle(GetCurrentThread())
803 {
804   if (isProcess)
805     return;
806 
807   DuplicateHandle(GetCurrentProcess(), threadHandle, GetCurrentProcess(), &threadHandle, 0, 0, DUPLICATE_SAME_ACCESS);
808 
809   PProcess & process = PProcess::Current();
810 
811   process.m_activeThreadMutex.Wait();
812   process.m_activeThreads[m_threadId] = this;
813   process.m_activeThreadMutex.Signal();
814 
815   process.deleteThreadMutex.Wait();
816   process.autoDeleteThreads.Append(this);
817   process.deleteThreadMutex.Signal();
818 }
819 
820 
PThread(PINDEX stackSize,AutoDeleteFlag deletion,Priority priorityLevel,const PString & name)821 PThread::PThread(PINDEX stackSize,
822                  AutoDeleteFlag deletion,
823                  Priority priorityLevel,
824                  const PString & name)
825   : m_isProcess(false)
826   , m_autoDelete(deletion == AutoDeleteThread)
827   , m_originalStackSize(stackSize)
828   , m_threadName(name)
829 {
830   PAssert(stackSize > 0, PInvalidParameter);
831 
832 #ifndef _WIN32_WCE
833   threadHandle = (HANDLE)_beginthreadex(NULL, stackSize, MainFunction, this, CREATE_SUSPENDED, &m_threadId);
834 #else
835    threadHandle = CreateThread(NULL, stackSize,
836                        (LPTHREAD_START_ROUTINE)MainFunction, this, CREATE_SUSPENDED, (LPDWORD) &m_threadId);
837 #endif
838 
839   PAssertOS(threadHandle != NULL);
840 
841   SetPriority(priorityLevel);
842 
843   if (IsAutoDelete()) {
844     PProcess & process = PProcess::Current();
845     process.deleteThreadMutex.Wait();
846     process.autoDeleteThreads.Append(this);
847     process.deleteThreadMutex.Signal();
848   }
849 }
850 
851 
~PThread()852 PThread::~PThread()
853 {
854   if (m_isProcess)
855     return;
856 
857   CleanUp();
858 }
859 
860 
GetMillisecondFromFileTime(const FILETIME & ft)861 static ULONGLONG GetMillisecondFromFileTime(const FILETIME & ft)
862 {
863   ULARGE_INTEGER i;
864   i.HighPart = ft.dwHighDateTime;
865   i.LowPart = ft.dwLowDateTime;
866   return (i.QuadPart+9999)/10000;
867 }
868 
869 
GetTimes(Times & times)870 bool PThread::GetTimes(Times & times)
871 {
872   FILETIME created, exit, kernel, user;
873   exit.dwHighDateTime = exit.dwLowDateTime = 0;
874   if (!GetThreadTimes(GetHandle(), &created, &exit, &kernel, &user))
875     return false;
876 
877   times.m_kernel.SetInterval(GetMillisecondFromFileTime(kernel));
878   times.m_user.SetInterval(GetMillisecondFromFileTime(user));
879   if (exit.dwHighDateTime == 0 && exit.dwLowDateTime == 0)
880     times.m_real = PTime() - PTime(created);
881   else
882     times.m_real = PTime(exit) - PTime(created);
883 
884   return true;
885 }
886 
887 
CleanUp()888 void PThread::CleanUp()
889 {
890   if (threadHandle == NULL)
891     return;
892 
893   PProcess & process = PProcess::Current();
894   process.m_activeThreadMutex.Wait();
895   process.m_activeThreads.erase(m_threadId);
896   process.m_activeThreadMutex.Signal();
897 
898   if (!IsTerminated())
899     Terminate();
900 
901   CloseHandle(threadHandle);
902   threadHandle = NULL;
903 }
904 
905 
Restart()906 void PThread::Restart()
907 {
908   if (!PAssert(m_originalStackSize != 0, "Cannot restart process/external thread") ||
909       !PAssert(IsTerminated(), "Cannot restart running thread"))
910     return;
911 
912   CleanUp();
913 
914 #ifndef _WIN32_WCE
915   threadHandle = (HANDLE)_beginthreadex(NULL, m_originalStackSize, MainFunction, this, 0, &m_threadId);
916 #else
917    threadHandle = CreateThread(NULL, m_originalStackSize,
918                                   (LPTHREAD_START_ROUTINE)MainFunction, this, 0, (LPDWORD)&m_threadId);
919 #endif
920   PAssertOS(threadHandle != NULL);
921 }
922 
923 
Terminate()924 void PThread::Terminate()
925 {
926   if (!PAssert(!m_isProcess, "Cannot terminate the process!"))
927     return;
928 
929   if (Current() == this)
930     ExitThread(0);
931   else
932     TerminateThread(threadHandle, 1);
933 }
934 
935 
IsTerminated() const936 PBoolean PThread::IsTerminated() const
937 {
938   if (this == PThread::Current())
939     return PFalse;
940 
941   return WaitForTermination(0);
942 }
943 
944 
WaitForTermination() const945 void PThread::WaitForTermination() const
946 {
947   WaitForTermination(PMaxTimeInterval);
948 }
949 
950 
WaitForTermination(const PTimeInterval & maxWait) const951 PBoolean PThread::WaitForTermination(const PTimeInterval & maxWait) const
952 {
953   if ((this == PThread::Current()) || threadHandle == NULL) {
954     PTRACE(3, "PTLib\tWaitForTermination short circuited");
955     return PTrue;
956   }
957 
958   DWORD result;
959   PINDEX retries = 10;
960   while ((result = WaitForSingleObject(threadHandle, maxWait.GetInterval())) != WAIT_TIMEOUT) {
961     if (result == WAIT_OBJECT_0)
962       return PTrue;
963 
964     if (::GetLastError() != ERROR_INVALID_HANDLE) {
965       PAssertAlways(POperatingSystemError);
966       return PTrue;
967     }
968 
969     if (retries == 0)
970       return PTrue;
971 
972     retries--;
973   }
974 
975   return PFalse;
976 }
977 
978 
Suspend(PBoolean susp)979 void PThread::Suspend(PBoolean susp)
980 {
981   PAssert(!IsTerminated(), "Operation on terminated thread");
982   if (susp)
983     SuspendThread(threadHandle);
984   else
985     Resume();
986 }
987 
988 
Resume()989 void PThread::Resume()
990 {
991   PAssert(!IsTerminated(), "Operation on terminated thread");
992   ResumeThread(threadHandle);
993 }
994 
995 
IsSuspended() const996 PBoolean PThread::IsSuspended() const
997 {
998   if (this == PThread::Current())
999     return false;
1000 
1001   SuspendThread(threadHandle);
1002   return ResumeThread(threadHandle) > 1;
1003 }
1004 
1005 
SetAutoDelete(AutoDeleteFlag deletion)1006 void PThread::SetAutoDelete(AutoDeleteFlag deletion)
1007 {
1008   PAssert(deletion != AutoDeleteThread || (!m_isProcess && this != &PProcess::Current()), PLogicError);
1009   bool newAutoDelete = (deletion == AutoDeleteThread);
1010   if (m_autoDelete == newAutoDelete)
1011     return;
1012 
1013   m_autoDelete = newAutoDelete;
1014 
1015   PProcess & process = PProcess::Current();
1016 
1017   process.deleteThreadMutex.Wait();
1018   if (m_autoDelete)
1019     process.autoDeleteThreads.Append(this);
1020   else {
1021     process.autoDeleteThreads.DisallowDeleteObjects();
1022     process.autoDeleteThreads.Remove(this);
1023     process.autoDeleteThreads.AllowDeleteObjects();
1024   }
1025   process.deleteThreadMutex.Signal();
1026 
1027 }
1028 
1029 
SetPriority(Priority priorityLevel)1030 void PThread::SetPriority(Priority priorityLevel)
1031 {
1032   PAssert(!IsTerminated(), "Operation on terminated thread");
1033 
1034   static int const priorities[NumPriorities] = {
1035     THREAD_PRIORITY_LOWEST,
1036     THREAD_PRIORITY_BELOW_NORMAL,
1037     THREAD_PRIORITY_NORMAL,
1038     THREAD_PRIORITY_ABOVE_NORMAL,
1039     THREAD_PRIORITY_HIGHEST
1040   };
1041   SetThreadPriority(threadHandle, priorities[priorityLevel]);
1042 }
1043 
1044 
GetPriority() const1045 PThread::Priority PThread::GetPriority() const
1046 {
1047   PAssert(!IsTerminated(), "Operation on terminated thread");
1048 
1049   switch (GetThreadPriority(threadHandle)) {
1050     case THREAD_PRIORITY_LOWEST :
1051       return LowestPriority;
1052     case THREAD_PRIORITY_BELOW_NORMAL :
1053       return LowPriority;
1054     case THREAD_PRIORITY_NORMAL :
1055       return NormalPriority;
1056     case THREAD_PRIORITY_ABOVE_NORMAL :
1057       return HighPriority;
1058     case THREAD_PRIORITY_HIGHEST :
1059       return HighestPriority;
1060   }
1061   PAssertAlways(POperatingSystemError);
1062   return LowestPriority;
1063 }
1064 
1065 
Yield()1066 void PThread::Yield()
1067 {
1068   ::Sleep(0);
1069 }
1070 
1071 
1072 ///////////////////////////////////////////////////////////////////////////////
1073 // PProcess::TimerThread
1074 
HouseKeepingThread()1075 PProcess::HouseKeepingThread::HouseKeepingThread()
1076   : PThread(1000, NoAutoDeleteThread, HighestPriority, "PTLib Housekeeper")
1077   , m_running(true)
1078 {
1079   Resume();
1080 }
1081 
1082 
~HouseKeepingThread()1083 PProcess::HouseKeepingThread::~HouseKeepingThread()
1084 {
1085   m_running = false;
1086   m_breakBlock.Signal();
1087   WaitForTermination(500);
1088 }
1089 
1090 
Main()1091 void PProcess::HouseKeepingThread::Main()
1092 {
1093   PProcess & process = PProcess::Current();
1094 
1095   while (m_running) {
1096 
1097     // collect a list of thread handles to check, and clean up
1098     // handles for threads that disappeared without telling us
1099     process.deleteThreadMutex.Wait();
1100     HANDLE handles[MAXIMUM_WAIT_OBJECTS];
1101     DWORD numHandles = 1;
1102     handles[0] = m_breakBlock.GetHandle();
1103     ThreadList::iterator thread = process.autoDeleteThreads.begin();
1104     while (thread != process.autoDeleteThreads.end()) {
1105       if (thread->IsTerminated())
1106         process.autoDeleteThreads.erase(thread++);
1107 
1108       else {
1109         handles[numHandles] = thread->GetHandle();
1110 
1111         // make sure we don't put invalid handles into the list
1112 #ifndef _WIN32_WCE
1113         DWORD dwFlags;
1114         if (GetHandleInformation(handles[numHandles], &dwFlags) == 0) {
1115           PTRACE(2, "PTLib\tRefused to put invalid handle into wait list");
1116         }
1117         else
1118 #endif
1119         // don't put the handle for the current process in the list
1120         if (handles[numHandles] != process.GetHandle()) {
1121           numHandles++;
1122           if (numHandles >= MAXIMUM_WAIT_OBJECTS)
1123             break;
1124         }
1125 
1126         ++thread;
1127       }
1128     }
1129     process.deleteThreadMutex.Signal();
1130 
1131     PTimeInterval nextTimer = process.timers.Process();
1132     DWORD delay;
1133     if (nextTimer == PMaxTimeInterval)
1134       delay = INFINITE;
1135     else if (nextTimer > 1000)
1136       delay = 1000;
1137     else
1138       delay = nextTimer.GetInterval();
1139 
1140     DWORD result;
1141     int retries = 100;
1142 
1143     while ((result = WaitForMultipleObjects(numHandles, handles, PFalse, delay)) == WAIT_FAILED) {
1144 
1145       // if we get an invalid handle error, than assume this is because a thread ended between
1146       // creating the handle list and testing it. So, cleanup the list before calling
1147       // WaitForMultipleObjects again
1148       if (::GetLastError() == ERROR_INVALID_HANDLE)
1149         break;
1150 
1151       // sometimes WaitForMultipleObjects fails. No idea why, so allow some retries
1152       else {
1153         retries--;
1154         if (retries <= 0)
1155           break;
1156       }
1157     }
1158   }
1159 }
1160 
1161 
SignalTimerChange()1162 bool PProcess::SignalTimerChange()
1163 {
1164   if (!PAssert(IsInitialised(), PLogicError) || m_shuttingDown)
1165     return false;
1166 
1167   deleteThreadMutex.Wait();
1168 
1169   if (houseKeeper == NULL)
1170     houseKeeper = new HouseKeepingThread;
1171   else
1172     houseKeeper->m_breakBlock.Signal();
1173 
1174   deleteThreadMutex.Signal();
1175   return true;
1176 }
1177 
1178 
1179 ///////////////////////////////////////////////////////////////////////////////
1180 // PProcess
1181 
~PProcess()1182 PProcess::~PProcess()
1183 {
1184   PTRACE(4, "PTLib\tStarting process destruction.");
1185 
1186   // do whatever needs to shutdown
1187   PreShutdown();
1188 
1189   Sleep(100);  // Give threads time to die a natural death
1190 
1191   // Get rid of the house keeper (majordomocide)
1192   PTRACE(4, "PTLib\tTerminating housekeeper thread.");
1193   delete houseKeeper;
1194   houseKeeper = NULL;
1195 
1196   // OK, if there are any left we get really insistent...
1197   m_activeThreadMutex.Wait();
1198   PTRACE(4, "PTLib\tTerminating " << m_activeThreads.size()-1 << " remaining threads.");
1199   for (ThreadMap::iterator it = m_activeThreads.begin(); it != m_activeThreads.end(); ++it) {
1200     PThread & thread = *it->second;
1201     if (this != &thread && !thread.IsTerminated()) {
1202       PTRACE(3, "PTLib\tTerminating thread " << thread);
1203       thread.Terminate();  // With extreme prejudice
1204     }
1205   }
1206   PTRACE(4, "PTLib\tTerminated all threads.");
1207   m_activeThreadMutex.Signal();
1208 
1209   deleteThreadMutex.Wait();
1210   PTRACE(4, "PTLib\tDestroying " << autoDeleteThreads.GetSize() << " remaining auto-delete threads.");
1211   autoDeleteThreads.RemoveAll();
1212   deleteThreadMutex.Signal();
1213 
1214 #if _DEBUG
1215   WaitOnExitConsoleWindow();
1216 #endif
1217 
1218   PTRACE(4, "PTLib\tCompleted process destruction.");
1219 
1220   // Can't do any more tracing after this ...
1221 #if PTRACING
1222   PTrace::Cleanup();
1223   PTrace::SetStream(NULL);
1224 #endif
1225 
1226   PostShutdown();
1227 }
1228 
1229 
GetOSClass()1230 PString PProcess::GetOSClass()
1231 {
1232   return "Windows";
1233 }
1234 
1235 
GetOSName()1236 PString PProcess::GetOSName()
1237 {
1238   OSVERSIONINFO info;
1239   info.dwOSVersionInfoSize = sizeof(info);
1240   GetVersionEx(&info);
1241   switch (info.dwPlatformId) {
1242     case VER_PLATFORM_WIN32s :
1243       return "32s";
1244 
1245 #ifdef VER_PLATFORM_WIN32_CE
1246     case VER_PLATFORM_WIN32_CE :
1247       return "CE";
1248 #endif
1249 
1250     case VER_PLATFORM_WIN32_WINDOWS :
1251       if (info.dwMinorVersion < 10)
1252         return "95";
1253       if (info.dwMinorVersion < 90)
1254         return "98";
1255       return "ME";
1256 
1257     case VER_PLATFORM_WIN32_NT :
1258       switch (info.dwMajorVersion) {
1259         case 4 :
1260           return "NT";
1261         case 5:
1262           switch (info.dwMinorVersion) {
1263             case 0 :
1264               return "2000";
1265             case 1 :
1266               return "XP";
1267           }
1268           return "Server 2003";
1269 
1270         case 6 :
1271           switch (info.dwMinorVersion) {
1272             case 0 :
1273               return "Vista";
1274             case 1 :
1275               return "7";
1276           }
1277       }
1278   }
1279   return "?";
1280 }
1281 
1282 
GetOSHardware()1283 PString PProcess::GetOSHardware()
1284 {
1285   SYSTEM_INFO info;
1286   GetSystemInfo(&info);
1287   switch (info.wProcessorArchitecture) {
1288     case PROCESSOR_ARCHITECTURE_INTEL :
1289       switch (info.dwProcessorType) {
1290         case PROCESSOR_INTEL_386 :
1291           return "i386";
1292         case PROCESSOR_INTEL_486 :
1293           return "i486";
1294         case PROCESSOR_INTEL_PENTIUM :
1295           return psprintf("i586 (Model=%u Stepping=%u)", info.wProcessorRevision>>8, info.wProcessorRevision&0xff);
1296       }
1297       return "iX86";
1298 
1299     case PROCESSOR_ARCHITECTURE_MIPS :
1300       return "mips";
1301 
1302     case PROCESSOR_ARCHITECTURE_ALPHA :
1303       return "alpha";
1304 
1305     case PROCESSOR_ARCHITECTURE_PPC :
1306       return "ppc";
1307   }
1308   return "?";
1309 }
1310 
1311 
GetOSVersion()1312 PString PProcess::GetOSVersion()
1313 {
1314   OSVERSIONINFO info;
1315   info.dwOSVersionInfoSize = sizeof(info);
1316   GetVersionEx(&info);
1317   WORD wBuildNumber = (WORD)info.dwBuildNumber;
1318   return psprintf(wBuildNumber > 0 ? "v%u.%u.%u" : "v%u.%u",
1319                   info.dwMajorVersion, info.dwMinorVersion, wBuildNumber);
1320 }
1321 
1322 
IsOSVersion(unsigned major,unsigned minor,unsigned build)1323 bool PProcess::IsOSVersion(unsigned major, unsigned minor, unsigned build)
1324 {
1325   OSVERSIONINFO info;
1326   info.dwOSVersionInfoSize = sizeof(info);
1327   GetVersionEx(&info);
1328 
1329   if (info.dwMajorVersion < major)
1330     return false;
1331   if (info.dwMajorVersion > major)
1332     return true;
1333 
1334   if (info.dwMinorVersion < minor)
1335     return false;
1336   if (info.dwMinorVersion > minor)
1337     return true;
1338 
1339   return info.dwBuildNumber >= build;
1340 }
1341 
1342 
GetOSConfigDir()1343 PDirectory PProcess::GetOSConfigDir()
1344 {
1345 #ifdef _WIN32_WCE
1346   return PString("\\Windows");
1347 #else
1348   OSVERSIONINFO info;
1349   info.dwOSVersionInfoSize = sizeof(info);
1350   GetVersionEx(&info);
1351 
1352   char dir[_MAX_PATH];
1353 
1354   if (info.dwPlatformId != VER_PLATFORM_WIN32_NT) {
1355     PAssertOS(GetWindowsDirectory(dir, sizeof(dir)) != 0);
1356     return dir;
1357   }
1358 
1359   PAssertOS(GetSystemDirectory(dir, sizeof(dir)) != 0);
1360   PDirectory sysdir = dir;
1361   return sysdir;  //+ "drivers\\etc";
1362 #endif
1363 }
1364 
GetUserName() const1365 PString PProcess::GetUserName() const
1366 {
1367   PString username;
1368 #ifndef _WIN32_WCE
1369   unsigned long size = 50;
1370   ::GetUserName(username.GetPointer((PINDEX)size), &size);
1371 #else
1372   TCHAR wcsuser[50] = {0};
1373   HKEY hKeyComm, hKeyIdent;
1374   RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Comm"), 0, 0, &hKeyComm);
1375   RegOpenKeyEx(hKeyComm, _T("Ident"), 0, 0, &hKeyIdent);
1376 
1377   DWORD dwType = REG_SZ; DWORD dw = 50;
1378   if( ERROR_SUCCESS != RegQueryValueEx(
1379     hKeyIdent, _T("Username"), NULL, &dwType, (LPBYTE) wcsuser, &dw)
1380     || !*wcsuser )
1381   {
1382   RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Ident"), 0, 0, &hKeyIdent);
1383   dw = 50L;
1384   if( ERROR_SUCCESS == RegQueryValueEx(
1385     hKeyIdent, _T("Name"), NULL, &dwType, (LPBYTE) wcsuser, &dw))
1386       wcscat( wcsuser, _T(" user") ); // like "Pocket_PC User"
1387   }
1388 
1389   username = wcsuser;
1390 #endif
1391   username.MakeMinimumSize();
1392   return username;
1393 }
1394 
1395 
SetUserName(const PString & username,PBoolean)1396 PBoolean PProcess::SetUserName(const PString & username, PBoolean)
1397 {
1398   if (username.IsEmpty())
1399     return PFalse;
1400 
1401   PAssertAlways(PUnimplementedFunction);
1402   return PFalse;
1403 }
1404 
1405 
GetGroupName() const1406 PString PProcess::GetGroupName() const
1407 {
1408   return "Users";
1409 }
1410 
1411 
SetGroupName(const PString & groupname,PBoolean)1412 PBoolean PProcess::SetGroupName(const PString & groupname, PBoolean)
1413 {
1414   if (groupname.IsEmpty())
1415     return PFalse;
1416 
1417   PAssertAlways(PUnimplementedFunction);
1418   return PFalse;
1419 }
1420 
1421 
GetCurrentProcessID()1422 PProcessIdentifier PProcess::GetCurrentProcessID()
1423 {
1424   return ::GetCurrentProcessId();
1425 }
1426 
1427 
IsServiceProcess() const1428 PBoolean PProcess::IsServiceProcess() const
1429 {
1430   return PFalse;
1431 }
1432 
1433 
1434 #ifdef _WIN32_WCE
1435 
IsGUIProcess() const1436 PBoolean PProcess::IsGUIProcess() const
1437 {
1438   return PTrue;
1439 }
1440 
1441 #else
1442 
1443 static int IsGUIProcessStatus;
1444 
EnumWindowsProc(HWND hWnd,LPARAM thisProcess)1445 static BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM thisProcess)
1446 {
1447   char wndClassName[100];
1448   GetClassName(hWnd, wndClassName, sizeof(wndClassName));
1449   if (strcmp(wndClassName, "ConsoleWindowClass") != 0)
1450     return TRUE;
1451 
1452   DWORD wndProcess;
1453   GetWindowThreadProcessId(hWnd, &wndProcess);
1454   if (wndProcess != (DWORD)thisProcess)
1455     return TRUE;
1456 
1457   IsGUIProcessStatus = -1;
1458   return FALSE;
1459 }
1460 
IsGUIProcess() const1461 PBoolean PProcess::IsGUIProcess() const
1462 {
1463   if (IsGUIProcessStatus == 0) {
1464     IsGUIProcessStatus = 1;
1465     EnumWindows(EnumWindowsProc, GetCurrentProcessId());
1466   }
1467   return IsGUIProcessStatus > 0;
1468 }
1469 
1470 #endif // _WIN32_WCE
1471 
1472 
1473 ///////////////////////////////////////////////////////////////////////////////
1474 // PSemaphore
1475 
PSemaphore(HANDLE h)1476 PSemaphore::PSemaphore(HANDLE h)
1477 {
1478   handle = h;
1479   PAssertOS(handle != NULL);
1480 }
1481 
1482 
PSemaphore(unsigned initial,unsigned maxCount)1483 PSemaphore::PSemaphore(unsigned initial, unsigned maxCount)
1484 {
1485   initialVal  = initial;
1486   maxCountVal = maxCount;
1487 
1488   if (initial > maxCount)
1489     initial = maxCount;
1490   handle = CreateSemaphore(NULL, initial, maxCount, NULL);
1491   PAssertOS(handle != NULL);
1492 }
1493 
PSemaphore(const PSemaphore & sem)1494 PSemaphore::PSemaphore(const PSemaphore & sem)
1495 {
1496   initialVal  = sem.GetInitialVal();
1497   maxCountVal = sem.GetMaxCountVal();
1498 
1499   if (initialVal > maxCountVal)
1500     initialVal = maxCountVal;
1501   handle = CreateSemaphore(NULL, initialVal, maxCountVal, NULL);
1502   PAssertOS(handle != NULL);
1503 }
1504 
~PSemaphore()1505 PSemaphore::~PSemaphore()
1506 {
1507   if (handle != NULL)
1508     PAssertOS(CloseHandle(handle));
1509 }
1510 
1511 
Wait()1512 void PSemaphore::Wait()
1513 {
1514   PAssertOS(WaitForSingleObject(handle, INFINITE) != WAIT_FAILED);
1515 }
1516 
1517 
Wait(const PTimeInterval & timeout)1518 PBoolean PSemaphore::Wait(const PTimeInterval & timeout)
1519 {
1520   DWORD result = WaitForSingleObject(handle, timeout.GetInterval());
1521   PAssertOS(result != WAIT_FAILED);
1522   return result != WAIT_TIMEOUT;
1523 }
1524 
1525 
Signal()1526 void PSemaphore::Signal()
1527 {
1528   if (!ReleaseSemaphore(handle, 1, NULL))
1529     PAssertOS(::GetLastError() != ERROR_INVALID_HANDLE);
1530   SetLastError(ERROR_SUCCESS);
1531 }
1532 
1533 
WillBlock() const1534 PBoolean PSemaphore::WillBlock() const
1535 {
1536   PSemaphore * unconst = (PSemaphore *)this;
1537 
1538   if (!unconst->Wait(0))
1539     return PTrue;
1540 
1541   unconst->Signal();
1542   return PFalse;
1543 }
1544 
1545 
1546 ///////////////////////////////////////////////////////////////////////////////
1547 // PTimedMutex
1548 
PTimedMutex()1549 PTimedMutex::PTimedMutex()
1550   : PSemaphore(::CreateMutex(NULL, PFalse, NULL))
1551 {
1552 }
1553 
PTimedMutex(const PTimedMutex &)1554 PTimedMutex::PTimedMutex(const PTimedMutex &)
1555   : PSemaphore(::CreateMutex(NULL, PFalse, NULL))
1556 {
1557 }
1558 
Signal()1559 void PTimedMutex::Signal()
1560 {
1561   PAssertOS(::ReleaseMutex(handle));
1562 }
1563 
1564 ///////////////////////////////////////////////////////////////////////////////
1565 // PSyncPoint
1566 
PSyncPoint()1567 PSyncPoint::PSyncPoint()
1568   : PSemaphore(::CreateEvent(NULL, PFalse, PFalse, NULL))
1569 {
1570 }
1571 
PSyncPoint(const PSyncPoint &)1572 PSyncPoint::PSyncPoint(const PSyncPoint &)
1573   : PSemaphore(::CreateEvent(NULL, PFalse, PFalse, NULL))
1574 {
1575 }
1576 
Signal()1577 void PSyncPoint::Signal()
1578 {
1579   PAssertOS(::SetEvent(handle));
1580 }
1581 
1582 
1583 ///////////////////////////////////////////////////////////////////////////////
1584 // PDynaLink
1585 
PDynaLink()1586 PDynaLink::PDynaLink()
1587 {
1588   m_hDLL = NULL;
1589 }
1590 
1591 
PDynaLink(const PString & name)1592 PDynaLink::PDynaLink(const PString & name)
1593 {
1594   Open(name);
1595 }
1596 
1597 
~PDynaLink()1598 PDynaLink::~PDynaLink()
1599 {
1600   Close();
1601 }
1602 
1603 
GetExtension()1604 PString PDynaLink::GetExtension()
1605 {
1606   return ".DLL";
1607 }
1608 
1609 
Open(const PString & name)1610 PBoolean PDynaLink::Open(const PString & name)
1611 {
1612   m_lastError.MakeEmpty();
1613 
1614   PVarString filename = name;
1615 #ifndef _WIN32_WCE
1616   m_hDLL = LoadLibraryEx(filename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
1617 #else
1618   m_hDLL = LoadLibrary(filename);
1619 #endif
1620   if (m_hDLL != NULL)
1621     return true;
1622 
1623   m_lastError.sprintf("0x%x", ::GetLastError());
1624   PTRACE(1, "DLL\tError loading DLL: " << m_lastError);
1625   return false;
1626 }
1627 
1628 
Close()1629 void PDynaLink::Close()
1630 {
1631   if (m_hDLL != NULL) {
1632     FreeLibrary(m_hDLL);
1633     m_hDLL = NULL;
1634   }
1635 }
1636 
1637 
IsLoaded() const1638 PBoolean PDynaLink::IsLoaded() const
1639 {
1640   return m_hDLL != NULL;
1641 }
1642 
1643 
GetName(PBoolean full) const1644 PString PDynaLink::GetName(PBoolean full) const
1645 {
1646   PFilePathString str;
1647   if (m_hDLL != NULL) {
1648 #ifdef UNICODE
1649     TCHAR path[_MAX_PATH];
1650     GetModuleFileName(m_hDLL, path, _MAX_PATH-1);
1651     str = PString(path);
1652 #else
1653     GetModuleFileName(m_hDLL, str.GetPointer(_MAX_PATH), _MAX_PATH-1);
1654 #endif
1655     if (!full) {
1656       str.Delete(0, str.FindLast('\\')+1);
1657       PINDEX pos = str.Find(".DLL");
1658       if (pos != P_MAX_INDEX)
1659         str.Delete(pos, P_MAX_INDEX);
1660     }
1661     str.MakeMinimumSize();
1662   }
1663   return str;
1664 }
1665 
1666 
GetFunction(PINDEX index,Function & func)1667 PBoolean PDynaLink::GetFunction(PINDEX index, Function & func)
1668 {
1669   m_lastError.MakeEmpty();
1670 
1671   if (m_hDLL == NULL)
1672     return false;
1673 
1674   func = (Function)GetProcAddress(m_hDLL, (LPTSTR)(DWORD)LOWORD(index));
1675   if (func != NULL)
1676     return true;
1677 
1678   m_lastError.sprintf("0x%x", ::GetLastError());
1679   return false;
1680 }
1681 
1682 
GetFunction(const PString & name,Function & func)1683 PBoolean PDynaLink::GetFunction(const PString & name, Function & func)
1684 {
1685   m_lastError.MakeEmpty();
1686 
1687   if (m_hDLL == NULL)
1688     return PFalse;
1689 
1690   PVarString funcname = name;
1691   func = (Function)GetProcAddress(m_hDLL, funcname);
1692   if (func != NULL)
1693     return true;
1694 
1695   m_lastError.sprintf("0x%x", ::GetLastError());
1696   return false;
1697 }
1698 
1699 
1700 ///////////////////////////////////////////////////////////////////////////////
1701 // PDebugStream
1702 
PDebugStream()1703 PDebugStream::PDebugStream()
1704   : ostream(&buffer)
1705 {
1706 }
1707 
1708 
Buffer()1709 PDebugStream::Buffer::Buffer()
1710 {
1711   setg(buffer, buffer, &buffer[sizeof(buffer)-2]);
1712   setp(buffer, &buffer[sizeof(buffer)-2]);
1713 }
1714 
1715 
overflow(int c)1716 int PDebugStream::Buffer::overflow(int c)
1717 {
1718   int bufSize = pptr() - pbase();
1719 
1720   if (c != EOF) {
1721     *pptr() = (char)c;
1722     bufSize++;
1723   }
1724 
1725   if (bufSize != 0) {
1726     char * p = pbase();
1727     setp(p, epptr());
1728     p[bufSize] = '\0';
1729 
1730 #ifdef UNICODE
1731     // Note we do NOT use PWideString here as it could cause infinitely
1732     // recursive calls if there is an error!
1733     PINDEX length = strlen(p);
1734     wchar_t * unicode = new wchar_t[length+1];
1735     unicode[length] = 0;
1736     MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, p, length, unicode, length+1);
1737     OutputDebugString(unicode);
1738     delete [] unicode;
1739 #else
1740     OutputDebugString(p);
1741 #endif
1742   }
1743 
1744   return 0;
1745 }
1746 
1747 
underflow()1748 int PDebugStream::Buffer::underflow()
1749 {
1750   return EOF;
1751 }
1752 
1753 
sync()1754 int PDebugStream::Buffer::sync()
1755 {
1756   return overflow(EOF);
1757 }
1758 
1759 // End Of File ///////////////////////////////////////////////////////////////
1760