]> git.saurik.com Git - wxWidgets.git/blame - src/msw/utils.cpp
Also allow key events for Shift-Tab when wxWANTS_CHARS style is used
[wxWidgets.git] / src / msw / utils.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
8caa4ed1 2// Name: msw/utils.cpp
2bda0e17
KB
3// Purpose: Various utilities
4// Author: Julian Smart
5// Modified by:
6// Created: 04/01/98
7// RCS-ID: $Id$
6c9a19aa
JS
8// Copyright: (c) Julian Smart
9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
b568d04f
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
2bda0e17
KB
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
a4f96412 24 #pragma hdrstop
2bda0e17
KB
25#endif
26
27#ifndef WX_PRECOMP
a4f96412
VZ
28 #include "wx/utils.h"
29 #include "wx/app.h"
1f0500b3
VZ
30 #include "wx/intl.h"
31 #include "wx/log.h"
743e0a66 32#endif //WX_PRECOMP
2bda0e17 33
2739d4f0 34#include "wx/apptrait.h"
15cdcf6a 35#include "wx/dynload.h"
2739d4f0 36
2dc357ea
VZ
37#include "wx/confbase.h" // for wxExpandEnvVars()
38
b568d04f 39#include "wx/msw/private.h" // includes <windows.h>
fcd0c90f 40#include "wx/msw/missing.h" // CHARSET_HANGUL
b568d04f 41
3c302c07
MB
42#if defined(__GNUWIN32_OLD__) || defined(__WXWINCE__) \
43 || defined(__CYGWIN32__)
c1cb4153
VZ
44 // apparently we need to include winsock.h to get WSADATA and other stuff
45 // used in wxGetFullHostName() with the old mingw32 versions
46 #include <winsock.h>
47#endif
48
2bda0e17
KB
49#include "wx/timer.h"
50
4676948b 51#if !defined(__GNUWIN32__) && !defined(__SALFORDC__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
a4f96412 52 #include <direct.h>
ce3ed50d 53
a4f96412
VZ
54 #ifndef __MWERKS__
55 #include <dos.h>
56 #endif
743e0a66 57#endif //GNUWIN32
2bda0e17 58
b39dbf34 59#if defined(__CYGWIN__)
a4f96412
VZ
60 #include <sys/unistd.h>
61 #include <sys/stat.h>
3ffbc733 62 #include <sys/cygwin.h> // for cygwin_conv_to_full_win32_path()
743e0a66
VZ
63#endif //GNUWIN32
64
2bda0e17
KB
65#ifdef __BORLANDC__ // Please someone tell me which version of Borland needs
66 // this (3.1 I believe) and how to test for it.
67 // If this works for Borland 4.0 as well, then no worries.
a4f96412 68 #include <dir.h>
2bda0e17
KB
69#endif
70
a4f96412
VZ
71// VZ: there is some code using NetXXX() functions to get the full user name:
72// I don't think it's a good idea because they don't work under Win95 and
73// seem to return the same as wxGetUserId() under NT. If you really want
74// to use them, just #define USE_NET_API
75#undef USE_NET_API
76
77#ifdef USE_NET_API
78 #include <lm.h>
79#endif // USE_NET_API
80
4676948b 81#if defined(__WIN32__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__)
2e38557f
JS
82 #ifndef __UNIX__
83 #include <io.h>
84 #endif
2bda0e17 85
a4f96412
VZ
86 #ifndef __GNUWIN32__
87 #include <shellapi.h>
88 #endif
2bda0e17
KB
89#endif
90
2bda0e17 91#ifndef __WATCOMC__
3f4a0c5b
VZ
92 #if !(defined(_MSC_VER) && (_MSC_VER > 800))
93 #include <errno.h>
94 #endif
2bda0e17 95#endif
2bda0e17 96
2dc357ea
VZ
97// 260 was taken from windef.h
98#ifndef MAX_PATH
99 #define MAX_PATH 260
100#endif
101
b568d04f
VZ
102// ----------------------------------------------------------------------------
103// constants
104// ----------------------------------------------------------------------------
105
2bda0e17 106// In the WIN.INI file
223d09f6 107static const wxChar WX_SECTION[] = wxT("wxWindows");
b568d04f
VZ
108static const wxChar eUSERNAME[] = wxT("UserName");
109
110// these are only used under Win16
04ef50df 111#if !defined(__WIN32__) && !defined(__WXMICROWIN__)
223d09f6
KB
112static const wxChar eHOSTNAME[] = wxT("HostName");
113static const wxChar eUSERID[] = wxT("UserId");
b568d04f
VZ
114#endif // !Win32
115
116// ============================================================================
117// implementation
118// ============================================================================
2bda0e17 119
b568d04f
VZ
120// ----------------------------------------------------------------------------
121// get host name and related
122// ----------------------------------------------------------------------------
2bda0e17 123
1f0500b3 124// Get hostname only (without domain name)
837e5743 125bool wxGetHostName(wxChar *buf, int maxSize)
2bda0e17 126{
4676948b
JS
127#if defined(__WXWINCE__)
128 return FALSE;
129#elif defined(__WIN32__) && !defined(__WXMICROWIN__)
b568d04f
VZ
130 DWORD nSize = maxSize;
131 if ( !::GetComputerName(buf, &nSize) )
132 {
f6bcfd97 133 wxLogLastError(wxT("GetComputerName"));
b568d04f
VZ
134
135 return FALSE;
136 }
137
138 return TRUE;
2bda0e17 139#else
b568d04f
VZ
140 wxChar *sysname;
141 const wxChar *default_host = wxT("noname");
142
143 if ((sysname = wxGetenv(wxT("SYSTEM_NAME"))) == NULL) {
144 GetProfileString(WX_SECTION, eHOSTNAME, default_host, buf, maxSize - 1);
145 } else
146 wxStrncpy(buf, sysname, maxSize - 1);
147 buf[maxSize] = wxT('\0');
148 return *buf ? TRUE : FALSE;
2bda0e17
KB
149#endif
150}
151
1f0500b3 152// get full hostname (with domain name if possible)
b568d04f
VZ
153bool wxGetFullHostName(wxChar *buf, int maxSize)
154{
ffcb4ee4 155#if !defined( __WXMICROWIN__) && wxUSE_DYNAMIC_LOADER
1f0500b3 156 // TODO should use GetComputerNameEx() when available
79180098 157
15cdcf6a
VZ
158 // we don't want to always link with Winsock DLL as we might not use it at
159 // all, so load it dynamically here if needed
160 wxDynamicLibrary dllWinsock(_T("ws2_32.dll"), wxDL_VERBATIM);
161 if ( dllWinsock.IsLoaded() )
1f0500b3 162 {
15cdcf6a
VZ
163 typedef int (PASCAL *WSAStartup_t)(WORD, WSADATA *);
164 typedef int (PASCAL *gethostname_t)(char *, int);
165 typedef hostent* (PASCAL *gethostbyname_t)(const char *);
166 typedef hostent* (PASCAL *gethostbyaddr_t)(const char *, int , int);
167 typedef int (PASCAL *WSACleanup_t)(void);
1f0500b3 168
15cdcf6a
VZ
169 #define LOAD_WINSOCK_FUNC(func) \
170 func ## _t \
171 pfn ## func = (func ## _t)dllWinsock.GetSymbol(_T(#func))
1f0500b3 172
15cdcf6a
VZ
173 LOAD_WINSOCK_FUNC(WSAStartup);
174
175 WSADATA wsa;
176 if ( pfnWSAStartup && pfnWSAStartup(MAKEWORD(1, 1), &wsa) == 0 )
177 {
178 LOAD_WINSOCK_FUNC(gethostname);
179
180 wxString host;
181 if ( pfngethostname )
182 {
183 char bufA[256];
184 if ( pfngethostname(bufA, WXSIZEOF(bufA)) == 0 )
1f0500b3 185 {
15cdcf6a
VZ
186 // gethostname() won't usually include the DNS domain name,
187 // for this we need to work a bit more
188 if ( !strchr(bufA, '.') )
189 {
190 LOAD_WINSOCK_FUNC(gethostbyname);
191
192 struct hostent *pHostEnt = pfngethostbyname
193 ? pfngethostbyname(bufA)
194 : NULL;
195
196 if ( pHostEnt )
197 {
198 // Windows will use DNS internally now
199 LOAD_WINSOCK_FUNC(gethostbyaddr);
200
201 pHostEnt = pfngethostbyaddr
202 ? pfngethostbyaddr(pHostEnt->h_addr,
203 4, AF_INET)
204 : NULL;
205 }
206
207 if ( pHostEnt )
208 {
209 host = wxString::FromAscii(pHostEnt->h_name);
210 }
211 }
1f0500b3
VZ
212 }
213 }
1f0500b3 214
15cdcf6a
VZ
215 LOAD_WINSOCK_FUNC(WSACleanup);
216 if ( pfnWSACleanup )
217 pfnWSACleanup();
1f0500b3 218
1f0500b3 219
15cdcf6a
VZ
220 if ( !host.empty() )
221 {
222 wxStrncpy(buf, host, maxSize);
223
224 return TRUE;
225 }
1f0500b3
VZ
226 }
227 }
15cdcf6a 228#endif // !__WXMICROWIN__
1f0500b3 229
b568d04f
VZ
230 return wxGetHostName(buf, maxSize);
231}
232
2bda0e17 233// Get user ID e.g. jacs
837e5743 234bool wxGetUserId(wxChar *buf, int maxSize)
2bda0e17 235{
4676948b
JS
236#if defined(__WXWINCE__)
237 return FALSE;
238#elif defined(__WIN32__) && !defined(__win32s__) && !defined(__WXMICROWIN__)
0a144271
VZ
239 DWORD nSize = maxSize;
240 if ( ::GetUserName(buf, &nSize) == 0 )
241 {
a4f96412 242 // actually, it does happen on Win9x if the user didn't log on
223d09f6 243 DWORD res = ::GetEnvironmentVariable(wxT("username"), buf, maxSize);
a4f96412
VZ
244 if ( res == 0 )
245 {
246 // not found
247 return FALSE;
248 }
0a144271
VZ
249 }
250
251 return TRUE;
0a144271 252#else // Win16 or Win32s
b568d04f
VZ
253 wxChar *user;
254 const wxChar *default_id = wxT("anonymous");
2bda0e17 255
b568d04f
VZ
256 // Can't assume we have NIS (PC-NFS) or some other ID daemon
257 // So we ...
258 if ( (user = wxGetenv(wxT("USER"))) == NULL &&
259 (user = wxGetenv(wxT("LOGNAME"))) == NULL )
260 {
261 // Use wxWindows configuration data (comming soon)
262 GetProfileString(WX_SECTION, eUSERID, default_id, buf, maxSize - 1);
263 }
264 else
265 {
266 wxStrncpy(buf, user, maxSize - 1);
267 }
a4f96412 268
b568d04f 269 return *buf ? TRUE : FALSE;
2bda0e17
KB
270#endif
271}
272
273// Get user name e.g. Julian Smart
837e5743 274bool wxGetUserName(wxChar *buf, int maxSize)
2bda0e17 275{
4676948b
JS
276#if defined(__WXWINCE__)
277 return FALSE;
278#elif defined(USE_NET_API)
3e3be693
VZ
279 CHAR szUserName[256];
280 if ( !wxGetUserId(szUserName, WXSIZEOF(szUserName)) )
281 return FALSE;
a4f96412 282
3e3be693
VZ
283 // TODO how to get the domain name?
284 CHAR *szDomain = "";
a4f96412 285
3e3be693
VZ
286 // the code is based on the MSDN example (also see KB article Q119670)
287 WCHAR wszUserName[256]; // Unicode user name
288 WCHAR wszDomain[256];
289 LPBYTE ComputerName;
a4f96412 290
3e3be693 291 USER_INFO_2 *ui2; // User structure
a4f96412 292
3e3be693
VZ
293 // Convert ANSI user name and domain to Unicode
294 MultiByteToWideChar( CP_ACP, 0, szUserName, strlen(szUserName)+1,
295 wszUserName, WXSIZEOF(wszUserName) );
296 MultiByteToWideChar( CP_ACP, 0, szDomain, strlen(szDomain)+1,
297 wszDomain, WXSIZEOF(wszDomain) );
a4f96412 298
3e3be693
VZ
299 // Get the computer name of a DC for the domain.
300 if ( NetGetDCName( NULL, wszDomain, &ComputerName ) != NERR_Success )
301 {
302 wxLogError(wxT("Can not find domain controller"));
a4f96412 303
3e3be693
VZ
304 goto error;
305 }
a4f96412 306
3e3be693
VZ
307 // Look up the user on the DC
308 NET_API_STATUS status = NetUserGetInfo( (LPWSTR)ComputerName,
309 (LPWSTR)&wszUserName,
310 2, // level - we want USER_INFO_2
311 (LPBYTE *) &ui2 );
312 switch ( status )
313 {
314 case NERR_Success:
315 // ok
316 break;
a4f96412 317
3e3be693
VZ
318 case NERR_InvalidComputer:
319 wxLogError(wxT("Invalid domain controller name."));
a4f96412 320
3e3be693 321 goto error;
a4f96412 322
3e3be693
VZ
323 case NERR_UserNotFound:
324 wxLogError(wxT("Invalid user name '%s'."), szUserName);
a4f96412 325
3e3be693 326 goto error;
a4f96412 327
3e3be693
VZ
328 default:
329 wxLogSysError(wxT("Can't get information about user"));
a4f96412 330
3e3be693
VZ
331 goto error;
332 }
a4f96412 333
3e3be693
VZ
334 // Convert the Unicode full name to ANSI
335 WideCharToMultiByte( CP_ACP, 0, ui2->usri2_full_name, -1,
336 buf, maxSize, NULL, NULL );
a4f96412 337
3e3be693 338 return TRUE;
a4f96412
VZ
339
340error:
3e3be693 341 wxLogError(wxT("Couldn't look up full user name."));
a4f96412 342
3e3be693 343 return FALSE;
a4f96412 344#else // !USE_NET_API
3e3be693
VZ
345 // Could use NIS, MS-Mail or other site specific programs
346 // Use wxWindows configuration data
fda7962d 347 bool ok = GetProfileString(WX_SECTION, eUSERNAME, wxEmptyString, buf, maxSize - 1) != 0;
3e3be693
VZ
348 if ( !ok )
349 {
350 ok = wxGetUserId(buf, maxSize);
351 }
0a144271 352
3e3be693
VZ
353 if ( !ok )
354 {
355 wxStrncpy(buf, wxT("Unknown User"), maxSize);
b568d04f 356 }
3e3be693 357#endif // Win32/16
0a144271 358
b568d04f 359 return TRUE;
2bda0e17
KB
360}
361
b568d04f 362const wxChar* wxGetHomeDir(wxString *pstr)
2bda0e17 363{
2dc357ea 364 wxString& strDir = *pstr;
2bda0e17 365
2dc357ea 366 // first branch is for Cygwin
4676948b 367#if defined(__UNIX__)
b568d04f
VZ
368 const wxChar *szHome = wxGetenv("HOME");
369 if ( szHome == NULL ) {
370 // we're homeless...
371 wxLogWarning(_("can't find user's HOME, using current directory."));
372 strDir = wxT(".");
373 }
374 else
375 strDir = szHome;
2bda0e17 376
b568d04f
VZ
377 // add a trailing slash if needed
378 if ( strDir.Last() != wxT('/') )
379 strDir << wxT('/');
3ffbc733 380
e02e8816 381 #ifdef __CYGWIN__
2dc357ea
VZ
382 // Cygwin returns unix type path but that does not work well
383 static wxChar windowsPath[MAX_PATH];
384 cygwin_conv_to_full_win32_path(strDir, windowsPath);
385 strDir = windowsPath;
3ffbc733 386 #endif
4676948b
JS
387#elif defined(__WXWINCE__)
388 // Nothing
389#else
2dc357ea 390 strDir.clear();
96c21216 391
2dc357ea
VZ
392 // If we have a valid HOME directory, as is used on many machines that
393 // have unix utilities on them, we should use that.
394 const wxChar *szHome = wxGetenv(wxT("HOME"));
96c21216 395
2dc357ea
VZ
396 if ( szHome != NULL )
397 {
96c21216 398 strDir = szHome;
2dc357ea
VZ
399 }
400 else // no HOME, try HOMEDRIVE/PATH
401 {
402 szHome = wxGetenv(wxT("HOMEDRIVE"));
403 if ( szHome != NULL )
96c21216 404 strDir << szHome;
2dc357ea 405 szHome = wxGetenv(wxT("HOMEPATH"));
96c21216 406
2dc357ea
VZ
407 if ( szHome != NULL )
408 {
96c21216
VZ
409 strDir << szHome;
410
411 // the idea is that under NT these variables have default values
412 // of "%systemdrive%:" and "\\". As we don't want to create our
413 // config files in the root directory of the system drive, we will
414 // create it in our program's dir. However, if the user took care
415 // to set HOMEPATH to something other than "\\", we suppose that he
416 // knows what he is doing and use the supplied value.
417 if ( wxStrcmp(szHome, wxT("\\")) == 0 )
2dc357ea
VZ
418 strDir.clear();
419 }
420 }
96c21216 421
2dc357ea
VZ
422 if ( strDir.empty() )
423 {
424 // If we have a valid USERPROFILE directory, as is the case in
425 // Windows NT, 2000 and XP, we should use that as our home directory.
426 szHome = wxGetenv(wxT("USERPROFILE"));
2bda0e17 427
2dc357ea 428 if ( szHome != NULL )
96c21216 429 strDir = szHome;
2dc357ea 430 }
2bda0e17 431
2dc357ea
VZ
432 if ( !strDir.empty() )
433 {
434 // sometimes the value of HOME may be "%USERPROFILE%", so reexpand the
435 // value once again, it shouldn't hurt anyhow
436 strDir = wxExpandEnvVars(strDir);
437 }
438 else // fall back to the program directory
439 {
440 wxString strPath;
441 ::GetModuleFileName(::GetModuleHandle(NULL),
442 wxStringBuffer(strPath, MAX_PATH), MAX_PATH);
58a33cb4 443
2dc357ea
VZ
444 // extract the dir name
445 wxSplitPath(strPath, &strDir, NULL, NULL);
446 }
4676948b 447#endif // UNIX/Win
b568d04f 448
2dc357ea 449 return strDir.c_str();
afb74891
VZ
450}
451
33ac7e6f 452wxChar *wxGetUserHome(const wxString& WXUNUSED(user))
2bda0e17 453{
b568d04f
VZ
454 // VZ: the old code here never worked for user != "" anyhow! Moreover, it
455 // returned sometimes a malloc()'d pointer, sometimes a pointer to a
456 // static buffer and sometimes I don't even know what.
457 static wxString s_home;
2bda0e17 458
b568d04f 459 return (wxChar *)wxGetHomeDir(&s_home);
2bda0e17
KB
460}
461
b568d04f 462bool wxDirExists(const wxString& dir)
2bda0e17 463{
04ef50df
JS
464#ifdef __WXMICROWIN__
465 return wxPathExist(dir);
466#elif defined(__WIN32__)
cf1eeea3 467 DWORD attribs = GetFileAttributes(dir);
3897b707 468 return ((attribs != (DWORD)-1) && (attribs & FILE_ATTRIBUTE_DIRECTORY));
b568d04f
VZ
469#else // Win16
470 #ifdef __BORLANDC__
471 struct ffblk fileInfo;
472 #else
473 struct find_t fileInfo;
474 #endif
b568d04f
VZ
475 // In Borland findfirst has a different argument
476 // ordering from _dos_findfirst. But _dos_findfirst
477 // _should_ be ok in both MS and Borland... why not?
478 #ifdef __BORLANDC__
479 return (findfirst(dir, &fileInfo, _A_SUBDIR) == 0 &&
480 (fileInfo.ff_attrib & _A_SUBDIR) != 0);
481 #else
482 return (_dos_findfirst(dir, _A_SUBDIR, &fileInfo) == 0) &&
483 ((fileInfo.attrib & _A_SUBDIR) != 0);
484 #endif
485#endif // Win32/16
486}
2bda0e17 487
eadd7bd2
VZ
488bool wxGetDiskSpace(const wxString& path, wxLongLong *pTotal, wxLongLong *pFree)
489{
4676948b
JS
490#ifdef __WXWINCE__
491 return FALSE;
492#else
eadd7bd2
VZ
493 if ( path.empty() )
494 return FALSE;
495
ee88cb34
MB
496// old w32api don't have ULARGE_INTEGER
497#if defined(__WIN32__) && \
498 (!defined(__GNUWIN32__) || wxCHECK_W32API_VERSION( 0, 3 ))
eadd7bd2
VZ
499 // GetDiskFreeSpaceEx() is not available under original Win95, check for
500 // it
9ee966ec
VZ
501 typedef BOOL (WINAPI *GetDiskFreeSpaceEx_t)(LPCTSTR,
502 PULARGE_INTEGER,
503 PULARGE_INTEGER,
504 PULARGE_INTEGER);
eadd7bd2
VZ
505
506 GetDiskFreeSpaceEx_t
bb0e27ee 507 pGetDiskFreeSpaceEx = (GetDiskFreeSpaceEx_t)::GetProcAddress
eadd7bd2
VZ
508 (
509 ::GetModuleHandle(_T("kernel32.dll")),
510#if wxUSE_UNICODE
511 "GetDiskFreeSpaceExW"
512#else
513 "GetDiskFreeSpaceExA"
514#endif
515 );
516
517 if ( pGetDiskFreeSpaceEx )
518 {
519 ULARGE_INTEGER bytesFree, bytesTotal;
520
521 // may pass the path as is, GetDiskFreeSpaceEx() is smart enough
522 if ( !pGetDiskFreeSpaceEx(path,
523 &bytesFree,
524 &bytesTotal,
525 NULL) )
526 {
527 wxLogLastError(_T("GetDiskFreeSpaceEx"));
528
529 return FALSE;
530 }
531
221e1181
VZ
532 // ULARGE_INTEGER is a union of a 64 bit value and a struct containing
533 // two 32 bit fields which may be or may be not named - try to make it
534 // compile in all cases
535#if defined(__BORLANDC__) && !defined(_ANONYMOUS_STRUCT)
536 #define UL(ul) ul.u
537#else // anon union
538 #define UL(ul) ul
539#endif
eadd7bd2
VZ
540 if ( pTotal )
541 {
221e1181 542 *pTotal = wxLongLong(UL(bytesTotal).HighPart, UL(bytesTotal).LowPart);
eadd7bd2
VZ
543 }
544
545 if ( pFree )
546 {
221e1181 547 *pFree = wxLongLong(UL(bytesFree).HighPart, UL(bytesFree).LowPart);
eadd7bd2
VZ
548 }
549 }
550 else
551#endif // Win32
552 {
bb0e27ee
VZ
553 // there's a problem with drives larger than 2GB, GetDiskFreeSpaceEx()
554 // should be used instead - but if it's not available, fall back on
555 // GetDiskFreeSpace() nevertheless...
556
eadd7bd2
VZ
557 DWORD lSectorsPerCluster,
558 lBytesPerSector,
559 lNumberOfFreeClusters,
560 lTotalNumberOfClusters;
561
562 // FIXME: this is wrong, we should extract the root drive from path
563 // instead, but this is the job for wxFileName...
564 if ( !::GetDiskFreeSpace(path,
565 &lSectorsPerCluster,
566 &lBytesPerSector,
567 &lNumberOfFreeClusters,
568 &lTotalNumberOfClusters) )
569 {
570 wxLogLastError(_T("GetDiskFreeSpace"));
571
572 return FALSE;
573 }
574
eadd7bd2
VZ
575 wxLongLong lBytesPerCluster = lSectorsPerCluster;
576 lBytesPerCluster *= lBytesPerSector;
577
578 if ( pTotal )
579 {
580 *pTotal = lBytesPerCluster;
581 *pTotal *= lTotalNumberOfClusters;
582 }
583
584 if ( pFree )
585 {
586 *pFree = lBytesPerCluster;
587 *pFree *= lNumberOfFreeClusters;
588 }
589 }
590
591 return TRUE;
4676948b
JS
592#endif
593 // __WXWINCE__
eadd7bd2
VZ
594}
595
308978f6
VZ
596// ----------------------------------------------------------------------------
597// env vars
598// ----------------------------------------------------------------------------
599
600bool wxGetEnv(const wxString& var, wxString *value)
601{
4676948b
JS
602#ifdef __WXWINCE__
603 return FALSE;
604#elif defined(__WIN16__)
788722ac 605 const wxChar* ret = wxGetenv(var);
e622dd68
VZ
606 if ( !ret )
607 return FALSE;
608
609 if ( value )
788722ac
JS
610 {
611 *value = ret;
788722ac 612 }
e622dd68
VZ
613
614 return TRUE;
615#else // Win32
308978f6
VZ
616 // first get the size of the buffer
617 DWORD dwRet = ::GetEnvironmentVariable(var, NULL, 0);
618 if ( !dwRet )
619 {
620 // this means that there is no such variable
621 return FALSE;
622 }
623
624 if ( value )
625 {
de564874
MB
626 (void)::GetEnvironmentVariable(var, wxStringBuffer(*value, dwRet),
627 dwRet);
308978f6
VZ
628 }
629
630 return TRUE;
e622dd68 631#endif // Win16/32
308978f6
VZ
632}
633
1fb45475
VZ
634bool wxSetEnv(const wxString& var, const wxChar *value)
635{
636 // some compilers have putenv() or _putenv() or _wputenv() but it's better
637 // to always use Win32 function directly instead of dealing with them
4676948b 638#if defined(__WIN32__) && !defined(__WXWINCE__)
1fb45475
VZ
639 if ( !::SetEnvironmentVariable(var, value) )
640 {
641 wxLogLastError(_T("SetEnvironmentVariable"));
642
643 return FALSE;
644 }
645
646 return TRUE;
647#else // no way to set env vars
648 return FALSE;
649#endif
650}
651
b568d04f
VZ
652// ----------------------------------------------------------------------------
653// process management
654// ----------------------------------------------------------------------------
655
50567b69
VZ
656// structure used to pass parameters from wxKill() to wxEnumFindByPidProc()
657struct wxFindByPidParams
b568d04f 658{
50567b69
VZ
659 wxFindByPidParams() { hwnd = 0; pid = 0; }
660
661 // the HWND used to return the result
662 HWND hwnd;
663
664 // the PID we're looking from
665 DWORD pid;
22f3361e
VZ
666
667 DECLARE_NO_COPY_CLASS(wxFindByPidParams)
50567b69
VZ
668};
669
670// wxKill helper: EnumWindows() callback which is used to find the first (top
671// level) window belonging to the given process
672BOOL CALLBACK wxEnumFindByPidProc(HWND hwnd, LPARAM lParam)
673{
674 DWORD pid;
675 (void)::GetWindowThreadProcessId(hwnd, &pid);
676
677 wxFindByPidParams *params = (wxFindByPidParams *)lParam;
678 if ( pid == params->pid )
679 {
680 // remember the window we found
681 params->hwnd = hwnd;
682
683 // return FALSE to stop the enumeration
684 return FALSE;
685 }
686
687 // continue enumeration
688 return TRUE;
689}
e949bf13 690
50567b69
VZ
691int wxKill(long pid, wxSignal sig, wxKillError *krc)
692{
50567b69
VZ
693 // get the process handle to operate on
694 HANDLE hProcess = ::OpenProcess(SYNCHRONIZE |
695 PROCESS_TERMINATE |
696 PROCESS_QUERY_INFORMATION,
697 FALSE, // not inheritable
698 (DWORD)pid);
699 if ( hProcess == NULL )
e949bf13 700 {
50567b69 701 if ( krc )
e949bf13 702 {
50567b69 703 if ( ::GetLastError() == ERROR_ACCESS_DENIED )
e949bf13 704 {
50567b69
VZ
705 *krc = wxKILL_ACCESS_DENIED;
706 }
707 else
708 {
709 *krc = wxKILL_NO_PROCESS;
710 }
711 }
712
713 return -1;
714 }
715
716 bool ok = TRUE;
717 switch ( sig )
718 {
719 case wxSIGKILL:
720 // kill the process forcefully returning -1 as error code
721 if ( !::TerminateProcess(hProcess, (UINT)-1) )
722 {
723 wxLogSysError(_("Failed to kill process %d"), pid);
724
725 if ( krc )
e949bf13 726 {
50567b69
VZ
727 // this is not supposed to happen if we could open the
728 // process
729 *krc = wxKILL_ERROR;
e949bf13 730 }
50567b69
VZ
731
732 ok = FALSE;
e949bf13 733 }
50567b69
VZ
734 break;
735
736 case wxSIGNONE:
737 // do nothing, we just want to test for process existence
738 break;
739
740 default:
741 // any other signal means "terminate"
742 {
743 wxFindByPidParams params;
744 params.pid = (DWORD)pid;
745
746 // EnumWindows() has nice semantics: it returns 0 if it found
747 // something or if an error occured and non zero if it
748 // enumerated all the window
749 if ( !::EnumWindows(wxEnumFindByPidProc, (LPARAM)&params) )
750 {
751 // did we find any window?
752 if ( params.hwnd )
753 {
754 // tell the app to close
755 //
756 // NB: this is the harshest way, the app won't have
757 // opportunity to save any files, for example, but
758 // this is probably what we want here. If not we
759 // can also use SendMesageTimeout(WM_CLOSE)
760 if ( !::PostMessage(params.hwnd, WM_QUIT, 0, 0) )
761 {
762 wxLogLastError(_T("PostMessage(WM_QUIT)"));
763 }
764 }
765 else // it was an error then
766 {
767 wxLogLastError(_T("EnumWindows"));
768
769 ok = FALSE;
770 }
771 }
772 else // no windows for this PID
773 {
774 if ( krc )
775 {
776 *krc = wxKILL_ERROR;
777 }
778
779 ok = FALSE;
780 }
781 }
782 }
783
784 // the return code
785 DWORD rc;
786
787 if ( ok )
788 {
789 // as we wait for a short time, we can use just WaitForSingleObject()
790 // and not MsgWaitForMultipleObjects()
791 switch ( ::WaitForSingleObject(hProcess, 500 /* msec */) )
792 {
793 case WAIT_OBJECT_0:
794 // process terminated
795 if ( !::GetExitCodeProcess(hProcess, &rc) )
796 {
797 wxLogLastError(_T("GetExitCodeProcess"));
798 }
799 break;
800
801 default:
802 wxFAIL_MSG( _T("unexpected WaitForSingleObject() return") );
803 // fall through
804
805 case WAIT_FAILED:
806 wxLogLastError(_T("WaitForSingleObject"));
807 // fall through
808
809 case WAIT_TIMEOUT:
810 if ( krc )
811 {
812 *krc = wxKILL_ERROR;
813 }
814
815 rc = STILL_ACTIVE;
816 break;
e949bf13
JS
817 }
818 }
50567b69
VZ
819 else // !ok
820 {
821 // just to suppress the warnings about uninitialized variable
822 rc = 0;
823 }
e949bf13 824
50567b69 825 ::CloseHandle(hProcess);
b568d04f 826
50567b69
VZ
827 // the return code is the same as from Unix kill(): 0 if killed
828 // successfully or -1 on error
b1a28eef
VZ
829 //
830 // be careful to interpret rc correctly: for wxSIGNONE we return success if
831 // the process exists, for all the other sig values -- if it doesn't
832 if ( ok &&
833 ((sig == wxSIGNONE) == (rc == STILL_ACTIVE)) )
e949bf13 834 {
b1a28eef 835 if ( krc )
50567b69 836 {
b1a28eef 837 *krc = wxKILL_OK;
50567b69 838 }
b1a28eef
VZ
839
840 return 0;
e949bf13 841 }
50567b69
VZ
842
843 // error
844 return -1;
2bda0e17
KB
845}
846
b568d04f
VZ
847// Execute a program in an Interactive Shell
848bool wxShell(const wxString& command)
2bda0e17 849{
4676948b
JS
850#ifdef __WXWINCE__
851 return FALSE;
852#else
b568d04f
VZ
853 wxChar *shell = wxGetenv(wxT("COMSPEC"));
854 if ( !shell )
ba14d986 855 shell = (wxChar*) wxT("\\COMMAND.COM");
b568d04f
VZ
856
857 wxString cmd;
858 if ( !command )
859 {
860 // just the shell
861 cmd = shell;
862 }
863 else
864 {
865 // pass the command to execute to the command processor
866 cmd.Printf(wxT("%s /c %s"), shell, command.c_str());
867 }
868
011a524e 869 return wxExecute(cmd, wxEXEC_SYNC) == 0;
4676948b 870#endif
2bda0e17
KB
871}
872
d8c65cf4 873// Shutdown or reboot the PC
f6ba47d9
VZ
874bool wxShutdown(wxShutdownFlags wFlags)
875{
4676948b
JS
876#ifdef __WXWINCE__
877 return FALSE;
878#elif defined(__WIN32__)
f6ba47d9
VZ
879 bool bOK = TRUE;
880
881 if ( wxGetOsVersion(NULL, NULL) == wxWINDOWS_NT ) // if is NT or 2K
882 {
d8c65cf4
RD
883 // Get a token for this process.
884 HANDLE hToken;
f6ba47d9
VZ
885 bOK = ::OpenProcessToken(GetCurrentProcess(),
886 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
887 &hToken) != 0;
888 if ( bOK )
889 {
d8c65cf4 890 TOKEN_PRIVILEGES tkp;
f6ba47d9 891
d8c65cf4 892 // Get the LUID for the shutdown privilege.
f6ba47d9 893 ::LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
d8c65cf4 894 &tkp.Privileges[0].Luid);
f6ba47d9 895
d8c65cf4
RD
896 tkp.PrivilegeCount = 1; // one privilege to set
897 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
f6ba47d9 898
d8c65cf4 899 // Get the shutdown privilege for this process.
f6ba47d9 900 ::AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
d8c65cf4 901 (PTOKEN_PRIVILEGES)NULL, 0);
f6ba47d9 902
d8c65cf4 903 // Cannot test the return value of AdjustTokenPrivileges.
f6ba47d9
VZ
904 bOK = ::GetLastError() == ERROR_SUCCESS;
905 }
906 }
907
908 if ( bOK )
909 {
910 UINT flags = EWX_SHUTDOWN | EWX_FORCE;
911 switch ( wFlags )
912 {
913 case wxSHUTDOWN_POWEROFF:
914 flags |= EWX_POWEROFF;
915 break;
916
917 case wxSHUTDOWN_REBOOT:
918 flags |= EWX_REBOOT;
919 break;
920
921 default:
922 wxFAIL_MSG( _T("unknown wxShutdown() flag") );
923 return FALSE;
924 }
925
999836aa 926 bOK = ::ExitWindowsEx(flags, 0) != 0;
f6ba47d9
VZ
927 }
928
929 return bOK;
930#else // Win16
931 return FALSE;
932#endif // Win32/16
933}
934
b568d04f
VZ
935// ----------------------------------------------------------------------------
936// misc
937// ----------------------------------------------------------------------------
938
939// Get free memory in bytes, or -1 if cannot determine amount (e.g. on UNIX)
940long wxGetFreeMemory()
2bda0e17 941{
b39dbf34 942#if defined(__WIN32__) && !defined(__BORLANDC__)
b568d04f
VZ
943 MEMORYSTATUS memStatus;
944 memStatus.dwLength = sizeof(MEMORYSTATUS);
945 GlobalMemoryStatus(&memStatus);
946 return memStatus.dwAvailPhys;
947#else
948 return (long)GetFreeSpace(0);
949#endif
2bda0e17
KB
950}
951
c1cb4153
VZ
952unsigned long wxGetProcessId()
953{
954#ifdef __WIN32__
955 return ::GetCurrentProcessId();
956#else
957 return 0;
958#endif
959}
960
2bda0e17 961// Emit a beeeeeep
634903fd 962void wxBell()
2bda0e17 963{
b568d04f 964 ::MessageBeep((UINT)-1); // default sound
2bda0e17
KB
965}
966
bdc72a22 967wxString wxGetOsDescription()
2bda0e17 968{
bdc72a22
VZ
969#ifdef __WIN32__
970 wxString str;
971
972 OSVERSIONINFO info;
973 wxZeroMemory(info);
974
975 info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
976 if ( ::GetVersionEx(&info) )
977 {
978 switch ( info.dwPlatformId )
979 {
980 case VER_PLATFORM_WIN32s:
981 str = _("Win32s on Windows 3.1");
982 break;
983
984 case VER_PLATFORM_WIN32_WINDOWS:
985 str.Printf(_("Windows 9%c"),
a46a73a6 986 info.dwMinorVersion == 0 ? _T('5') : _T('8'));
bdc72a22
VZ
987 if ( !wxIsEmpty(info.szCSDVersion) )
988 {
989 str << _T(" (") << info.szCSDVersion << _T(')');
990 }
991 break;
992
993 case VER_PLATFORM_WIN32_NT:
994 str.Printf(_T("Windows NT %lu.%lu (build %lu"),
995 info.dwMajorVersion,
996 info.dwMinorVersion,
997 info.dwBuildNumber);
998 if ( !wxIsEmpty(info.szCSDVersion) )
999 {
1000 str << _T(", ") << info.szCSDVersion;
1001 }
1002 str << _T(')');
1003 break;
1004 }
1005 }
1006 else
1007 {
1008 wxFAIL_MSG( _T("GetVersionEx() failed") ); // should never happen
1009 }
1010
1011 return str;
1012#else // Win16
1013 return _("Windows 3.1");
1014#endif // Win32/16
1015}
6f65e337 1016
324899f6 1017wxToolkitInfo& wxAppTraits::GetToolkitInfo()
bdc72a22 1018{
2739d4f0
VZ
1019 // cache the version info, it's not going to change
1020 //
1021 // NB: this is MT-safe, we may use these static vars from different threads
1022 // but as they always have the same value it doesn't matter
1023 static int s_ver = -1,
1024 s_major = -1,
1025 s_minor = -1;
96c21216 1026
2739d4f0 1027 if ( s_ver == -1 )
bdc72a22 1028 {
6af638ed
VS
1029 OSVERSIONINFO info;
1030 wxZeroMemory(info);
ec5d7799 1031
2739d4f0 1032 s_ver = wxWINDOWS;
6af638ed
VS
1033 info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1034 if ( ::GetVersionEx(&info) )
bdc72a22 1035 {
2739d4f0
VZ
1036 s_major = info.dwMajorVersion;
1037 s_minor = info.dwMinorVersion;
bdc72a22 1038
6af638ed
VS
1039 switch ( info.dwPlatformId )
1040 {
1041 case VER_PLATFORM_WIN32s:
2739d4f0 1042 s_ver = wxWIN32S;
6af638ed 1043 break;
bdc72a22 1044
6af638ed 1045 case VER_PLATFORM_WIN32_WINDOWS:
2739d4f0 1046 s_ver = wxWIN95;
6af638ed
VS
1047 break;
1048
1049 case VER_PLATFORM_WIN32_NT:
2739d4f0 1050 s_ver = wxWINDOWS_NT;
6af638ed 1051 break;
471085e4 1052#ifdef __WXWINCE__
f07dc2e2
JS
1053 case VER_PLATFORM_WIN32_CE:
1054 s_ver = wxWINDOWS_CE;
1055 break;
eccd1992 1056#endif
6af638ed 1057 }
bdc72a22
VZ
1058 }
1059 }
1060
eccd1992 1061 static wxToolkitInfo info;
a8eaaeb2
VS
1062 info.versionMajor = s_major;
1063 info.versionMinor = s_minor;
1064 info.os = s_ver;
1065 info.name = _T("wxBase");
324899f6 1066 return info;
2bda0e17
KB
1067}
1068
b568d04f
VZ
1069// ----------------------------------------------------------------------------
1070// sleep functions
1071// ----------------------------------------------------------------------------
1072
b568d04f
VZ
1073void wxUsleep(unsigned long milliseconds)
1074{
1075 ::Sleep(milliseconds);
1076}
1077
1078void wxSleep(int nSecs)
1079{
1080 wxUsleep(1000*nSecs);
1081}
1082
b568d04f 1083// ----------------------------------------------------------------------------
e2478fde 1084// font encoding <-> Win32 codepage conversion functions
b568d04f
VZ
1085// ----------------------------------------------------------------------------
1086
bddd7a8d 1087extern WXDLLIMPEXP_BASE long wxEncodingToCharset(wxFontEncoding encoding)
b568d04f 1088{
e2478fde 1089 switch ( encoding )
b568d04f 1090 {
e2478fde
VZ
1091 // although this function is supposed to return an exact match, do do
1092 // some mappings here for the most common case of "standard" encoding
1093 case wxFONTENCODING_SYSTEM:
1094 return DEFAULT_CHARSET;
2bda0e17 1095
e2478fde
VZ
1096 case wxFONTENCODING_ISO8859_1:
1097 case wxFONTENCODING_ISO8859_15:
1098 case wxFONTENCODING_CP1252:
1099 return ANSI_CHARSET;
2bda0e17 1100
e2478fde
VZ
1101#if !defined(__WXMICROWIN__)
1102 // The following four fonts are multi-byte charsets
1103 case wxFONTENCODING_CP932:
1104 return SHIFTJIS_CHARSET;
2bda0e17 1105
e2478fde
VZ
1106 case wxFONTENCODING_CP936:
1107 return GB2312_CHARSET;
2bda0e17 1108
e2478fde
VZ
1109 case wxFONTENCODING_CP949:
1110 return HANGUL_CHARSET;
634903fd 1111
e2478fde
VZ
1112 case wxFONTENCODING_CP950:
1113 return CHINESEBIG5_CHARSET;
2bda0e17 1114
e2478fde
VZ
1115 // The rest are single byte encodings
1116 case wxFONTENCODING_CP1250:
1117 return EASTEUROPE_CHARSET;
bfbd6dc1 1118
e2478fde
VZ
1119 case wxFONTENCODING_CP1251:
1120 return RUSSIAN_CHARSET;
2bda0e17 1121
e2478fde
VZ
1122 case wxFONTENCODING_CP1253:
1123 return GREEK_CHARSET;
b07135ba 1124
e2478fde
VZ
1125 case wxFONTENCODING_CP1254:
1126 return TURKISH_CHARSET;
2bda0e17 1127
e2478fde
VZ
1128 case wxFONTENCODING_CP1255:
1129 return HEBREW_CHARSET;
2bda0e17 1130
e2478fde
VZ
1131 case wxFONTENCODING_CP1256:
1132 return ARABIC_CHARSET;
b568d04f 1133
e2478fde
VZ
1134 case wxFONTENCODING_CP1257:
1135 return BALTIC_CHARSET;
634903fd 1136
e2478fde
VZ
1137 case wxFONTENCODING_CP874:
1138 return THAI_CHARSET;
5acc0d5f 1139#endif // !__WXMICROWIN__
cc2b7472 1140
e2478fde
VZ
1141 case wxFONTENCODING_CP437:
1142 return OEM_CHARSET;
ea28b885
VZ
1143
1144 default:
1145 // no way to translate this encoding into a Windows charset
69557827 1146 return -1;
e2478fde 1147 }
373658eb 1148}
7f555861 1149
e2478fde
VZ
1150// we have 2 versions of wxCharsetToCodepage(): the old one which directly
1151// looks up the vlaues in the registry and the new one which is more
1152// politically correct and has more chances to work on other Windows versions
1153// as well but the old version is still needed for !wxUSE_FONTMAP case
1154#if wxUSE_FONTMAP
c030b70f 1155
373658eb 1156#include "wx/fontmap.h"
c030b70f 1157
bddd7a8d 1158extern WXDLLIMPEXP_BASE long wxEncodingToCodepage(wxFontEncoding encoding)
c030b70f 1159{
373658eb 1160 // translate encoding into the Windows CHARSET
e2478fde
VZ
1161 long charset = wxEncodingToCharset(encoding);
1162 if ( charset == -1 )
373658eb
VZ
1163 return -1;
1164
1165 // translate CHARSET to code page
1166 CHARSETINFO csetInfo;
e2478fde 1167 if ( !::TranslateCharsetInfo((DWORD *)(DWORD)charset,
373658eb
VZ
1168 &csetInfo,
1169 TCI_SRCCHARSET) )
1170 {
1171 wxLogLastError(_T("TranslateCharsetInfo(TCI_SRCCHARSET)"));
1172
1173 return -1;
1174 }
1175
1176 return csetInfo.ciACP;
c030b70f 1177}
373658eb 1178
373658eb 1179extern long wxCharsetToCodepage(const wxChar *name)
c030b70f 1180{
373658eb
VZ
1181 // first get the font encoding for this charset
1182 if ( !name )
1183 return -1;
1184
142b3bc2 1185 wxFontEncoding enc = wxFontMapper::Get()->CharsetToEncoding(name, FALSE);
373658eb
VZ
1186 if ( enc == wxFONTENCODING_SYSTEM )
1187 return -1;
1188
1189 // the use the helper function
1190 return wxEncodingToCodepage(enc);
c030b70f 1191}
c030b70f 1192
e2478fde 1193#else // !wxUSE_FONTMAP
373658eb
VZ
1194
1195#include "wx/msw/registry.h"
1196
1197// this should work if Internet Exploiter is installed
1198extern long wxCharsetToCodepage(const wxChar *name)
04ef50df 1199{
373658eb
VZ
1200 if (!name)
1201 return GetACP();
1202
e2478fde 1203 long CP = -1;
373658eb 1204
e2478fde 1205 wxString path(wxT("MIME\\Database\\Charset\\"));
373658eb 1206 wxString cn(name);
373658eb 1207
e2478fde
VZ
1208 // follow the alias loop
1209 for ( ;; )
1210 {
1211 wxRegKey key(wxRegKey::HKCR, path + cn);
1212
1213 if (!key.Exists())
1214 break;
373658eb
VZ
1215
1216 // two cases: either there's an AliasForCharset string,
1217 // or there are Codepage and InternetEncoding dwords.
1218 // The InternetEncoding gives us the actual encoding,
1219 // the Codepage just says which Windows character set to
1220 // use when displaying the data.
1221 if (key.HasValue(wxT("InternetEncoding")) &&
e2478fde
VZ
1222 key.QueryValue(wxT("InternetEncoding"), &CP))
1223 break;
373658eb
VZ
1224
1225 // no encoding, see if it's an alias
1226 if (!key.HasValue(wxT("AliasForCharset")) ||
e2478fde
VZ
1227 !key.QueryValue(wxT("AliasForCharset"), cn))
1228 break;
1229 }
373658eb
VZ
1230
1231 return CP;
04ef50df 1232}
373658eb 1233
e2478fde 1234#endif // wxUSE_FONTMAP/!wxUSE_FONTMAP
c030b70f 1235
eccd1992
VZ
1236/*
1237 Creates a hidden window with supplied window proc registering the class for
1238 it if necesssary (i.e. the first time only). Caller is responsible for
1239 destroying the window and unregistering the class (note that this must be
1240 done because wxWindows may be used as a DLL and so may be loaded/unloaded
1241 multiple times into/from the same process so we cna't rely on automatic
1242 Windows class unregistration).
1243
1244 pclassname is a pointer to a caller stored classname, which must initially be
1245 NULL. classname is the desired wndclass classname. If function succesfully
1246 registers the class, pclassname will be set to classname.
1247 */
487f2d58 1248extern "C" WXDLLIMPEXP_BASE HWND
eccd1992
VZ
1249wxCreateHiddenWindow(LPCTSTR *pclassname, LPCTSTR classname, WNDPROC wndproc)
1250{
1251 wxCHECK_MSG( classname && pclassname && wndproc, NULL,
1252 _T("NULL parameter in wxCreateHiddenWindow") );
1253
1254 // register the class fi we need to first
1255 if ( *pclassname == NULL )
1256 {
1257 WNDCLASS wndclass;
1258 wxZeroMemory(wndclass);
1259
1260 wndclass.lpfnWndProc = wndproc;
1261 wndclass.hInstance = wxGetInstance();
1262 wndclass.lpszClassName = classname;
1263
1264 if ( !::RegisterClass(&wndclass) )
1265 {
1266 wxLogLastError(wxT("RegisterClass() in wxCreateHiddenWindow"));
1267
1268 return NULL;
1269 }
1270
1271 *pclassname = classname;
1272 }
1273
1274 // next create the window
1275 HWND hwnd = ::CreateWindow
1276 (
1277 *pclassname,
1278 NULL,
1279 0, 0, 0, 0,
1280 0,
1281 (HWND) NULL,
1282 (HMENU)NULL,
1283 wxGetInstance(),
1284 (LPVOID) NULL
1285 );
1286
1287 if ( !hwnd )
1288 {
1289 wxLogLastError(wxT("CreateWindow() in wxCreateHiddenWindow"));
1290 }
1291
1292 return hwnd;
1293}
1294