1 /*****************************************************************************\
3 * Name : windoze_helper *
4 * Author : Chris Koeritz *
6 *******************************************************************************
7 * Copyright (c) 1994-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
15 #include "windoze_helper.h"
17 ///#include "build_configuration.h"
18 ///#include "convert_utf.h"
19 ///#include "function.h"
21 ///#include "portable.h"
23 ///#include "version_record.h"
24 #include <configuration/application_configuration.h>
27 ///#include <stdlib.h>
35 #include <sys/times.h>
36 #include <sys/utsname.h>
42 //hmmm: need code for the wait cursor stuff.
52 using namespace basis;
53 using namespace structures;
54 using namespace configuration;
56 #undef static_class_name
57 #define static_class_name() "windoze_helper"
60 //#define DEBUG_PORTABLE
61 // uncomment for noisy debugging.
63 //#define DEBUG_UPTIME
64 // uncomment to get noisier reporting about system and rolling uptime.
66 //#define JUMP_TIME_49_DAYS
67 // uncomment to make our uptimes start just before the 32 bit uptime rollover.
68 //#define JUMP_TIME_497_DAYS
69 // uncomment to make our uptimes start just before the jiffies rollover.
71 //#define ENABLE_ROLLOVER_BUG
72 // uncomment to get old behavior back where the uptime was not rolling
73 // over properly. this turns off our remainder calculation and leaves the
74 // conversion as a simple cast, which will fail and get stuck at 2^32-1.
76 #define SUPPORT_SHELL_EXECUTE
77 // if this is not commented out, then the ShellExecute version of launch_
78 // -process() is available. when commented out, ShellExecute is turned off.
79 // disabling this support is the most common because the ShellExecute method
80 // in win32 was only supported for wk203 and wxp, that is only after
81 // windows2000 was already available. since nt and w2k don't support this,
82 // we just usually don't mess with it. it didn't answer a single one of our
83 // issues on windows vista (wfista) anyway, so it's not helping.
85 // ensure we always have debugging turned on if the jump is enabled.
86 #ifdef JUMP_TIME_49_DAYS
90 #ifdef JUMP_TIME_497_DAYS
94 // the JUMP..DAYS macros are mutually exclusive. use none or one, not both.
95 #ifdef JUMP_TIME_497_DAYS
96 #ifdef JUMP_TIME_49_DAYS
97 #error One cannot use both 497 day and 49 day bug inducers
100 #ifdef JUMP_TIME_49_DAYS
101 #ifdef JUMP_TIME_497_DAYS
102 #error One cannot use both 49 day and 497 day bug inducers
108 namespace application {
111 mutex BASIS_EXTERN &__uptime_synchronizer();
112 // used by our low-level uptime methods to protect singleton variables.
113 mutex BASIS_EXTERN &__process_synchronizer();
114 // used for synchronizing our records of child processes.
116 int_set BASIS_EXTERN &__our_kids();
117 // the static list of processes we've started.
120 const int MAXIMUM_COMMAND_LINE = 32 * KILOBYTE;
121 // maximum command line that we'll deal with here.
124 const char *UPTIME_REPORT_FILE = "/tmp/uptime_report.log";
127 const char *UPTIME_REPORT_FILE = "c:/uptime_report.log";
131 #define LOG(s) STAMPED_EMERGENCY_LOG(program_wide_logger(), s);
133 #define COMPLAIN(to_print) { \
134 guards::write_to_console((isprintf("basis/portable::%s: ", func) + to_print).s()); \
137 static const double __rollover_point = 2.0 * double(MAXINT);
138 // this number is our rollover point for 32 bit integers.
140 double rolling_uptime()
142 auto_synchronizer l(__uptime_synchronizer());
143 // protect our rollover records.
145 static u_int __last_ticks = 0;
146 static int __rollovers = 0;
148 u_int ticks_up = system_uptime();
149 // acquire the current uptime as a 32 bit unsigned int.
151 if (ticks_up < __last_ticks) {
152 // rollover happened. increment our tracker.
155 __last_ticks = ticks_up;
157 double to_return = double(__rollovers) * __rollover_point + double(ticks_up);
161 static FILE *__outfile = fopen(UPTIME_REPORT_FILE, "a+b");
162 static double old_value = 0;
163 if (absolute_value(old_value - to_return) > 9.9999) {
164 // only report when the time changes by more than 10 ms.
165 fprintf(__outfile, "-> uptime=%.0f\n", to_return);
167 old_value = to_return;
168 if (__rollover_point - to_return <= 40.00001) {
169 fprintf(__outfile, "---> MAXIMUM UPTIME SOON!\n");
179 u_int system_uptime()
182 return timeGetTime();
184 auto_synchronizer l(__uptime_synchronizer());
186 static clock_t __ctps = sysconf(_SC_CLK_TCK); // clock ticks per second.
187 static const double __multiplier = 1000.0 / double(__ctps);
188 // the multiplier gives us our full range for the tick counter.
191 static FILE *__outfile = fopen(UPTIME_REPORT_FILE, "wb");
192 if (__multiplier - u_int(__multiplier) > 0.000001) {
193 fprintf(__outfile, "uptime multiplier is "
194 "non-integral (%f)!\n", __multiplier);
199 // read uptime info from the OS.
201 u_int real_ticks = times(&uptime);
203 #ifdef JUMP_TIME_497_DAYS
204 static u_int old_value_497 = 0;
205 bool report_497 = (absolute_value(real_ticks - old_value_497) > 99);
207 old_value_497 = real_ticks; // update before changing it.
208 fprintf(__outfile, "pre kludge497 tix=%u\n", real_ticks);
211 real_ticks += u_int(49.0 * double(DAY_ms) + 17.0 * double(HOUR_ms));
213 fprintf(__outfile, "post kludge497 tix=%u\n", real_ticks);
218 // now turn this into the number of milliseconds.
219 double ticks_up = (double)real_ticks;
220 ticks_up = ticks_up * __multiplier; // convert to time here.
222 #ifdef JUMP_TIME_497_DAYS
223 #ifndef ENABLE_ROLLOVER_BUG
224 // add in additional time so we don't have to wait forever. we make the
225 // big number above rollover, but that's still got 27.5 or so minutes before
226 // we really rollover. so we add that part of the fraction (lost in the
227 // haze of the multiplier) in here. we don't add this in unless they are
228 // not exercising the rollover bug, because we already know that the 497
229 // day bug will show without the addition. but when we're already fixing
230 // the uptime, we jump time a bit forward so we only have to wait a couple
231 // minutes instead of 27.
232 ticks_up += 25.0 * MINUTE_ms;
236 #ifdef JUMP_TIME_49_DAYS
237 static u_int old_value_49 = 0;
238 bool report_49 = (absolute_value(u_int(ticks_up) - old_value_49) > 999);
240 old_value_49 = u_int(ticks_up); // update before changing it.
241 fprintf(__outfile, "pre kludge49 up=%f\n", ticks_up);
244 ticks_up += 49.0 * double(DAY_ms) + 17.0 * double(HOUR_ms);
246 fprintf(__outfile, "post kludge49 up=%f\n", ticks_up);
251 #ifndef ENABLE_ROLLOVER_BUG
252 // fix the return value if is has actually gone over the 2^32 limit for uint.
253 // casting a double larger than 2*MAXINT to a u_int on some platforms does
254 // not calculate a rolled-over value, but instead leaves the int at 2*MAXINT.
255 // thus we make sure it will be correct, as long as there are no more than
256 // 2^32-1 rollovers, which would be about 584,542 millenia. it's unlikely
257 // earth will last that long, so this calculation seems safe.
258 u_int divided = u_int(ticks_up / __rollover_point);
259 double to_return = ticks_up - (double(divided) * __rollover_point);
261 // we use the previous version of this calculation, which expected a u_int
262 // to double conversion to provide a modulo operation rather than just leaving
263 // the u_int at its maximum value (2^32-1). however, that expectation is not
264 // guaranteed on some platforms (e.g., ARM processor with floating point
265 // emulation) and thus it becomes a bug around 49 days and 17 hours into
266 // OS uptime because the value gets stuck at 2^32-1 and never rolls over.
267 double to_return = ticks_up;
271 static u_int old_value = 0;
272 int to_print = int(u_int(to_return));
273 if (absolute_value(int(old_value) - to_print) > 9) {
274 // only report when the time changes by more than 10 ms.
275 fprintf(__outfile, "-> uptime=%u\n", to_print);
277 old_value = u_int(to_print);
278 if (__rollover_point - to_return <= 40.00001) {
279 fprintf(__outfile, "---> MAXIMUM UPTIME SOON!\n");
285 return u_int(to_return);
289 void sleep_ms(u_int msec)
299 istring null_device()
309 bool event_poll(MSG &message)
315 if (!PeekMessage(&message, NIL, 0, 0, PM_REMOVE))
317 TranslateMessage(&message);
318 DispatchMessage(&message);
323 // makes a complaint about a failure.
324 #ifndef EMBEDDED_BUILD
325 #define COMPLAIN_QUERY(to_print) { \
326 unlink(tmpfile.s()); \
327 COMPLAIN(to_print); \
330 #define COMPLAIN_QUERY(to_print) { \
331 COMPLAIN(to_print); \
336 istring get_cmdline_from_proc()
338 FUNCDEF("get_cmdline_from_proc");
339 isprintf cmds_filename("/proc/%d/cmdline", portable::process_id());
340 FILE *cmds_file = fopen(cmds_filename.s(), "r");
342 COMPLAIN("failed to open our process's command line file.\n");
346 char *buff = new char[size + 1];
347 ssize_t chars_read = getline(&buff, &size, cmds_file);
348 // read the first line, giving ample space for how long it might be.
349 fclose(cmds_file); // drop the file again.
350 if (!chars_read || negative(chars_read)) {
351 COMPLAIN("failed to get any characters from our process's cmdline file.\n");
354 istring buffer = buff;
356 // clean out quote characters from the name.
357 for (int i = buffer.length() - 1; i >= 0; i--) {
358 if (buffer[i] == '"') buffer.zap(i, i);
363 // deprecated; better to use the /proc/pid/cmdline file.
364 istring query_for_process_info()
366 FUNCDEF("query_for_process_info");
367 istring to_return = "unknown";
368 // we ask the operating system about our process identifier and store
369 // the results in a temporary file.
371 isprintf tmpfile("/tmp/proc_name_check_%d_%d.txt", portable::process_id(),
372 rando.inclusive(0, 128000));
373 isprintf cmd("ps h --format \"%%a\" %d >%s", portable::process_id(),
375 // run the command to locate our process info.
376 int sysret = system(cmd.s());
377 if (negative(sysret)) {
378 COMPLAIN_QUERY("failed to run ps command to get process info");
381 // open the output file for reading.
382 FILE *output = fopen(tmpfile.s(), "r");
384 COMPLAIN_QUERY("failed to open the ps output file");
387 // read the file's contents into a string buffer.
388 char buff[MAXIMUM_COMMAND_LINE];
389 size_t size_read = fread(buff, 1, MAXIMUM_COMMAND_LINE, output);
391 // success at finding some text in the file at least.
392 while (size_read > 0) {
393 const char to_check = buff[size_read - 1];
394 if ( !to_check || (to_check == '\r') || (to_check == '\n')
395 || (to_check == '\t') )
399 to_return.reset(istring::UNTERMINATED, buff, size_read);
401 // couldn't read anything.
402 COMPLAIN_QUERY("could not read output of process list");
409 // used as a return value when the name cannot be determined.
410 #ifndef EMBEDDED_BUILD
411 #define SET_BOGUS_NAME(error) { \
415 unlink(tmpfile.s()); \
417 istring home_dir = env_string("HOME"); \
418 to_return = home_dir + "/failed_to_determine.exe"; \
421 #define SET_BOGUS_NAME(error) { \
423 to_return = "unknown"; \
427 istring application_name()
429 FUNCDEF("application_name");
432 to_return = get_cmdline_from_proc();
433 #elif defined(__WIN32__)
434 flexichar low_buff[MAX_ABS_PATH + 1];
435 GetModuleFileName(NIL, low_buff, MAX_ABS_PATH - 1);
436 istring buff = from_unicode_temp(low_buff);
437 buff.to_lower(); // we lower-case the name since windows seems to UC it.
439 #elif defined(EMBEDDED_BUILD)
440 SET_BOGUS_NAME("embedded_exe");
442 #pragma error("hmmm: no means of finding app name is implemented.")
443 SET_BOGUS_NAME("not_implemented_for_this_OS");
448 istring module_name(const void *module_handle)
451 //hmmm: implement module name for linux if that makes sense.
452 if (module_handle) {}
453 return application_name();
454 #elif defined(__WIN32__)
455 flexichar low_buff[MAX_ABS_PATH + 1];
456 GetModuleFileName((HMODULE)module_handle, low_buff, MAX_ABS_PATH - 1);
457 istring buff = from_unicode_temp(low_buff);
461 #pragma message("module_name unknown for this operating system.")
462 return application_name();
468 #if defined(__UNIX__)
470 #elif defined(__WIN32__)
471 return GetLastError();
473 #pragma error("hmmm: no code for error number for this operating system")
478 istring system_error_text(u_int to_name)
480 #if defined(__UNIX__)
481 return strerror(to_name);
482 #elif defined(__WIN32__)
483 char error_text[1000];
484 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NIL, to_name,
485 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)error_text,
486 sizeof(error_text) - 1, NIL);
487 istring to_return = error_text;
488 // trim off the ridiculous carriage return they add.
489 while ( (to_return[to_return.end()] == '\r')
490 || (to_return[to_return.end()] == '\n') )
491 to_return.zap(to_return.end(), to_return.end());
494 #pragma error("hmmm: no code for error text for this operating system")
501 bool is_address_valid(const void *address, int size_expected, bool writable)
503 return address && !IsBadReadPtr(address, size_expected)
504 && !(writable && IsBadWritePtr((void *)address, size_expected));
509 version get_OS_version()
513 utsname kernel_parms;
514 uname(&kernel_parms);
515 to_return = version(kernel_parms.release);
516 #elif defined(__WIN32__)
518 info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
519 ::GetVersionEx(&info);
520 to_return = version(isprintf("%u.%u.%u.%u", u_short(info.dwMajorVersion),
521 u_short(info.dwMinorVersion), u_short(info.dwPlatformId),
522 u_short(info.dwBuildNumber)));
523 #elif defined(EMBEDDED_BUILD)
524 // no version support.
526 #pragma error("hmmm: need version info for this OS!")
531 // for non-win32 and non-unix OSes, this might not work.
532 #if defined(__UNIX__) || defined(__WIN32__)
533 u_int process_id() { return getpid(); }
535 #pragma error("hmmm: need process id implementation for this OS!")
536 u_int process_id() { return 0; }
539 istring current_directory()
543 char buff[MAX_ABS_PATH];
544 getcwd(buff, MAX_ABS_PATH - 1);
546 #elif defined(__WIN32__)
547 flexichar low_buff[MAX_ABS_PATH + 1];
548 GetCurrentDirectory(MAX_ABS_PATH, low_buff);
549 to_return = from_unicode_temp(low_buff);
551 #pragma error("hmmm: need support for current directory on this OS.")
557 istring env_string(const istring &variable_name)
560 char *value = getenv(variable_name.upper().observe());
561 // dos & os/2 require upper case for the name, so we just do it that way.
563 char *value = getenv(variable_name.observe());
564 // reasonable OSes support mixed-case environment variables.
568 to_return = istring(value);
572 bool set_environ(const istring &variable_name, const istring &value)
576 ret = putenv((variable_name + "=" + value).s());
578 ret = setenv(variable_name.s(), value.s(), true);
583 timeval fill_timeval_ms(int duration)
585 timeval time_out; // timeval has tv_sec=seconds, tv_usec=microseconds.
587 // duration is immediate for the check; just a quick poll.
589 time_out.tv_usec = 0;
590 #ifdef DEBUG_PORTABLE
591 // LOG("no duration specified");
594 // a non-zero duration means we need to compute secs and usecs.
595 time_out.tv_sec = duration / 1000;
596 // set the number of seconds from the input in milliseconds.
597 duration -= time_out.tv_sec * 1000;
598 // now take out the chunk we've already recorded as seconds.
599 time_out.tv_usec = duration * 1000;
600 // set the number of microseconds from the remaining milliseconds.
601 #ifdef DEBUG_PORTABLE
602 // LOG(isprintf("duration of %d ms went to %d sec and %d usec.", duration,
603 // time_out.tv_sec, time_out.tv_usec));
609 //hmmm: this doesn't seem to account for quoting properly at all?
610 basis::char_star_array break_line(istring &app, const istring ¶meters)
612 basis::char_star_array to_return;
615 // find the positions of the spaces and count them.
616 for (int j = 0; j < parameters.length(); j++) {
617 if (parameters[j] == ' ') {
622 // first, add the app name to the list of parms.
623 to_return += new char[app.length() + 1];
624 app.stuff(to_return[0], app.length());
626 // now add each space-separated parameter to the list.
627 for (int i = 0; i < num; i++) {
628 int len = posns[i] - last_posn;
629 to_return += new char[len + 1];
630 parameters.substring(last_posn, posns[i] - 1).stuff(to_return[i + 1], len);
631 last_posn = posns[i] + 1;
633 // catch anything left after last separator.
634 if (last_posn < parameters.length() - 1) {
635 int len = parameters.length() - last_posn;
636 to_return += new char[len + 1];
637 parameters.substring(last_posn, parameters.length() - 1)
638 .stuff(to_return[to_return.last()], len);
640 // add the sentinel to the list of strings.
642 #ifdef DEBUG_PORTABLE
643 for (int q = 0; to_return[q]; q++) {
644 printf("%d: %s\n", q, to_return[q]);
647 // now a special detour; fix the app name to remove quotes, which are
648 // not friendly to pass to exec.
649 if (app[0] == '"') app.zap(0, 0);
650 if (app[app.end()] == '"') app.zap(app.end(), app.end());
656 void exiting_child_signal_handler(int sig_num)
658 FUNCDEF("exiting_child_signal_handler");
659 if (sig_num != SIGCHLD) {
660 // uhhh, this seems wrong.
662 auto_synchronizer l(__process_synchronizer());
663 for (int i = 0; i < __our_kids().length(); i++) {
665 pid_t exited = waitpid(__our_kids()[i], &status, WNOHANG);
666 if ( (exited == -1) || (exited == __our_kids()[i]) ) {
667 // negative one denotes an error, which we are going to assume means the
668 // process has exited via some other method than our wait. if the value
669 // is the same as the process we waited for, that means it exited.
670 __our_kids().zap(i, i);
672 } else if (exited != 0) {
673 // zero would be okay; this result we do not understand.
674 #ifdef DEBUG_PORTABLE
675 printf("unknown result %d waiting for process %d", exited,
683 u_int launch_process(const istring &app_name_in, const istring &command_line,
684 int flag, u_int &child_id)
687 istring app_name = app_name_in;
688 if (app_name[0] != '"')
689 app_name.insert(0, "\"");
690 if (app_name[app_name.end()] != '"')
693 // unix / linux implementation.
694 if (flag & RETURN_IMMEDIATELY) {
695 // they want to get back right away.
696 pid_t kid_pid = fork();
697 #ifdef DEBUG_PORTABLE
698 printf("launch fork returned %d\n", kid_pid);
701 // this is the child; we now need to launch into what we were asked for.
702 #ifdef DEBUG_PORTABLE
703 printf((isprintf("process %d execing ", process_id()) + app_name
704 + " parms " + command_line + "\n").s());
706 basis::char_star_array parms = break_line(app_name, command_line);
707 execv(app_name.s(), parms.observe());
708 // oops. failed to exec if we got to here.
709 #ifdef DEBUG_PORTABLE
710 printf((isprintf("child of fork (pid %d) failed to exec, "
711 "error is ", process_id()) + system_error_text(system_error())
714 exit(0); // leave since this is a failed child process.
716 // this is the parent. let's see if the launch worked.
719 u_int to_return = system_error();
720 #ifdef DEBUG_PORTABLE
721 printf((isprintf("parent %d is returning after failing to create, "
722 "error is ", process_id()) + system_error_text(to_return)
727 // yes, launch worked okay.
730 auto_synchronizer l(__process_synchronizer());
731 __our_kids() += kid_pid;
733 // hook in our child exit signal handler.
734 signal(SIGCHLD, exiting_child_signal_handler);
736 #ifdef DEBUG_PORTABLE
737 printf((isprintf("parent %d is returning after successfully "
738 "creating %d ", process_id(), kid_pid) + app_name
739 + " parms " + command_line + "\n").s());
745 // assume they want to wait.
746 return system((app_name + " " + command_line).s());
748 #elif defined(__WIN32__)
750 //checking on whether we have admin rights for the launch.
751 //if (IsUserAnAdmin()) {
752 // MessageBox(0, (istring("IS admin with ") + app_name_in + " " + command_line).s(), "launch process", MB_OK);
754 // MessageBox(0, (istring("NOT admin for ") + app_name_in + " " + command_line).s(), "launch process", MB_OK);
757 PROCESS_INFORMATION process_info;
758 ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
760 #ifdef SUPPORT_SHELL_EXECUTE
761 if (flag & SHELL_EXECUTE) {
762 // new code for using shell execute method--required on vista for proper
763 // launching with the right security tokens.
765 if (flag & HIDE_APP_WINDOW) {
766 // magic that hides a console window for mswindows.
769 show_cmd = SW_SHOWNORMAL;
772 SHELLEXECUTEINFO exec_info;
773 ZeroMemory(&exec_info, sizeof(SHELLEXECUTEINFO));
774 exec_info.cbSize = sizeof(SHELLEXECUTEINFO);
775 exec_info.fMask = SEE_MASK_NOCLOSEPROCESS // get the process info.
776 | SEE_MASK_FLAG_NO_UI; // turn off any visible error dialogs.
777 exec_info.hwnd = GetDesktopWindow();
778 //hmmm: is get desktop window always appropriate?
779 to_unicode_persist(temp_verb, "open");
780 //does "runas" work on xp also? or anywhere?
781 exec_info.lpVerb = temp_verb;
782 to_unicode_persist(temp_file, app_name);
783 exec_info.lpFile = temp_file;
784 to_unicode_persist(temp_parms, command_line);
785 exec_info.lpParameters = temp_parms;
786 exec_info.nShow = show_cmd;
787 // exec_info.hProcess = &process_info;
789 BOOL worked = ShellExecuteEx(&exec_info);
791 return system_error();
792 // copy out the returned process handle.
793 process_info.hProcess = exec_info.hProcess;
794 process_info.dwProcessId = GetProcessId(exec_info.hProcess);
797 // standard windows implementation using CreateProcess.
798 STARTUPINFO startup_info;
799 ZeroMemory(&startup_info, sizeof(STARTUPINFO));
800 startup_info.cb = sizeof(STARTUPINFO);
802 if (flag & HIDE_APP_WINDOW) {
803 // magic that hides a console window for mswindows.
804 // version ver = portable::get_OS_version();
805 // version vista_version(6, 0);
806 // if (ver < vista_version) {
807 // // we suspect that this flag is hosing us in vista.
808 create_flag = CREATE_NO_WINDOW;
811 istring parms = app_name + " " + command_line;
812 bool success = CreateProcess(NIL, to_unicode_temp(parms), NIL, NIL, false,
813 create_flag, NIL, NIL, &startup_info, &process_info);
815 return system_error();
816 // success then, merge back into stream.
818 #ifdef SUPPORT_SHELL_EXECUTE
822 // common handling for CreateProcess and ShellExecuteEx.
823 child_id = process_info.dwProcessId;
825 if (flag & AWAIT_VIA_POLLING) {
826 // this type of waiting is done without blocking on the process.
830 // check if the process is gone yet.
831 BOOL ret = GetExitCodeProcess(process_info.hProcess, &retval);
835 // if they aren't saying it's still active, then we will leave.
836 if (retval != STILL_ACTIVE)
841 } else if (flag & AWAIT_APP_EXIT) {
842 // they want to wait for the process to exit.
843 WaitForInputIdle(process_info.hProcess, INFINITE);
844 WaitForSingleObject(process_info.hProcess, INFINITE);
845 GetExitCodeProcess(process_info.hProcess, &retval);
847 // drop the process and thread handles.
848 if (process_info.hProcess)
849 CloseHandle(process_info.hProcess);
850 if (process_info.hThread)
851 CloseHandle(process_info.hThread);
852 return (u_int)retval;
854 #pragma error("hmmm: launch_process: no implementation for this OS.")
859 ////////////////////////////////////////////////////////////////////////////
863 char *itoa(int to_convert, char *buffer, int radix)
865 // slow implementation; may need enhancement for serious speed.
866 // ALSO: only supports base 10 and base 16 currently.
867 const char *formatter = "%d";
868 if (radix == 16) formatter = "%x";
869 isprintf printed(formatter, to_convert);
870 printed.stuff(buffer, printed.length() + 1);
880 void show_wait_cursor() { SetCursor(LoadCursor(NULL, IDC_WAIT)); }
882 void show_normal_cursor() { SetCursor(LoadCursor(NULL, IDC_ARROW)); }
884 istring rc_string(UINT id, application_instance instance)
886 flexichar temp[MAX_ABS_PATH + 1];
887 int ret = LoadString(instance, id, temp, MAX_ABS_PATH);
888 if (!ret) return istring();
889 return istring(from_unicode_temp(temp));
892 istring rc_string(u_int id) { return rc_string(id, GET_INSTANCE_HANDLE()); }
895 const char *opsystem_name(known_operating_systems which)
898 case WIN_95: return "WIN_95";
899 case WIN_NT: return "WIN_NT";
900 case WIN_2K: return "WIN_2K";
901 case WIN_XP: return "WIN_XP";
902 case WIN_SRV2K3: return "WIN_SRV2K3";
903 case WIN_VISTA: return "WIN_VISTA";
904 case WIN_SRV2K8: return "WIN_SRV2K8";
905 default: return "UNKNOWN_OS";
909 known_operating_systems determine_OS()
911 version osver = application_configuration::get_OS_version();
912 if ( (osver.v_major() == 4) && (osver.v_minor() == 0) ) {
913 if (osver.v_revision() == VER_PLATFORM_WIN32_WINDOWS) return WIN_95;
914 if (osver.v_revision() == VER_PLATFORM_WIN32_NT) return WIN_NT;
915 } else if ( (osver.v_major() == 5) && (osver.v_minor() == 0) ) {
917 } else if ( (osver.v_major() == 5) && (osver.v_minor() == 1) ) {
919 } else if ( (osver.v_major() == 5) && (osver.v_minor() == 2) ) {
921 } else if ( (osver.v_major() == 6) && (osver.v_minor() == 0) ) {
923 } else if ( (osver.v_major() == 6) && (osver.v_minor() == 1) ) {
933 #undef static_class_name