/src/native/windows/run/run.c
C | 1643 lines | 1366 code | 185 blank | 92 comment | 251 complexity | 1ace895a4a990021c5b2b8cc158d11b7 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1/* 2 * Jitsi, the OpenSource Java VoIP and Instant Messaging client. 3 * 4 * Distributable under LGPL license. 5 * See terms of license at gnu.org. 6 */ 7 8#include "run.h" 9 10#include <ctype.h> /* isspace */ 11#include <jni.h> 12#include <psapi.h> /* GetModuleFileNameEx */ 13#include <stdint.h> 14#include <stdio.h> 15#include <stdlib.h> 16#include <string.h> 17#include <tchar.h> 18#include <tlhelp32.h> /* CreateToolhelp32Snapshot */ 19 20#include "registry.h" 21#include "../setup/nls.h" 22 23#define JAVA_MAIN_CLASS _T("net.java.sip.communicator.launcher.SIPCommunicator") 24 25/** 26 * The pipe through which the launcher is to communicate with the crash handler. 27 */ 28static HANDLE Run_channel = INVALID_HANDLE_VALUE; 29 30/** 31 * The command line that the application has received as the <tt>cmdLine</tt> 32 * function argument of its <tt>WinMain</tt> entry point and that is currently 33 * unparsed i.e. the parts which have already been parsed are no longer present. 34 */ 35static LPSTR Run_cmdLine = NULL; 36 37/** 38 * The indicator which determines whether the crash handler is to launch the 39 * application. 40 */ 41static BOOL Run_launch = TRUE; 42 43static DWORD Run_addPath(LPCTSTR path); 44static DWORD Run_callStaticVoidMain(JNIEnv *jniEnv, BOOL *searchForJava); 45static int Run_displayMessageBoxFromString(DWORD textId, DWORD_PTR *textArgs, LPCTSTR caption, UINT type); 46static DWORD Run_equalsParentProcessExecutableFilePath(LPCTSTR executableFilePath, BOOL *equals); 47static DWORD Run_getExecutableFilePath(LPTSTR *executableFilePath); 48static DWORD Run_getJavaExeCommandLine(LPCTSTR javaExe, LPTSTR *commandLine); 49static LPTSTR Run_getJavaLibraryPath(); 50static LONG Run_getJavaPathsFromRegKey(HKEY key, LPTSTR *runtimeLib, LPTSTR *javaHome); 51static DWORD Run_getJavaVMOptionStrings(size_t head, TCHAR separator, size_t tail, LPTSTR *optionStrings, jint *optionStringCount); 52static LPTSTR Run_getLockFilePath(); 53static DWORD Run_getParentProcessId(DWORD *ppid); 54static DWORD Run_handleLauncherExitCode(DWORD exitCode, LPCTSTR lockFilePath, LPCTSTR executableFilePath); 55static BOOL Run_isDirectory(LPCTSTR fileName); 56static BOOL Run_isFile(LPCTSTR fileName); 57static DWORD Run_openProcessAndResumeThread(DWORD processId, DWORD threadId, HANDLE *process); 58static DWORD Run_runAsCrashHandler(LPCTSTR executableFilePath, LPSTR cmdLine); 59static DWORD Run_runAsCrashHandlerWithPipe(LPCTSTR executableFilePath, LPSTR cmdLine, HANDLE *readPipe, HANDLE *writePipe); 60static DWORD Run_runAsLauncher(LPCTSTR executableFilePath, LPSTR cmdLine); 61static DWORD Run_runJava(LPCTSTR executableFilePath, LPSTR cmdLine); 62static DWORD Run_runJavaExe(LPCTSTR javaExe, BOOL searchPath, BOOL *searchForJava); 63static DWORD Run_runJavaFromEnvVar(LPCTSTR envVar, BOOL *searchForJava); 64static DWORD Run_runJavaFromJavaHome(LPCTSTR javaHome, BOOL searchForRuntimeLib, BOOL *searchForJava); 65static DWORD Run_runJavaFromRegKey(HKEY key, BOOL *searchForJava); 66static DWORD Run_runJavaFromRuntimeLib(LPCTSTR runtimeLib, LPCTSTR javaHome, BOOL *searchForJava); 67static LPSTR Run_skipWhitespace(LPSTR str); 68 69static DWORD 70Run_addPath(LPCTSTR path) 71{ 72 LPCTSTR envVarName = _T("PATH"); 73 TCHAR envVar[4096]; 74 DWORD envVarCapacity = sizeof(envVar) / sizeof(TCHAR); 75 DWORD envVarLength 76 = GetEnvironmentVariable(envVarName, envVar, envVarCapacity); 77 DWORD error; 78 79 if (envVarLength) 80 { 81 if (envVarLength >= envVarCapacity) 82 error = ERROR_NOT_ENOUGH_MEMORY; 83 else 84 { 85 DWORD pathLength = _tcslen(path); 86 87 if (envVarLength + 1 + pathLength + 1 > envVarCapacity) 88 error = ERROR_NOT_ENOUGH_MEMORY; 89 else 90 { 91 LPTSTR str = envVar + envVarLength; 92 93 *str = _T(';'); 94 str++; 95 _tcsncpy(str, path, pathLength); 96 str += pathLength; 97 *str = 0; 98 99 if (SetEnvironmentVariable(envVarName, envVar)) 100 error = ERROR_SUCCESS; 101 else 102 error = GetLastError(); 103 } 104 } 105 } 106 else 107 error = GetLastError(); 108 return error; 109} 110 111static DWORD 112Run_callStaticVoidMain(JNIEnv *jniEnv, BOOL *searchForJava) 113{ 114 LPTSTR mainClassName; 115 jclass mainClass; 116 DWORD error; 117 118 mainClassName = _tcsdup(JAVA_MAIN_CLASS); 119 if (mainClassName) 120 { 121 LPTSTR ch; 122 123 for (ch = mainClassName; *ch; ch++) 124 if (_T('.') == *ch) 125 *ch = _T('/'); 126 mainClass = (*jniEnv)->FindClass(jniEnv, mainClassName); 127 free(mainClassName); 128 } 129 else 130 mainClass = NULL; 131 if (mainClass) 132 { 133 jmethodID mainMethodID 134 = (*jniEnv)->GetStaticMethodID( 135 jniEnv, 136 mainClass, 137 "main", 138 "([Ljava/lang/String;)V"); 139 140 if (mainMethodID) 141 { 142 jclass stringClass 143 = (*jniEnv)->FindClass(jniEnv, "java/lang/String"); 144 145 if (stringClass) 146 { 147 int argc = 0; 148 LPWSTR *argv = NULL; 149 150 if (Run_cmdLine && strlen(Run_cmdLine)) 151 { 152 LPWSTR cmdLineW = NLS_str2wstr(Run_cmdLine); 153 154 if (cmdLineW) 155 { 156 argv = CommandLineToArgvW(cmdLineW, &argc); 157 free(cmdLineW); 158 error = argv ? ERROR_SUCCESS : GetLastError(); 159 } 160 else 161 error = ERROR_NOT_ENOUGH_MEMORY; 162 } 163 else 164 error = ERROR_SUCCESS; 165 if (ERROR_SUCCESS == error) 166 { 167 jobjectArray mainArgs 168 = (*jniEnv)->NewObjectArray( 169 jniEnv, 170 argc, stringClass, NULL); 171 172 if (mainArgs) 173 { 174 int i; 175 176 for (i = 0; (ERROR_SUCCESS == error) && (i < argc); i++) 177 { 178 LPWSTR arg = *(argv + i); 179 jstring mainArg 180 = (*jniEnv)->NewString( 181 jniEnv, 182 arg, wcslen(arg)); 183 184 if (mainArg) 185 { 186 (*jniEnv)->SetObjectArrayElement( 187 jniEnv, 188 mainArgs, i, mainArg); 189 if (JNI_TRUE 190 == (*jniEnv)->ExceptionCheck(jniEnv)) 191 error = ERROR_FUNCTION_FAILED; 192 } 193 else 194 error = ERROR_NOT_ENOUGH_MEMORY; 195 } 196 if (argv) 197 { 198 LocalFree(argv); 199 argv = NULL; 200 } 201 202 if (ERROR_SUCCESS == error) 203 { 204 *searchForJava = FALSE; 205 206 /* 207 * The parent process will have to wait for and get 208 * the exit code of its child, not java.exe so it 209 * does not need telling who to wait for or get the 210 * exit code of. 211 */ 212 if (INVALID_HANDLE_VALUE != Run_channel) 213 { 214 CloseHandle(Run_channel); 215 Run_channel = INVALID_HANDLE_VALUE; 216 } 217 218 (*jniEnv)->CallStaticVoidMethod( 219 jniEnv, 220 mainClass, mainMethodID, mainArgs); 221 } 222 } 223 else 224 error = ERROR_NOT_ENOUGH_MEMORY; 225 226 if (argv) 227 LocalFree(argv); 228 } 229 } 230 else 231 error = ERROR_CLASS_DOES_NOT_EXIST; 232 } 233 else 234 error = ERROR_INVALID_FUNCTION; 235 } 236 else 237 error = ERROR_CLASS_DOES_NOT_EXIST; 238 239 return error; 240} 241 242static int 243Run_displayMessageBoxFromString( 244 DWORD textId, DWORD_PTR *textArgs, 245 LPCTSTR caption, 246 UINT type) 247{ 248 TCHAR format[1024]; 249 int formatLength 250 = LoadString( 251 GetModuleHandle(NULL), 252 textId, 253 format, sizeof(format) / sizeof(TCHAR)); 254 int answer = 0; 255 256 if (formatLength > 0) 257 { 258 LPTSTR message = NULL; 259 DWORD messageLength 260 = FormatMessage( 261 FORMAT_MESSAGE_ALLOCATE_BUFFER 262 | FORMAT_MESSAGE_ARGUMENT_ARRAY 263 | FORMAT_MESSAGE_FROM_STRING, 264 format, 265 0, 266 LANG_USER_DEFAULT, 267 (LPTSTR) &message, 268 0, 269 (va_list *) textArgs); 270 271 if (messageLength) 272 { 273 answer 274 = MessageBox( 275 NULL, 276 message, 277 caption, 278 type); 279 LocalFree(message); 280 } 281 } 282 return answer; 283} 284 285static DWORD 286Run_equalsParentProcessExecutableFilePath( 287 LPCTSTR executableFilePath, 288 BOOL *equals) 289{ 290 DWORD ppid = 0; 291 DWORD error = Run_getParentProcessId(&ppid); 292 293 if (ERROR_SUCCESS == error) 294 { 295 HANDLE parentProcess 296 = OpenProcess( 297 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 298 FALSE, 299 ppid); 300 301 if (parentProcess) 302 { 303 TCHAR parentProcessExecutableFilePath[MAX_PATH + 1]; 304 DWORD parentProcessExecutableFilePathLength 305 = GetModuleFileNameEx( 306 parentProcess, 307 NULL, 308 parentProcessExecutableFilePath, 309 sizeof(parentProcessExecutableFilePath)); 310 311 if (parentProcessExecutableFilePathLength) 312 { 313 *equals 314 = (_tcsnicmp( 315 parentProcessExecutableFilePath, 316 executableFilePath, 317 parentProcessExecutableFilePathLength) 318 == 0); 319 } 320 else 321 error = GetLastError(); 322 323 CloseHandle(parentProcess); 324 } 325 else 326 error = GetLastError(); 327 } 328 return error; 329} 330 331static DWORD 332Run_getExecutableFilePath(LPTSTR *executableFilePath) 333{ 334 TCHAR str[MAX_PATH + 1]; 335 DWORD capacity = sizeof(str); 336 DWORD length = GetModuleFileName(NULL, str, capacity); 337 DWORD error; 338 339 if (length) 340 { 341 /* Make sure str is null terminated on Windows XP/2000. */ 342 if (length == capacity) 343 { 344 length--; 345 str[length] = 0; 346 } 347 348 *executableFilePath = (LPTSTR) malloc(sizeof(TCHAR) * (length + 1)); 349 if (*executableFilePath) 350 { 351 _tcsncpy(*executableFilePath, str, length); 352 *((*executableFilePath) + length) = 0; 353 error = ERROR_SUCCESS; 354 } 355 else 356 error = ERROR_OUTOFMEMORY; 357 } 358 else 359 error = GetLastError(); 360 return error; 361} 362 363static DWORD 364Run_getJavaExeCommandLine(LPCTSTR javaExe, LPTSTR *commandLine) 365{ 366 LPCTSTR mainClass = JAVA_MAIN_CLASS; 367 368 size_t javaExeLength; 369 size_t mainClassLength; 370 size_t cmdLineLength; 371 DWORD error; 372 373 javaExeLength = _tcslen(javaExe); 374 mainClassLength = _tcslen(mainClass); 375 if (Run_cmdLine) 376 { 377 cmdLineLength = _tcslen(Run_cmdLine); 378 if (cmdLineLength) 379 cmdLineLength++; /* ' ' */ 380 } 381 else 382 cmdLineLength = 0; 383 384 error 385 = Run_getJavaVMOptionStrings( 386 javaExeLength + 1 /* ' ' */, 387 _T(' '), 388 mainClassLength + cmdLineLength, 389 commandLine, 390 NULL); 391 if (ERROR_SUCCESS == error) 392 { 393 LPTSTR str = *commandLine; 394 395 _tcsncpy(str, javaExe, javaExeLength); 396 str += javaExeLength; 397 *str = _T(' '); 398 str++; 399 400 str += _tcslen(str); 401 402 _tcsncpy(str, mainClass, mainClassLength); 403 str += mainClassLength; 404 if (cmdLineLength) 405 { 406 *str = _T(' '); 407 str++; 408 cmdLineLength--; 409 _tcsncpy(str, Run_cmdLine, cmdLineLength); 410 str += cmdLineLength; 411 } 412 *str = 0; 413 } 414 415 return error; 416} 417 418static LPTSTR 419Run_getJavaLibraryPath() 420{ 421 LPCTSTR relativeJavaLibraryPath = _T("native"); 422 TCHAR javaLibraryPath[MAX_PATH + 1]; 423 DWORD javaLibraryPathCapacity 424 = sizeof(javaLibraryPath) / sizeof(TCHAR); 425 DWORD javaLibraryPathLength 426 = GetFullPathName( 427 relativeJavaLibraryPath, 428 javaLibraryPathCapacity, javaLibraryPath, 429 NULL); 430 LPCTSTR dup; 431 432 if (javaLibraryPathLength 433 && (javaLibraryPathLength < javaLibraryPathCapacity)) 434 { 435 LPTSTR str = javaLibraryPath; 436 437 str += javaLibraryPathLength; 438 *str = 0; 439 440 dup = javaLibraryPath; 441 } 442 else 443 dup = relativeJavaLibraryPath; 444 return _tcsdup(dup); 445} 446 447static LONG 448Run_getJavaPathsFromRegKey(HKEY key, LPTSTR *runtimeLib, LPTSTR *javaHome) 449{ 450 HKEY jreKey = 0; 451 LONG error 452 = RegOpenKeyEx( 453 key, 454 _T("Software\\JavaSoft\\Java Runtime Environment"), 455 0, 456 KEY_QUERY_VALUE, 457 &jreKey); 458 459 if (ERROR_SUCCESS == error) 460 { 461 LPTSTR currentVersion = NULL; 462 463 error 464 = Run_getRegSzValue(jreKey, _T("CurrentVersion"), ¤tVersion); 465 if (ERROR_SUCCESS == error) 466 { 467 HKEY currentVersionKey = 0; 468 469 error 470 = RegOpenKeyEx( 471 jreKey, 472 currentVersion, 473 0, 474 KEY_QUERY_VALUE, 475 ¤tVersionKey); 476 free(currentVersion); 477 if (ERROR_SUCCESS == error) 478 { 479 if (ERROR_SUCCESS 480 != Run_getRegSzValue( 481 currentVersionKey, 482 _T("RuntimeLib"), 483 runtimeLib)) 484 *runtimeLib = NULL; 485 if (ERROR_SUCCESS 486 != Run_getRegSzValue( 487 currentVersionKey, 488 _T("JavaHome"), 489 javaHome)) 490 *javaHome = NULL; 491 RegCloseKey(currentVersionKey); 492 } 493 } 494 RegCloseKey(jreKey); 495 } 496 return error; 497} 498 499static DWORD 500Run_getJavaVMOptionStrings 501 (size_t head, TCHAR separator, size_t tail, 502 LPTSTR *optionStrings, jint *optionStringCount) 503{ 504 LPTSTR javaLibraryPath = Run_getJavaLibraryPath(); 505 jint _optionStringCount = 0; 506 DWORD error; 507 508 if (javaLibraryPath) 509 { 510 LPCTSTR classpath[] 511 = { 512 _T("lib\\felix.jar"), 513 _T("lib\\jdic-all.jar"), 514 _T("lib\\jdic_stub.jar"), 515 _T("lib\\bcprovider.jar"), 516 _T("sc-bundles\\sc-launcher.jar"), 517 _T("sc-bundles\\util.jar"), 518 NULL 519 }; 520 LPCTSTR properties[] 521 = { 522 _T("felix.config.properties"), 523 _T("file:./lib/felix.client.run.properties"), 524 _T("java.util.logging.config.file"), 525 _T("lib/logging.properties"), 526 _T("java.library.path"), 527 javaLibraryPath, 528 _T("jna.library.path"), 529 javaLibraryPath, 530 _T("net.java.sip.communicator.SC_HOME_DIR_NAME"), 531 PRODUCTNAME, 532 NULL 533 }; 534 535 size_t classpathLength; 536 size_t propertiesLength; 537 BOOL quote = separator; 538 539 { 540 LPCTSTR cp; 541 size_t i = 0; 542 543 classpathLength = 0; 544 while ((cp = classpath[i++])) 545 classpathLength += (_tcslen(cp) + 1 /* ';' */); 546 if (classpathLength) 547 classpathLength += 18 /* "-Djava.class.path=" */; 548 } 549 { 550 LPCTSTR property; 551 size_t i = 0; 552 553 propertiesLength = 0; 554 while ((property = properties[i++])) 555 { 556 propertiesLength 557 += (2 /* "\"-D" */ 558 + _tcslen(property) 559 + 1 /* '=' */ 560 + _tcslen(properties[i++]) 561 + 1 /* ' ' */); 562 if (quote) 563 propertiesLength += 2; 564 } 565 } 566 567 *optionStrings 568 = (LPTSTR) 569 malloc( 570 sizeof(TCHAR) 571 * (head 572 + classpathLength 573 + propertiesLength 574 + 1 /* 0 */ 575 + tail)); 576 if (*optionStrings) 577 { 578 LPTSTR str = (*optionStrings) + head; 579 580 if (classpathLength) 581 { 582 LPCTSTR cp; 583 size_t i = 0; 584 585 _tcscpy(str, _T("-Djava.class.path=")); 586 str += 18; 587 while ((cp = classpath[i++])) 588 { 589 size_t length = _tcslen(cp); 590 591 _tcsncpy(str, cp, length); 592 str += length; 593 *str = _T(';'); 594 str++; 595 } 596 str--; /* Drop the last ';'. */ 597 *str = separator; 598 str++; 599 600 _optionStringCount++; 601 } 602 if (propertiesLength) 603 { 604 LPCTSTR property; 605 size_t i = 0; 606 607 while ((property = properties[i++])) 608 { 609 size_t length; 610 LPCTSTR value; 611 612 if (quote) 613 *str++ = _T('"'); 614 _tcscpy(str, _T("-D")); 615 str += 2; 616 length = _tcslen(property); 617 _tcsncpy(str, property, length); 618 str += length; 619 *str++ = _T('='); 620 621 value = properties[i++]; 622 length = _tcslen(value); 623 _tcsncpy(str, value, length); 624 str += length; 625 if (quote) 626 *str++ = _T('"'); 627 *str++ = separator; 628 629 _optionStringCount++; 630 } 631 } 632 *str = 0; 633 634 if (optionStringCount) 635 *optionStringCount = _optionStringCount; 636 error = ERROR_SUCCESS; 637 } 638 else 639 error = ERROR_OUTOFMEMORY; 640 641 free(javaLibraryPath); 642 } 643 else 644 error = ERROR_OUTOFMEMORY; 645 646 return error; 647} 648 649static LPTSTR 650Run_getLockFilePath() 651{ 652 TCHAR appData[MAX_PATH + 1]; 653 DWORD appDataCapacity = sizeof(appData) / sizeof(TCHAR); 654 DWORD appDataLength 655 = GetEnvironmentVariable( 656 _T("APPDATA"), 657 appData, appDataCapacity); 658 LPTSTR lockFilePath = NULL; 659 660 if (appDataLength && (appDataLength < appDataCapacity)) 661 { 662 LPCTSTR productName = PRODUCTNAME; 663 size_t productNameLength = _tcslen(productName); 664 LPCTSTR lockFileName = _T(".lock"); 665 size_t lockFileNameLength = _tcslen(lockFileName); 666 667 lockFilePath 668 = (LPTSTR) 669 malloc( 670 sizeof(TCHAR) 671 * (appDataLength 672 + 1 673 + productNameLength 674 + 1 675 + lockFileNameLength 676 + 1)); 677 if (lockFilePath) 678 { 679 LPTSTR str = lockFilePath; 680 681 _tcsncpy(str, appData, appDataLength); 682 str += appDataLength; 683 *str = _T('\\'); 684 str++; 685 _tcsncpy(str, productName, productNameLength); 686 str += productNameLength; 687 *str = _T('\\'); 688 str++; 689 _tcsncpy(str, lockFileName, lockFileNameLength); 690 str += lockFileNameLength; 691 *str = 0; 692 } 693 } 694 return lockFilePath; 695} 696 697static DWORD 698Run_getParentProcessId(DWORD *ppid) 699{ 700 HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 701 DWORD error; 702 703 if (INVALID_HANDLE_VALUE == snapshot) 704 error = GetLastError(); 705 else 706 { 707 PROCESSENTRY32 pe32; 708 709 pe32.dwSize = sizeof(PROCESSENTRY32); 710 if (Process32First(snapshot, &pe32)) 711 { 712 DWORD pid = GetCurrentProcessId(); 713 714 error = ERROR_FILE_NOT_FOUND; 715 do 716 { 717 if (pe32.th32ProcessID == pid) 718 { 719 error = ERROR_SUCCESS; 720 *ppid = pe32.th32ParentProcessID; 721 break; 722 } 723 724 if (!Process32Next(snapshot, &pe32)) 725 { 726 error = GetLastError(); 727 break; 728 } 729 } 730 while (1); 731 } 732 else 733 error = GetLastError(); 734 735 CloseHandle(snapshot); 736 } 737 return error; 738} 739 740static DWORD 741Run_handleLauncherExitCode( 742 DWORD exitCode, LPCTSTR lockFilePath, 743 LPCTSTR executableFilePath) 744{ 745 DWORD error = ERROR_SUCCESS; 746 747 if (Run_isFile(lockFilePath)) 748 { 749 DWORD_PTR arguments[] = { (DWORD_PTR) PRODUCTNAME }; 750 int answer 751 = Run_displayMessageBoxFromString( 752 IDS_CRASHANDRELAUNCH, arguments, 753 executableFilePath, 754 MB_ICONEXCLAMATION | MB_YESNO); 755 756 if (answer) 757 { 758 if (IDYES == answer) 759 Run_launch = TRUE; 760 761 /* 762 * We believe the lockFilePath is related to the reported crash 763 * instance so we have to remove it after notifying the user in 764 * order to not take it into account upon a possible next launch. 765 */ 766 DeleteFile(lockFilePath); 767 } 768 else 769 error = GetLastError(); 770 } 771 return error; 772} 773 774static BOOL 775Run_isDirectory(LPCTSTR fileName) 776{ 777 DWORD fileAttributes = GetFileAttributes(fileName); 778 779 return 780 (INVALID_FILE_ATTRIBUTES != fileAttributes) 781 && (0 != (FILE_ATTRIBUTE_DIRECTORY & fileAttributes)); 782} 783 784static BOOL 785Run_isFile(LPCTSTR fileName) 786{ 787 DWORD fileAttributes = GetFileAttributes(fileName); 788 789 return 790 (INVALID_FILE_ATTRIBUTES != fileAttributes) 791 && (0 == (FILE_ATTRIBUTE_DIRECTORY & fileAttributes)); 792} 793 794static DWORD 795Run_openProcessAndResumeThread(DWORD processId, DWORD threadId, HANDLE *process) 796{ 797 HANDLE p 798 = OpenProcess( 799 PROCESS_QUERY_INFORMATION | SYNCHRONIZE, 800 FALSE, 801 processId); 802 DWORD error; 803 804 if (p) 805 { 806 HANDLE t = OpenThread(THREAD_SUSPEND_RESUME, FALSE, threadId); 807 808 if (t) 809 { 810 DWORD prevSuspendCount = ResumeThread(t); 811 812 if (1 == prevSuspendCount) 813 { 814 *process = p; 815 error = ERROR_SUCCESS; 816 } 817 else 818 error = ERROR_NOT_FOUND; 819 CloseHandle(t); 820 } 821 else 822 error = GetLastError(); 823 if (ERROR_SUCCESS != error) 824 CloseHandle(p); 825 } 826 else 827 error = GetLastError(); 828 return error; 829} 830 831static DWORD 832Run_runAsCrashHandler(LPCTSTR executableFilePath, LPSTR cmdLine) 833{ 834 SECURITY_ATTRIBUTES pipeAttributes; 835 HANDLE readPipe = INVALID_HANDLE_VALUE; 836 HANDLE writePipe = INVALID_HANDLE_VALUE; 837 DWORD error; 838 839 ZeroMemory(&pipeAttributes, sizeof(pipeAttributes)); 840 pipeAttributes.nLength = sizeof(pipeAttributes); 841 pipeAttributes.bInheritHandle = TRUE; 842 if (CreatePipe(&readPipe, &writePipe, &pipeAttributes, 0)) 843 { 844 /* 845 * Do not let the child process inherit the readPipe because it does not 846 * need it. 847 */ 848 HANDLE currentProcess = GetCurrentProcess(); 849 HANDLE readPipeDuplicate = INVALID_HANDLE_VALUE; 850 851 if (DuplicateHandle( 852 currentProcess, readPipe, 853 currentProcess, &readPipeDuplicate, 854 0, 855 FALSE, 856 DUPLICATE_SAME_ACCESS)) 857 { 858 CloseHandle(readPipe); 859 readPipe = readPipeDuplicate; 860 861 error 862 = Run_runAsCrashHandlerWithPipe( 863 executableFilePath, cmdLine, 864 &readPipe, &writePipe); 865 } 866 else 867 error = GetLastError(); 868 869 if (INVALID_HANDLE_VALUE != readPipe) 870 CloseHandle(readPipe); 871 if (INVALID_HANDLE_VALUE != writePipe) 872 CloseHandle(writePipe); 873 } 874 else 875 error = GetLastError(); 876 return error; 877} 878 879static DWORD 880Run_runAsCrashHandlerWithPipe( 881 LPCTSTR executableFilePath, LPSTR cmdLine, 882 HANDLE *readPipe, HANDLE *writePipe) 883{ 884 LPCTSTR commandLineFormat = _T("run.exe --channel=%d %s"); 885 int commandLineLength = 256 + _tcslen(cmdLine); 886 LPTSTR commandLine 887 = (LPTSTR) malloc(sizeof(TCHAR) * (commandLineLength + 1)); 888 DWORD error; 889 890 if (commandLine) 891 { 892 LPTSTR lockFilePath = Run_getLockFilePath(); 893 DWORD exitCode = 0; 894 895 commandLineLength 896 = _sntprintf( 897 commandLine, 898 commandLineLength, 899 commandLineFormat, 900 (int) (intptr_t) (*writePipe), 901 cmdLine); 902 if (commandLineLength < 0) 903 { 904 free(commandLine); 905 commandLine = NULL; 906 error = ERROR_NOT_ENOUGH_MEMORY; 907 } 908 else 909 { 910 *(commandLine + commandLineLength) = 0; 911 error = ERROR_SUCCESS; 912 } 913 914 if (ERROR_SUCCESS == error) 915 { 916 BOOL waitForChildProcess 917 = !(lockFilePath && Run_isFile(lockFilePath)); 918 STARTUPINFO si; 919 PROCESS_INFORMATION pi; 920 921 ZeroMemory(&si, sizeof(si)); 922 si.cb = sizeof(si); 923 if (CreateProcess( 924 executableFilePath, 925 commandLine, 926 NULL, 927 NULL, 928 TRUE, 929 CREATE_NO_WINDOW, 930 NULL, 931 NULL, 932 &si, 933 &pi)) 934 { 935 HANDLE childProcessToWaitFor = NULL; 936 DWORD event; 937 938 /* We didn't really want to hold on to the thread. */ 939 CloseHandle(pi.hThread); 940 941 /* 942 * The command line of the child process is no longer necessary. 943 */ 944 free(commandLine); 945 commandLine = NULL; 946 947 /* 948 * The child process has inherited the writePipe so close it in 949 * the current process in order to let it know that it is only 950 * waiting for the child process. 951 */ 952 CloseHandle(*writePipe); 953 *writePipe = INVALID_HANDLE_VALUE; 954 955 /* 956 * Wait for the child process to tell the current process if it 957 * is to wait for another child process in order to get the exit 958 * code from it. 959 */ 960 if (INVALID_HANDLE_VALUE != *readPipe) 961 { 962 DWORD childToWaitFor[2]; 963 DWORD numberOfBytesRead = 0; 964 965 if (ReadFile( 966 *readPipe, 967 childToWaitFor, 968 sizeof(childToWaitFor), 969 &numberOfBytesRead, 970 NULL) 971 && (numberOfBytesRead 972 == sizeof(childToWaitFor)) 973 && childToWaitFor[0] 974 && childToWaitFor[1]) 975 { 976 error 977 = Run_openProcessAndResumeThread( 978 childToWaitFor[0], 979 childToWaitFor[1], 980 &childProcessToWaitFor); 981 } 982 CloseHandle(*readPipe); 983 *readPipe = INVALID_HANDLE_VALUE; 984 } 985 if (childProcessToWaitFor) 986 { 987 /* 988 * We'll have to wait for another process, not the one that 989 * we have just created ourselves. 990 */ 991 CloseHandle(pi.hProcess); 992 } 993 else 994 childProcessToWaitFor = pi.hProcess; 995 996 if (waitForChildProcess) 997 { 998 error = ERROR_SUCCESS; 999 do 1000 { 1001 event 1002 = WaitForSingleObject( 1003 childProcessToWaitFor, 1004 INFINITE); 1005 if (WAIT_FAILED == event) 1006 { 1007 error = GetLastError(); 1008 break; 1009 } 1010 } 1011 while (WAIT_TIMEOUT == event); 1012 1013 if ((ERROR_SUCCESS == error) 1014 && !GetExitCodeProcess( 1015 childProcessToWaitFor, 1016 &exitCode)) 1017 error = GetLastError(); 1018 } 1019 1020 CloseHandle(childProcessToWaitFor); 1021 } 1022 else 1023 error = GetLastError(); 1024 } 1025 1026 if (commandLine) 1027 free(commandLine); 1028 1029 if (lockFilePath) 1030 { 1031 /* 1032 * Notify the user if the application has crashed and ask whether it 1033 * is to be relaunched. 1034 */ 1035 if ((ERROR_SUCCESS == error) && exitCode) 1036 { 1037 error 1038 = Run_handleLauncherExitCode( 1039 exitCode, lockFilePath, 1040 executableFilePath); 1041 } 1042 free(lockFilePath); 1043 } 1044 } 1045 else 1046 error = ERROR_OUTOFMEMORY; 1047 return error; 1048} 1049 1050static DWORD 1051Run_runAsLauncher(LPCTSTR executableFilePath, LPSTR cmdLine) 1052{ 1053 LPSTR commandLine; 1054 DWORD error = ERROR_SUCCESS; 1055 1056 /* Parse the command line. */ 1057 if (cmdLine) 1058 { 1059 size_t commandLineLength; 1060 LPCSTR channelArg = "--channel="; 1061 size_t channelArgLength = strlen(channelArg); 1062 1063 commandLine = Run_skipWhitespace(cmdLine); 1064 commandLineLength = strlen(commandLine); 1065 1066 /* Get the value of the "--channel=" command-line argument. */ 1067 if ((commandLineLength > channelArgLength) 1068 && (strnicmp(commandLine, channelArg, channelArgLength) == 0)) 1069 { 1070 commandLine += channelArgLength; 1071 if (!isspace(*commandLine)) 1072 { 1073 HANDLE channel = (HANDLE) (intptr_t) atoi(commandLine); 1074 DWORD flags; 1075 char ch; 1076 1077 if (channel && GetHandleInformation(channel, &flags)) 1078 { 1079 /* 1080 * Make sure channel will not be inherited by any child 1081 * process. 1082 */ 1083 HANDLE currentProcess = GetCurrentProcess(); 1084 HANDLE channelDuplicate = INVALID_HANDLE_VALUE; 1085 1086 if (DuplicateHandle( 1087 currentProcess, channel, 1088 currentProcess, &channelDuplicate, 1089 0, 1090 FALSE, 1091 DUPLICATE_SAME_ACCESS)) 1092 Run_channel = channelDuplicate; 1093 CloseHandle(channel); 1094 } 1095 1096 /* 1097 * Skip the value of the channelArg and the whitespace after it. 1098 */ 1099 while ((ch = *commandLine) && !isspace(ch)) 1100 commandLine++; 1101 commandLine = Run_skipWhitespace(commandLine); 1102 } 1103 } 1104 } 1105 else 1106 commandLine = cmdLine; 1107 1108 /* Run the Java process in the directory of the executable file. */ 1109 if (_tcslen(executableFilePath) <= MAX_PATH) 1110 { 1111 TCHAR path[MAX_PATH]; 1112 DWORD pathCapacity = sizeof(path) / sizeof(TCHAR); 1113 LPTSTR filePart = NULL; 1114 DWORD pathLength 1115 = GetFullPathName( 1116 executableFilePath, 1117 pathCapacity, path, &filePart); 1118 1119 if (!pathLength) 1120 error = GetLastError(); 1121 else if (pathLength >= pathCapacity) 1122 error = ERROR_NOT_ENOUGH_MEMORY; 1123 else 1124 { 1125 /* 1126 * Strip the filePart because only the directory of the executable 1127 * file is necessary. 1128 */ 1129 if (filePart && *filePart) 1130 *filePart = 0; 1131 if (!SetCurrentDirectory(path)) 1132 error = GetLastError(); 1133 } 1134 } 1135 1136 error = Run_runJava(executableFilePath, commandLine); 1137 return error; 1138} 1139 1140static DWORD 1141Run_runJava(LPCTSTR executableFilePath, LPSTR cmdLine) 1142{ 1143 DWORD cdLength; 1144 DWORD error = ERROR_CALL_NOT_IMPLEMENTED; 1145 BOOL searchForJava = TRUE; 1146 1147 Run_cmdLine = cmdLine; 1148 1149 /* Try to use the private Java distributed with the application. */ 1150 if ((cdLength = GetCurrentDirectory(0, NULL))) 1151 { 1152 LPTSTR cd = (LPTSTR) malloc(sizeof(TCHAR) * cdLength); 1153 1154 if (cd) 1155 { 1156 cdLength = GetCurrentDirectory(cdLength, cd); 1157 if (cdLength) 1158 { 1159 if ((ERROR_SUCCESS != error) || searchForJava) 1160 error = Run_runJavaFromJavaHome(cd, TRUE, &searchForJava); 1161 } 1162 else 1163 error = GetLastError(); 1164 free(cd); 1165 } 1166 else 1167 error = ERROR_OUTOFMEMORY; 1168 } 1169 else 1170 error = GetLastError(); 1171 1172 /* Try to locate Java through the well-known enviroment variables. */ 1173 if ((ERROR_SUCCESS != error) || searchForJava) 1174 error = Run_runJavaFromEnvVar(_T("JAVA_HOME"), &searchForJava); 1175 if ((ERROR_SUCCESS != error) || searchForJava) 1176 error = Run_runJavaFromEnvVar(_T("JRE_HOME"), &searchForJava); 1177 if ((ERROR_SUCCESS != error) || searchForJava) 1178 error = Run_runJavaFromEnvVar(_T("JDK_HOME"), &searchForJava); 1179 1180 /* Try to locate Java through the Windows registry. */ 1181 if ((ERROR_SUCCESS != error) || searchForJava) 1182 error = Run_runJavaFromRegKey(HKEY_CURRENT_USER, &searchForJava); 1183 if ((ERROR_SUCCESS != error) || searchForJava) 1184 error = Run_runJavaFromRegKey(HKEY_LOCAL_MACHINE, &searchForJava); 1185 1186 /* Default to the Java in the PATH. */ 1187 if ((ERROR_SUCCESS != error) || searchForJava) 1188 error = Run_runJavaExe(_T("javaw.exe"), TRUE, &searchForJava); 1189 if ((ERROR_SUCCESS != error) || searchForJava) 1190 error = Run_runJavaExe(_T("java.exe"), TRUE, &searchForJava); 1191 1192 /* Notify the user that Java could not be found. */ 1193 if ((ERROR_SUCCESS != error) || searchForJava) 1194 { 1195 DWORD_PTR arguments[] = { (DWORD_PTR) PRODUCTNAME }; 1196 1197 if (Run_displayMessageBoxFromString( 1198 IDS_JAVANOTFOUND, arguments, 1199 executableFilePath, 1200 MB_ICONSTOP | MB_OK)) 1201 { 1202 /* 1203 * We have failed to locate Java but we've just notified the user 1204 * about this fact so the execution is according to plan. 1205 */ 1206 error = ERROR_SUCCESS; 1207 } 1208 else 1209 error = GetLastError(); 1210 } 1211 1212 return error; 1213} 1214 1215static DWORD 1216Run_runJavaExe(LPCTSTR javaExe, BOOL searchPath, BOOL *searchForJava) 1217{ 1218 DWORD error; 1219 1220 if (searchPath || Run_isFile(javaExe)) 1221 { 1222 LPCTSTR applicationName; 1223 LPCTSTR fileName; 1224 LPTSTR commandLine = NULL; 1225 1226 if (searchPath) 1227 { 1228 applicationName = NULL; 1229 fileName = javaExe; 1230 } 1231 else 1232 { 1233 applicationName = javaExe; 1234 fileName = _T("java.exe"); 1235 } 1236 error = Run_getJavaExeCommandLine(fileName, &commandLine); 1237 1238 if (ERROR_SUCCESS == error) 1239 { 1240 DWORD creationFlags; 1241 STARTUPINFO si; 1242 PROCESS_INFORMATION pi; 1243 1244 creationFlags = CREATE_NO_WINDOW; 1245 if (INVALID_HANDLE_VALUE != Run_channel) 1246 creationFlags |= CREATE_SUSPENDED; 1247 ZeroMemory(&si, sizeof(si)); 1248 si.cb = sizeof(si); 1249 if (CreateProcess( 1250 applicationName, 1251 commandLine, 1252 NULL, 1253 NULL, 1254 TRUE, 1255 creationFlags, 1256 NULL, 1257 NULL, 1258 &si, 1259 &pi)) 1260 { 1261 *searchForJava = FALSE; 1262 1263 /* 1264 * Tell the parent process it will have to wait for java.exe and 1265 * get its exit code. 1266 */ 1267 if (INVALID_HANDLE_VALUE != Run_channel) 1268 { 1269 DWORD processAndThreadIds[2]; 1270 DWORD numberOfBytesWritten; 1271 1272 processAndThreadIds[0] = pi.dwProcessId; 1273 processAndThreadIds[1] = pi.dwThreadId; 1274 WriteFile( 1275 Run_channel, 1276 processAndThreadIds, 1277 sizeof(processAndThreadIds), 1278 &numberOfBytesWritten, 1279 NULL); 1280 FlushFileBuffers(Run_channel); 1281 CloseHandle(Run_channel); 1282 Run_channel = INVALID_HANDLE_VALUE; 1283 } 1284 1285 CloseHandle(pi.hProcess); 1286 CloseHandle(pi.hThread); 1287 } 1288 else 1289 error = GetLastError(); 1290 } 1291 1292 if (commandLine) 1293 free(commandLine); 1294 } 1295 else 1296 error = ERROR_CALL_NOT_IMPLEMENTED; 1297 return error; 1298} 1299 1300static DWORD 1301Run_runJavaFromEnvVar(LPCTSTR envVar, BOOL *searchForJava) 1302{ 1303 TCHAR envVarValue[MAX_PATH + 1]; 1304 DWORD envVarValueLength 1305 = GetEnvironmentVariable(envVar, envVarValue, sizeof(envVarValue)); 1306 DWORD error; 1307 1308 if (envVarValueLength) 1309 { 1310 if (envVarValueLength >= sizeof(envVarValue)) 1311 error = ERROR_NOT_ENOUGH_MEMORY; 1312 else 1313 error = Run_runJavaFromJavaHome(envVarValue, TRUE, searchForJava); 1314 } 1315 else 1316 error = GetLastError(); 1317 return error; 1318} 1319 1320static DWORD 1321Run_runJavaFromJavaHome( 1322 LPCTSTR javaHome, BOOL searchForRuntimeLib, 1323 BOOL *searchForJava) 1324{ 1325 DWORD error; 1326 1327 if (Run_isDirectory(javaHome)) 1328 { 1329 size_t javaHomeLength = _tcslen(javaHome); 1330 LPTSTR path 1331 = (LPTSTR) malloc(sizeof(TCHAR) * (javaHomeLength + 19 + 1)); 1332 1333 if (path) 1334 { 1335 if (javaHomeLength >= 1) 1336 { 1337 TCHAR *ch = (TCHAR *) (javaHome + (javaHomeLength - 1)); 1338 1339 if ((_T('\\') == *ch) || (_T('/') == *ch)) 1340 { 1341 *ch = 0; 1342 javaHomeLength--; 1343 } 1344 } 1345 1346 _tcscpy(path, javaHome); 1347 error = ERROR_CALL_NOT_IMPLEMENTED; 1348 1349 if (searchForRuntimeLib) 1350 { 1351 _tcscpy(path + javaHomeLength, _T("\\bin\\client\\jvm.dll")); 1352 error 1353 = Run_runJavaFromRuntimeLib(path, javaHome, searchForJava); 1354 1355 if ((ERROR_SUCCESS != error) || *searchForJava) 1356 { 1357 _tcscpy( 1358 path + javaHomeLength, 1359 _T("\\bin\\server\\jvm.dll")); 1360 error 1361 = Run_runJavaFromRuntimeLib( 1362 path, 1363 javaHome, 1364 searchForJava); 1365 } 1366 } 1367 1368 if ((ERROR_SUCCESS != error) || *searchForJava) 1369 { 1370 if ((javaHomeLength >= 4) 1371 && (_tcsnicmp( 1372 javaHome + javaHomeLength - 4, 1373 _T("\\jre"), 1374 4) 1375 != 0)) 1376 { 1377 _tcscpy(path + javaHomeLength, _T("\\jre")); 1378 error 1379 = Run_runJavaFromJavaHome( 1380 path, searchForRuntimeLib, 1381 searchForJava); 1382 } 1383 1384 if ((ERROR_SUCCESS != error) || *searchForJava) 1385 { 1386 _tcscpy(path + javaHomeLength, _T("\\bin\\javaw.exe")); 1387 error = Run_runJavaExe(path, FALSE, searchForJava); 1388 1389 if ((ERROR_SUCCESS != error) || *searchForJava) 1390 { 1391 _tcscpy(path + javaHomeLength, _T("\\bin\\java.exe")); 1392 error = Run_runJavaExe(path, FALSE, searchForJava); 1393 } 1394 } 1395 } 1396 1397 free(path); 1398 } 1399 else 1400 error = ERROR_OUTOFMEMORY; 1401 } 1402 else 1403 error = ERROR_FILE_NOT_FOUND; 1404 return error; 1405} 1406 1407static DWORD 1408Run_runJavaFromRegKey(HKEY key, BOOL *searchForJava) 1409{ 1410 DWORD error; 1411 LPTSTR runtimeLib = NULL; 1412 LPTSTR javaHome = NULL; 1413 1414 error = Run_getJavaPathsFromRegKey(key, &runtimeLib, &javaHome); 1415 if (ERROR_SUCCESS == error) 1416 { 1417 if (runtimeLib) 1418 { 1419 error 1420 = Run_runJavaFromRuntimeLib( 1421 runtimeLib, 1422 javaHome, 1423 searchForJava); 1424 free(runtimeLib); 1425 } 1426 if (javaHome) 1427 { 1428 if ((ERROR_SUCCESS != error) || *searchForJava) 1429 { 1430 error 1431 = Run_runJavaFromJavaHome( 1432 javaHome, 1433 NULL == runtimeLib, 1434 searchForJava); 1435 } 1436 free(javaHome); 1437 } 1438 } 1439 return error; 1440} 1441 1442static DWORD 1443Run_runJavaFromRuntimeLib 1444 (LPCTSTR runtimeLib, LPCTSTR javaHome, BOOL *searchForJava) 1445{ 1446 HMODULE hRuntimeLib; 1447 DWORD error; 1448 1449 if (Run_isFile(runtimeLib)) 1450 { 1451 /* 1452 * It turns out that the bin directory in javaHome may contain 1453 * dependencies of the runtimeLib so add it to the PATH. Well, it may 1454 * not be standard but it happens to our private JRE. 1455 */ 1456 if (javaHome && Run_isDirectory(javaHome)) 1457 { 1458 size_t javaHomeLength = _tcslen(javaHome); 1459 LPTSTR javaHomeBin; 1460 1461 /* 1462 * Drop the last file name separator if any because we will be 1463 * adding one later on. 1464 */ 1465 while (javaHomeLength >= 1) 1466 { 1467 TCHAR ch = *(javaHome + (javaHomeLength - 1)); 1468 1469 if ((_T('\\') == ch) || (_T('/') == ch)) 1470 javaHomeLength--; 1471 else 1472 break; 1473 } 1474 1475 javaHomeBin 1476 = malloc( 1477 sizeof(TCHAR) * (javaHomeLength + 4 /* "\\bin" */ + 1)); 1478 if (javaHomeBin) 1479 { 1480 LPTSTR str = javaHomeBin; 1481 1482 _tcsncpy(str, javaHome, javaHomeLength); 1483 str += javaHomeLength; 1484 _tcsncpy(str, _T("\\bin"), 4); 1485 str += 4; 1486 *str = 0; 1487 1488 if (Run_isDirectory(javaHomeBin)) 1489 Run_addPath(javaHomeBin); 1490 1491 free(javaHomeBin); 1492 } 1493 } 1494 1495 hRuntimeLib = LoadLibrary(runtimeLib); 1496 } 1497 else 1498 hRuntimeLib = NULL; 1499 1500 if (hRuntimeLib) 1501 { 1502 typedef jint (JNICALL *JNICreateJavaVMFunc)(JavaVM **, void **, void *); 1503 JNICreateJavaVMFunc jniCreateJavaVM 1504 = (JNICreateJavaVMFunc) 1505 GetProcAddress(hRuntimeLib, "JNI_CreateJavaVM"); 1506 1507 if (jniCreateJavaVM) 1508 { 1509 LPTSTR optionStrings = NULL; 1510 jint optionStringCount = 0; 1511 1512 error 1513 = Run_getJavaVMOptionStrings( 1514 0, 0, 0, 1515 &optionStrings, &optionStringCount); 1516 if (ERROR_SUCCESS == error) 1517 { 1518 JavaVMOption *options 1519 = calloc(optionStringCount, sizeof(JavaVMOption)); 1520 1521 if (options) 1522 { 1523 jint i; 1524 LPTSTR optionString; 1525 JavaVMInitArgs javaVMInitArgs; 1526 JavaVM *javaVM; 1527 JNIEnv *jniEnv; 1528 1529 for (i = 0, optionString = optionStrings; 1530 i < optionStringCount; 1531 i++, optionString += (_tcslen(optionString) + 1)) 1532 (options + i)->optionString = optionString; 1533 1534 javaVMInitArgs.ignoreUnrecognized = JNI_FALSE; 1535 javaVMInitArgs.nOptions = optionStringCount; 1536 javaVMInitArgs.options = options; 1537 javaVMInitArgs.version = JNI_VERSION_1_2; 1538 if (jniCreateJavaVM( 1539 &javaVM, 1540 (void **) &jniEnv, 1541 &javaVMInitArgs)) 1542 error = ERROR_FUNCTION_FAILED; 1543 else 1544 { 1545 free(options); 1546 options = NULL; 1547 free(optionStrings); 1548 optionStrings = NULL; 1549 1550 error = Run_callStaticVoidMain(jniEnv, searchForJava); 1551 if (JNI_TRUE == (*jniEnv)->ExceptionCheck(jniEnv)) 1552 (*jniEnv)->ExceptionClear(jniEnv); 1553 1554 (*javaVM)->DestroyJavaVM(javaVM); 1555 } 1556 if (options) 1557 free(options); 1558 } 1559 else 1560 error = ERROR_OUTOFMEMORY; 1561 if (optionStrings) 1562 free(optionStrings); 1563 } 1564 } 1565 else 1566 error = GetLastError(); 1567 FreeLibrary(hRuntimeLib); 1568 } 1569 else 1570 error = GetLastError(); 1571 return error; 1572} 1573 1574static LPSTR 1575Run_skipWhitespace(LPSTR str) 1576{ 1577 char ch; 1578 1579 while ((ch = *str) && isspace(ch)) 1580 str++; 1581 return str; 1582} 1583 1584int CALLBACK 1585WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine, int cmdShow) 1586{ 1587 LPTSTR executableFilePath = NULL; 1588 DWORD error; 1589 1590 AttachConsole(ATTACH_PARENT_PROCESS); 1591 1592 error = Run_getExecutableFilePath(&executableFilePath); 1593 if (ERROR_SUCCESS == error) 1594 { 1595 BOOL runAsLauncher = FALSE; 1596 1597 Run_equalsParentProcessExecutableFilePath( 1598 executableFilePath, 1599 &runAsLauncher); 1600 if (runAsLauncher) 1601 { 1602 error = Run_runAsLauncher(executableFilePath, cmdLine); 1603 1604 if (ERROR_SUCCESS !=…
Large files files are truncated, but you can click here to view the full file