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(×tamp);
72 #else
73 SYSTEMTIME SystemTime;
74 GetSystemTime(&SystemTime);
75 SystemTimeToFileTime(&SystemTime, ×tamp);
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 §orsPerCluster,
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 §orsPerCluster,
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