From afe58cfa890ca90d9d3a54e8bbea834ec3061f5e Mon Sep 17 00:00:00 2001 From: valerino Date: Thu, 20 May 2010 22:50:37 -0400 Subject: [PATCH 1/5] Don't use "try" as an identifier C allows try, but some windows CE headers like to redefine 'try' to be a reserved word. --- src/or/circuitbuild.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 233d60f15c..fff56f0386 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -2327,7 +2327,7 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, * possibly support any of them. Choose a router at random that satisfies * at least one predicted exit port. */ - int try; + int attempt; smartlist_t *needed_ports, *supporting, *use; if (best_support == -1) { @@ -2347,13 +2347,13 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, supporting = smartlist_create(); use = smartlist_create(); needed_ports = circuit_get_unhandled_ports(time(NULL)); - for (try = 0; try < 2; try++) { + for (attempt = 0; attempt < 2; attempt++) { /* try once to pick only from routers that satisfy a needed port, * then if there are none, pick from any that support exiting. */ for (i = 0; i < smartlist_len(dir->routers); i++) { router = smartlist_get(dir->routers, i); if (n_supported[i] != -1 && - (try || router_handles_some_port(router, needed_ports))) { + (attempt || router_handles_some_port(router, needed_ports))) { // log_fn(LOG_DEBUG,"Try %d: '%s' is a possibility.", // try, router->nickname); smartlist_add(supporting, router); From ddf250119df44927c424512f286a3255aea1d16b Mon Sep 17 00:00:00 2001 From: valerino Date: Thu, 20 May 2010 22:51:47 -0400 Subject: [PATCH 2/5] Log the correct address when purging a mismatchd DNS cache address --- src/or/dns.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/or/dns.c b/src/or/dns.c index 192a1929d7..fa26cf062a 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -453,7 +453,7 @@ purge_expired_resolves(time_t now) log_err(LD_BUG, "The expired resolve we purged didn't match any in" " the cache. Tried to purge %s (%p); instead got %s (%p).", resolve->address, (void*)resolve, - removed ? removed->address : "NULL", (void*)remove); + removed ? removed->address : "NULL", (void*)removed); } tor_assert(removed == resolve); } else { From 8d31141ccbdbeee9589d04ea99819af7aa35193b Mon Sep 17 00:00:00 2001 From: valerino Date: Thu, 20 May 2010 22:53:39 -0400 Subject: [PATCH 3/5] Port Tor to work on Windows CE Most of the changes here are switches to use APIs available on Windows CE. The most pervasive change is that Windows CE only provides the wide-character ("FooW") variants of most of the windows function, and doesn't support the older ASCII verions at all. This patch will require use of the wcecompat library to get working versions of the posix-style fd-based file IO functions. [commit message by nickm] --- src/common/compat.c | 46 ++++++++++++++++++++++++++----------- src/common/compat.h | 4 ++-- src/common/util.c | 21 ++++++++++------- src/or/config.c | 11 +++++++-- src/or/eventdns.c | 32 ++++++++++++++++---------- src/or/main.c | 56 +++++++++++++++++++++++++++++++++++++++++++++ src/or/or.h | 2 ++ 7 files changed, 135 insertions(+), 37 deletions(-) diff --git a/src/common/compat.c b/src/common/compat.c index 0fb169b734..f93a1619ce 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -22,8 +22,10 @@ #ifdef MS_WINDOWS #include #include +#if !defined (WINCE) #include #endif +#endif #ifdef HAVE_UNAME #include @@ -169,12 +171,13 @@ tor_munmap_file(tor_mmap_t *handle) tor_mmap_t * tor_mmap_file(const char *filename) { + WCHAR wfilename[MAX_PATH]= {0}; tor_mmap_t *res = tor_malloc_zero(sizeof(tor_mmap_t)); int empty = 0; res->file_handle = INVALID_HANDLE_VALUE; res->mmap_handle = NULL; - - res->file_handle = CreateFile(filename, + mbstowcs(wfilename,filename,MAX_PATH); + res->file_handle = CreateFileW(wfilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, @@ -1697,10 +1700,15 @@ get_uname(void) #endif { #ifdef MS_WINDOWS - OSVERSIONINFOEX info; +#if defined (WINCE) + OSVERSIONINFO info; +#else + OSVERSIONINFOEXW info; +#endif int i; const char *plat = NULL; const char *extra = NULL; + char acsd[MAX_PATH] = {0}; static struct { unsigned major; unsigned minor; const char *version; } win_version_table[] = { @@ -1718,20 +1726,21 @@ get_uname(void) }; memset(&info, 0, sizeof(info)); info.dwOSVersionInfoSize = sizeof(info); - if (! GetVersionEx((LPOSVERSIONINFO)&info)) { + if (! GetVersionExW((LPOSVERSIONINFO)&info)) { strlcpy(uname_result, "Bizarre version of Windows where GetVersionEx" " doesn't work.", sizeof(uname_result)); uname_result_is_set = 1; return uname_result; } + wcstombs(acsd, info.szCSDVersion, MAX_PATH); if (info.dwMajorVersion == 4 && info.dwMinorVersion == 0) { if (info.dwPlatformId == VER_PLATFORM_WIN32_NT) plat = "Windows NT 4.0"; else plat = "Windows 95"; - if (info.szCSDVersion[1] == 'B') + if (acsd[1] == 'B') extra = "OSR2 (B)"; - else if (info.szCSDVersion[1] == 'C') + else if (acsd[1] == 'C') extra = "OSR2 (C)"; } else { for (i=0; win_version_table[i].major>0; ++i) { @@ -1743,14 +1752,14 @@ get_uname(void) } } if (plat && !strcmp(plat, "Windows 98")) { - if (info.szCSDVersion[1] == 'A') + if (acsd[1] == 'A') extra = "SE (A)"; - else if (info.szCSDVersion[1] == 'B') + else if (acsd[1] == 'B') extra = "SE (B)"; } if (plat) { if (!extra) - extra = info.szCSDVersion; + extra = acsd; tor_snprintf(uname_result, sizeof(uname_result), "%s %s", plat, extra); } else { @@ -1759,13 +1768,14 @@ get_uname(void) tor_snprintf(uname_result, sizeof(uname_result), "Very recent version of Windows [major=%d,minor=%d] %s", (int)info.dwMajorVersion,(int)info.dwMinorVersion, - info.szCSDVersion); + acsd); else tor_snprintf(uname_result, sizeof(uname_result), "Unrecognized version of Windows [major=%d,minor=%d] %s", (int)info.dwMajorVersion,(int)info.dwMinorVersion, - info.szCSDVersion); + acsd); } +#if !defined (WINCE) #ifdef VER_SUITE_BACKOFFICE if (info.wProductType == VER_NT_DOMAIN_CONTROLLER) { strlcat(uname_result, " [domain controller]", sizeof(uname_result)); @@ -1775,6 +1785,7 @@ get_uname(void) strlcat(uname_result, " [workstation]", sizeof(uname_result)); } #endif +#endif #else strlcpy(uname_result, "Unknown platform", sizeof(uname_result)); #endif @@ -1902,8 +1913,15 @@ tor_gettimeofday(struct timeval *timeval) uint64_t ft_64; FILETIME ft_ft; } ft; +#if defined (WINCE) + /* wince do not have GetSystemTimeAsFileTime */ + SYSTEMTIME stime; + GetSystemTime(&stime); + SystemTimeToFileTime(&stime,&ft.ft_ft); +#else /* number of 100-nsec units since Jan 1, 1601 */ GetSystemTimeAsFileTime(&ft.ft_ft); +#endif if (ft.ft_64 < EPOCH_BIAS) { log_err(LD_GENERAL,"System time is before 1970; failing."); exit(1); @@ -2515,10 +2533,11 @@ char * format_win32_error(DWORD err) { LPVOID str = NULL; + char abuf[1024] = {0}; char *result; /* Somebody once decided that this interface was better than strerror(). */ - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, @@ -2527,7 +2546,8 @@ format_win32_error(DWORD err) 0, NULL); if (str) { - result = tor_strdup((char*)str); + wcstombs(abuf,str,1024); + result = tor_strdup((char*)abuf); LocalFree(str); /* LocalFree != free() */ } else { result = tor_strdup(""); diff --git a/src/common/compat.h b/src/common/compat.h index dbadd60509..077256e401 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -177,8 +177,8 @@ extern INLINE double U64_TO_DBL(uint64_t x) { /* ===== String compatibility */ #ifdef MS_WINDOWS /* Windows names string functions differently from most other platforms. */ -#define strncasecmp strnicmp -#define strcasecmp stricmp +#define strncasecmp _strnicmp +#define strcasecmp _stricmp #endif #ifndef HAVE_STRLCAT size_t strlcat(char *dst, const char *src, size_t siz) ATTR_NONNULL((1,2)); diff --git a/src/common/util.c b/src/common/util.c index 1c0db392d0..fe48e93284 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -1707,7 +1707,7 @@ check_private_dir(const char *dirname, cpd_check_t check) return -1; } else if (check == CPD_CREATE) { log_info(LD_GENERAL, "Creating directory %s", dirname); -#ifdef MS_WINDOWS +#if defined (MS_WINDOWS) && !defined (WINCE) r = mkdir(dirname); #else r = mkdir(dirname, 0700); @@ -1843,7 +1843,8 @@ start_writing_to_file(const char *fname, int open_flags, int mode, if (open_flags & O_BINARY) new_file->binary = 1; - if ((new_file->fd = open(open_name, open_flags, mode)) < 0) { + new_file->fd = open(open_name, open_flags, mode); + if (new_file->fd < 0) { log_warn(LD_FS, "Couldn't open \"%s\" (%s) for writing: %s", open_name, fname, strerror(errno)); goto err; @@ -2526,22 +2527,26 @@ tor_listdir(const char *dirname) smartlist_t *result; #ifdef MS_WINDOWS char *pattern; + WCHAR wpattern[MAX_PATH] = {0}; + char name[MAX_PATH] = {0}; HANDLE handle; - WIN32_FIND_DATA findData; + WIN32_FIND_DATAW findData; size_t pattern_len = strlen(dirname)+16; pattern = tor_malloc(pattern_len); tor_snprintf(pattern, pattern_len, "%s\\*", dirname); - if (INVALID_HANDLE_VALUE == (handle = FindFirstFile(pattern, &findData))) { + mbstowcs(wpattern,pattern,MAX_PATH); + if (INVALID_HANDLE_VALUE == (handle = FindFirstFileW(wpattern, &findData))) { tor_free(pattern); return NULL; } + wcstombs(name,findData.cFileName,MAX_PATH); result = smartlist_create(); while (1) { - if (strcmp(findData.cFileName, ".") && - strcmp(findData.cFileName, "..")) { - smartlist_add(result, tor_strdup(findData.cFileName)); + if (strcmp(name, ".") && + strcmp(name, "..")) { + smartlist_add(result, tor_strdup(name)); } - if (!FindNextFile(handle, &findData)) { + if (!FindNextFileW(handle, &findData)) { DWORD err; if ((err = GetLastError()) != ERROR_NO_MORE_FILES) { char *errstr = format_win32_error(err); diff --git a/src/or/config.c b/src/or/config.c index 5d07cd7343..82184c77d6 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -217,8 +217,12 @@ static config_var_t _option_vars[] = { V(ExitPortStatistics, BOOL, "0"), V(ExtraInfoStatistics, BOOL, "0"), +#if defined (WINCE) + V(FallbackNetworkstatusFile, FILENAME, "fallback-consensus"), +#else V(FallbackNetworkstatusFile, FILENAME, SHARE_DATADIR PATH_SEPARATOR "tor" PATH_SEPARATOR "fallback-consensus"), +#endif V(FascistFirewall, BOOL, "0"), V(FirewallPorts, CSV, ""), V(FastFirstHopPK, BOOL, "1"), @@ -3697,6 +3701,7 @@ get_windows_conf_root(void) { static int is_set = 0; static char path[MAX_PATH+1]; + WCHAR wpath[MAX_PATH] = {0}; LPITEMIDLIST idl; IMalloc *m; @@ -3714,7 +3719,7 @@ get_windows_conf_root(void) #define APPDATA_PATH CSIDL_APPDATA #endif if (!SUCCEEDED(SHGetSpecialFolderLocation(NULL, APPDATA_PATH, &idl))) { - GetCurrentDirectory(MAX_PATH, path); + getcwd(path,MAX_PATH); is_set = 1; log_warn(LD_CONFIG, "I couldn't find your application data folder: are you " @@ -3723,7 +3728,9 @@ get_windows_conf_root(void) return path; } /* Convert the path from an "ID List" (whatever that is!) to a path. */ - result = SHGetPathFromIDList(idl, path); + result = SHGetPathFromIDListW(idl, wpath); + wcstombs(path,wpath,MAX_PATH); + /* Now we need to free the */ SHGetMalloc(&m); if (m) { diff --git a/src/or/eventdns.c b/src/or/eventdns.c index 06add11b1d..4e44d15163 100644 --- a/src/or/eventdns.c +++ b/src/or/eventdns.c @@ -3132,13 +3132,13 @@ load_nameservers_with_getnetworkparams(void) GetNetworkParams_fn_t fn; /* XXXX Possibly, we should hardcode the location of this DLL. */ - if (!(handle = LoadLibrary("iphlpapi.dll"))) { + if (!(handle = LoadLibraryW(L"iphlpapi.dll"))) { log(EVDNS_LOG_WARN, "Could not open iphlpapi.dll"); /* right now status = 0, doesn't that mean "good" - mikec */ status = -1; goto done; } - if (!(fn = (GetNetworkParams_fn_t) GetProcAddress(handle, "GetNetworkParams"))) { + if (!(fn = (GetNetworkParams_fn_t) GetProcAddress(handle, TEXT("GetNetworkParams")))) { log(EVDNS_LOG_WARN, "Could not get address of function."); /* same as above */ status = -1; @@ -3205,32 +3205,40 @@ config_nameserver_from_reg_key(HKEY key, const char *subkey) { char *buf; DWORD bufsz = 0, type = 0; + WCHAR wsubkey[MAX_PATH] = {0}; + char ansibuf[MAX_PATH] = {0}; int status = 0; - if (RegQueryValueEx(key, subkey, 0, &type, NULL, &bufsz) + mbstowcs(wsubkey,subkey,MAX_PATH); + if (RegQueryValueExW(key, wsubkey, 0, &type, NULL, &bufsz) != ERROR_MORE_DATA) return -1; if (!(buf = mm_malloc(bufsz))) return -1; - if (RegQueryValueEx(key, subkey, 0, &type, (LPBYTE)buf, &bufsz) + if (RegQueryValueExW(key, wsubkey, 0, &type, (LPBYTE)buf, &bufsz) == ERROR_SUCCESS && bufsz > 1) { - status = evdns_nameserver_ip_add_line(buf); + wcstombs(ansibuf,(wchar_t*)buf,MAX_PATH); + status = evdns_nameserver_ip_add_line(ansibuf); } mm_free(buf); return status; } -#define SERVICES_KEY "System\\CurrentControlSet\\Services\\" -#define WIN_NS_9X_KEY SERVICES_KEY "VxD\\MSTCP" -#define WIN_NS_NT_KEY SERVICES_KEY "Tcpip\\Parameters" +#define SERVICES_KEY L"System\\CurrentControlSet\\Services\\" +#define WIN_NS_9X_KEY SERVICES_KEY L"VxD\\MSTCP" +#define WIN_NS_NT_KEY SERVICES_KEY L"Tcpip\\Parameters" static int load_nameservers_from_registry(void) { int found = 0; int r; + OSVERSIONINFO info = {0}; + info.dwOSVersionInfoSize = sizeof (info); + GetVersionExW((LPOSVERSIONINFO)&info); + #define TRY(k, name) \ if (!found && config_nameserver_from_reg_key(k,name) == 0) { \ log(EVDNS_LOG_DEBUG,"Found nameservers in %s/%s",#k,name); \ @@ -3240,15 +3248,15 @@ load_nameservers_from_registry(void) #k,#name); \ } - if (((int)GetVersion()) > 0) { /* NT */ + if (info.dwMajorVersion >= 5) { /* NT */ HKEY nt_key = 0, interfaces_key = 0; - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, &nt_key) != ERROR_SUCCESS) { log(EVDNS_LOG_DEBUG,"Couldn't open nt key, %d",(int)GetLastError()); return -1; } - r = RegOpenKeyEx(nt_key, "Interfaces", 0, + r = RegOpenKeyExW(nt_key, L"Interfaces", 0, KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS, &interfaces_key); if (r != ERROR_SUCCESS) { @@ -3263,7 +3271,7 @@ load_nameservers_from_registry(void) RegCloseKey(nt_key); } else { HKEY win_key = 0; - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_9X_KEY, 0, + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, WIN_NS_9X_KEY, 0, KEY_READ, &win_key) != ERROR_SUCCESS) { log(EVDNS_LOG_DEBUG, "Couldn't open registry key, %d", (int)GetLastError()); return -1; diff --git a/src/or/main.c b/src/or/main.c index 0ddb65a749..542383526c 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -24,6 +24,10 @@ #include #endif +#if defined (WINCE) +#include +#endif + void evdns_shutdown(int); /********* PROTOTYPES **********/ @@ -2104,6 +2108,31 @@ do_hash_password(void) printf("16:%s\n",output); } +#if defined (WINCE) +int +find_flashcard_path(PWCHAR path, size_t size) +{ + WIN32_FIND_DATA d = {0}; + HANDLE h = NULL; + + if (!path) + return -1; + + h = FindFirstFlashCard(&d); + if (h == INVALID_HANDLE_VALUE) + return -1; + + if (wcslen(d.cFileName) == 0) { + FindClose(h); + return -1; + } + + wcsncpy(path,d.cFileName,size); + FindClose(h); + return 0; +} +#endif + /** Main entry point for the Tor process. Called from main(). */ /* This function is distinct from main() only so we can link main.c into * the unittest binary without conflicting with the unittests' main. */ @@ -2111,6 +2140,33 @@ int tor_main(int argc, char *argv[]) { int result = 0; +#if defined (WINCE) + WCHAR path [MAX_PATH] = {0}; + WCHAR fullpath [MAX_PATH] = {0}; + PWCHAR p = NULL; + FILE* redir = NULL; + FILE* redirdbg = NULL; + + // this is to facilitate debugging by opening + // a file on a folder shared by the wm emulator. + // if no flashcard (real or emulated) is present, + // log files will be written in the root folder + if (find_flashcard_path(path,MAX_PATH) == -1) + { + redir = _wfreopen( L"\\stdout.log", L"w", stdout ); + redirdbg = _wfreopen( L"\\stderr.log", L"w", stderr ); + } else { + swprintf(fullpath,L"\\%s\\tor",path); + CreateDirectory(fullpath,NULL); + + swprintf(fullpath,L"\\%s\\tor\\stdout.log",path); + redir = _wfreopen( fullpath, L"w", stdout ); + + swprintf(fullpath,L"\\%s\\tor\\stderr.log",path); + redirdbg = _wfreopen( fullpath, L"w", stderr ); + } +#endif + update_approx_time(time(NULL)); tor_threads_init(); init_logging(); diff --git a/src/or/or.h b/src/or/or.h index 9c613d28d1..ec8dd3a5dc 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -4377,8 +4377,10 @@ void networkstatus_free_all(void); /********************************* ntmain.c ***************************/ #ifdef MS_WINDOWS +#if !defined (WINCE) #define NT_SERVICE #endif +#endif #ifdef NT_SERVICE int nt_service_parse_options(int argc, char **argv, int *should_exit); From 076063ca90cc784363127e24a83e38bd54b0e003 Mon Sep 17 00:00:00 2001 From: valerino Date: Sat, 22 May 2010 03:02:32 +0200 Subject: [PATCH 4/5] moved wince related includes and defs to compat.h where possible, removed unused/redundant wince includes --- src/common/compat.c | 2 -- src/common/compat.h | 16 ++++++++++++++++ src/common/tortls.c | 4 ++++ src/or/main.c | 4 ---- src/win32/orconfig.h | 9 ++++++++- 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/common/compat.c b/src/common/compat.c index f93a1619ce..066623b832 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -22,10 +22,8 @@ #ifdef MS_WINDOWS #include #include -#if !defined (WINCE) #include #endif -#endif #ifdef HAVE_UNAME #include diff --git a/src/common/compat.h b/src/common/compat.h index 077256e401..7d59501e2b 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -51,6 +51,22 @@ #include #endif +#if defined (WINCE) +#include +#include +#include +#include +#define snprintf _snprintf +/* this is not exported as W .... */ +#define SHGetPathFromIDListW SHGetPathFromIDList +/* wcecompat has vasprintf */ +#define HAVE_VASPRINTF +/* no service here */ +#ifdef NT_SERVICE +#undef NT_SERVICE +#endif +#endif // WINCE + #ifndef NULL_REP_IS_ZERO_BYTES #error "It seems your platform does not represent NULL as zero. We can't cope." #endif diff --git a/src/common/tortls.c b/src/common/tortls.c index 218f110d8c..6732d55b8a 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -16,6 +16,10 @@ #include "orconfig.h" +#if defined (WINCE) +#include +#endif + #include #include #include diff --git a/src/or/main.c b/src/or/main.c index 542383526c..0c3e6d5425 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -24,10 +24,6 @@ #include #endif -#if defined (WINCE) -#include -#endif - void evdns_shutdown(int); /********* PROTOTYPES **********/ diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h index 4915597c01..0a9ecfd224 100644 --- a/src/win32/orconfig.h +++ b/src/win32/orconfig.h @@ -88,11 +88,18 @@ #define HAVE_STRING_H /* Define to 1 if you have the `strlcat' function. */ +#if defined (WINCE) +#define HAVE_STRLCAT +#else #undef HAVE_STRLCAT +#endif /* Define to 1 if you have the `strlcpy' function. */ +#if defined (WINCE) +#define HAVE_STRLCPY +#else #undef HAVE_STRLCPY - +#endif /* Define to 1 if you have the `strptime' function. */ #undef HAVE_STRPTIME From 7dcf88e69bdb3786ebac90f2609b9e190b279a4f Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 20 May 2010 22:59:48 -0400 Subject: [PATCH 5/5] Add 'changes' file for Windows CE port --- changes/port_to_wince | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changes/port_to_wince diff --git a/changes/port_to_wince b/changes/port_to_wince new file mode 100644 index 0000000000..fb6dc60108 --- /dev/null +++ b/changes/port_to_wince @@ -0,0 +1,4 @@ + o Major features: + - Tor has now been ported to build and run correctly on Windows CE + systems, using the wcecompat library. (Valerio Lupi) +