1 /*
   2  * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include "Platform.h"
  27 
  28 #ifdef WINDOWS
  29 
  30 #include "JavaVirtualMachine.h"
  31 #include "WindowsPlatform.h"
  32 #include "Package.h"
  33 #include "Helpers.h"
  34 #include "PlatformString.h"
  35 #include "Macros.h"
  36 
  37 #include <map>
  38 #include <vector>
  39 #include <regex>
  40 
  41 #define WINDOWS_JPACKAGER_TMP_DIR \
  42         L"\\AppData\\LocalLow\\Sun\\Java\\JPackager\\tmp"
  43 
  44 
  45 class Registry {
  46 private:
  47     HKEY FKey;
  48     HKEY FOpenKey;
  49     bool FOpen;
  50 
  51 public:
  52     Registry(HKEY Key) {
  53         FOpen = false;
  54         FKey = Key;
  55     }
  56 
  57     ~Registry() {
  58         Close();
  59     }
  60 
  61     void Close() {
  62         if (FOpen == true) {
  63             RegCloseKey(FOpenKey);
  64         }
  65     }
  66 
  67     bool Open(TString SubKey) {
  68         bool result = false;
  69         Close();
  70 
  71         if (RegOpenKeyEx(FKey, SubKey.data(), 0, KEY_READ, &FOpenKey) ==
  72                 ERROR_SUCCESS) {
  73             result = true;
  74         }
  75 
  76         return result;
  77     }
  78 
  79     std::list<TString> GetKeys() {
  80         std::list<TString> result;
  81         DWORD count;
  82 
  83         if (RegQueryInfoKey(FOpenKey, NULL, NULL, NULL, NULL, NULL, NULL,
  84                  &count, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
  85 
  86             DWORD length = 255;
  87             DynamicBuffer<TCHAR> buffer(length);
  88             if (buffer.GetData() == NULL) {
  89                 return result;
  90             }
  91 
  92             for (unsigned int index = 0; index < count; index++) {
  93                 buffer.Zero();
  94                 DWORD status = RegEnumValue(FOpenKey, index, buffer.GetData(),
  95                                             &length, NULL, NULL, NULL, NULL);
  96 
  97                 while (status == ERROR_MORE_DATA) {
  98                     length = length * 2;
  99                     if (!buffer.Resize(length)) {
 100                         return result;
 101                     }
 102                     status = RegEnumValue(FOpenKey, index, buffer.GetData(),
 103                                           &length, NULL, NULL, NULL, NULL);
 104                 }
 105 
 106                 if (status == ERROR_SUCCESS) {
 107                     TString value = buffer.GetData();
 108                     result.push_back(value);
 109                 }
 110             }
 111         }
 112 
 113         return result;
 114     }
 115 
 116     TString ReadString(TString Name) {
 117         TString result;
 118         DWORD length;
 119         DWORD dwRet;
 120         DynamicBuffer<wchar_t> buffer(0);
 121         length = 0;
 122 
 123         dwRet = RegQueryValueEx(FOpenKey, Name.data(), NULL, NULL, NULL,
 124                 &length);
 125         if (dwRet == ERROR_MORE_DATA || dwRet == 0) {
 126             if (!buffer.Resize(length + 1)) {
 127                 return result;
 128             }
 129             dwRet = RegQueryValueEx(FOpenKey, Name.data(), NULL, NULL,
 130                     (LPBYTE)buffer.GetData(), &length);
 131             result = buffer.GetData();
 132         }
 133 
 134         return result;
 135     }
 136 };
 137 
 138 WindowsPlatform::WindowsPlatform(void) : Platform(), GenericPlatform() {
 139     FMainThread = ::GetCurrentThreadId();
 140 }
 141 
 142 WindowsPlatform::~WindowsPlatform(void) {
 143 }
 144 
 145 TCHAR* WindowsPlatform::ConvertStringToFileSystemString(TCHAR* Source,
 146         bool &release) {
 147     // Not Implemented.
 148     return NULL;
 149 }
 150 
 151 TCHAR* WindowsPlatform::ConvertFileSystemStringToString(TCHAR* Source,
 152         bool &release) {
 153     // Not Implemented.
 154     return NULL;
 155 }
 156 
 157 void WindowsPlatform::SetCurrentDirectory(TString Value) {
 158     _wchdir(Value.data());
 159 }
 160 
 161 TString WindowsPlatform::GetPackageRootDirectory() {
 162     TString filename = GetModuleFileName();
 163     return FilePath::ExtractFilePath(filename);
 164 }
 165 
 166 TString WindowsPlatform::GetAppDataDirectory() {
 167     TString result;
 168     TCHAR path[MAX_PATH];
 169 
 170     if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, path) == S_OK) {
 171         result = path;
 172     }
 173 
 174     return result;
 175 }
 176 
 177 void WindowsPlatform::ShowMessage(TString title, TString description) {
 178     MessageBox(NULL, description.data(),
 179             !title.empty() ? title.data() : description.data(),
 180             MB_ICONERROR | MB_OK);
 181 }
 182 
 183 void WindowsPlatform::ShowMessage(TString description) {
 184     TString appname = GetModuleFileName();
 185     appname = FilePath::ExtractFileName(appname);
 186     MessageBox(NULL, description.data(), appname.data(), MB_ICONERROR | MB_OK);
 187 }
 188 
 189 MessageResponse WindowsPlatform::ShowResponseMessage(TString title,
 190         TString description) {
 191     MessageResponse result = mrCancel;
 192 
 193     if (::MessageBox(NULL, description.data(), title.data(), MB_OKCANCEL) ==
 194             IDOK) {
 195         result = mrOK;
 196     }
 197 
 198     return result;
 199 }
 200 
 201 TString WindowsPlatform::GetBundledJVMLibraryFileName(TString RuntimePath) {
 202     TString result = FilePath::IncludeTrailingSeparator(RuntimePath) +
 203         _T("jre\\bin\\jli.dll");
 204 
 205     if (FilePath::FileExists(result) == false) {
 206         result = FilePath::IncludeTrailingSeparator(RuntimePath) +
 207             _T("bin\\jli.dll");
 208     }
 209 
 210     return result;
 211 }
 212 
 213 ISectionalPropertyContainer* WindowsPlatform::GetConfigFile(TString FileName) {
 214     IniFile *result = new IniFile();
 215     if (result == NULL) {
 216         return NULL;
 217     }
 218 
 219     if (result->LoadFromFile(FileName) == false) {
 220         // New property file format was not found,
 221         // attempt to load old property file format.
 222         Helpers::LoadOldConfigFile(FileName, result);
 223     }
 224 
 225     return result;
 226 }
 227 
 228 TString WindowsPlatform::GetModuleFileName() {
 229     TString result;
 230     DynamicBuffer<wchar_t> buffer(MAX_PATH);
 231     if (buffer.GetData() == NULL) {
 232         return result;
 233     }
 234 
 235     ::GetModuleFileName(NULL, buffer.GetData(),
 236             static_cast<DWORD>(buffer.GetSize()));
 237 
 238     while (ERROR_INSUFFICIENT_BUFFER == GetLastError()) {
 239         if (!buffer.Resize(buffer.GetSize() * 2)) {
 240             return result;
 241         }
 242         ::GetModuleFileName(NULL, buffer.GetData(),
 243                 static_cast<DWORD>(buffer.GetSize()));
 244     }
 245 
 246     result = buffer.GetData();
 247     return result;
 248 }
 249 
 250 Module WindowsPlatform::LoadLibrary(TString FileName) {
 251     return ::LoadLibrary(FileName.data());
 252 }
 253 
 254 void WindowsPlatform::FreeLibrary(Module AModule) {
 255     ::FreeLibrary((HMODULE)AModule);
 256 }
 257 
 258 Procedure WindowsPlatform::GetProcAddress(Module AModule,
 259         std::string MethodName) {
 260     return ::GetProcAddress((HMODULE)AModule, MethodName.c_str());
 261 }
 262 
 263 bool WindowsPlatform::IsMainThread() {
 264     bool result = (FMainThread == ::GetCurrentThreadId());
 265     return result;
 266 }
 267 
 268 TString WindowsPlatform::GetTempDirectory() {
 269     TString result;
 270     PWSTR userDir = 0;
 271 
 272     if (SUCCEEDED(SHGetKnownFolderPath(
 273                     FOLDERID_Profile,
 274                     0,
 275                     NULL,
 276                     &userDir))) {
 277         result = userDir;
 278         result += WINDOWS_JPACKAGER_TMP_DIR;
 279         CoTaskMemFree(userDir);
 280     }
 281 
 282     return result;
 283 }
 284 
 285 static BOOL CALLBACK enumWindows(HWND winHandle, LPARAM lParam) {
 286     DWORD pid = (DWORD)lParam, wPid = 0;
 287     GetWindowThreadProcessId(winHandle, &wPid);
 288     if (pid == wPid)  {
 289         SetForegroundWindow(winHandle);
 290         return FALSE;
 291     }
 292     return TRUE;
 293 }
 294 
 295 void WindowsPlatform::reactivateAnotherInstance() {
 296     if (singleInstanceProcessId == 0) {
 297         printf("Unable to reactivate another instance, PID is undefined");
 298         return;
 299     }
 300     EnumWindows(&enumWindows, (LPARAM)singleInstanceProcessId);
 301 }
 302 
 303 // returns true if another instance is already running.
 304 // if false, we need to continue regular launch.
 305 bool WindowsPlatform::CheckForSingleInstance(TString name) {
 306     if (SingleInstance::getInstance(name)->IsAnotherInstanceRunning()) {
 307         // read PID
 308         DWORD pid = SingleInstance::getInstance(name)->readPid();
 309         if (pid != 0) {
 310             singleInstanceProcessId = pid;
 311             return true;
 312         }
 313     } else {
 314         // it is the first intance
 315         // write pid and continue regular launch
 316         SingleInstance::getInstance(name)->writePid(GetCurrentProcessId());
 317     }
 318     return false;
 319 }
 320 
 321 SingleInstance::SingleInstance(TString& name_): BUF_SIZE(256), _name(name_),
 322         _hMapFile(NULL), _pBuf(NULL) {
 323     _mutex = CreateMutex(NULL, TRUE, name_.data());
 324     _lastError = GetLastError();
 325     _sharedMemoryName = _T("Local\\jpackager-") + _name;
 326 }
 327 
 328 SingleInstance::~SingleInstance() {
 329     if (_pBuf != NULL) {
 330         UnmapViewOfFile(_pBuf);
 331         _pBuf = NULL;
 332     }
 333 
 334     if (_hMapFile != NULL) {
 335         CloseHandle(_hMapFile);
 336         _hMapFile = NULL;
 337     }
 338 
 339     if (_mutex != NULL) {
 340         CloseHandle(_mutex);
 341         _mutex = NULL;
 342     }
 343 }
 344 
 345 bool SingleInstance::writePid(DWORD pid) {
 346     _hMapFile = CreateFileMapping(
 347                  INVALID_HANDLE_VALUE,
 348                  NULL,
 349                  PAGE_READWRITE,
 350                  0,
 351                  BUF_SIZE,
 352                  _sharedMemoryName.data());
 353 
 354     if (_hMapFile == NULL) {
 355         return false;
 356     }
 357 
 358     _pBuf = (LPTSTR) MapViewOfFile(_hMapFile,
 359                         FILE_MAP_ALL_ACCESS,
 360                         0,
 361                         0,
 362                         BUF_SIZE);
 363 
 364     if (_pBuf == NULL) {
 365         CloseHandle(_hMapFile);
 366         _hMapFile = NULL;
 367         return false;
 368     }
 369 
 370     CopyMemory((PVOID)_pBuf, &pid, sizeof(DWORD));
 371 
 372     return true;
 373 }
 374 
 375 DWORD SingleInstance::readPid() {
 376     _hMapFile = OpenFileMapping(
 377                    FILE_MAP_ALL_ACCESS,
 378                    FALSE,
 379                    _sharedMemoryName.data());
 380 
 381     if (_hMapFile == NULL) {
 382         return 0;
 383     }
 384 
 385    _pBuf = (LPTSTR) MapViewOfFile(_hMapFile,
 386                FILE_MAP_ALL_ACCESS,
 387                0,
 388                0,
 389                BUF_SIZE);
 390 
 391     if (_pBuf == NULL) {
 392         CloseHandle(_hMapFile);
 393         _hMapFile = NULL;
 394         return 0;
 395     }
 396 
 397     DWORD pid = 0;
 398     CopyMemory(&pid, (PVOID)_pBuf,  sizeof(DWORD));
 399 
 400     return pid;
 401 }
 402 
 403 TPlatformNumber WindowsPlatform::GetMemorySize() {
 404     SYSTEM_INFO si;
 405     GetSystemInfo(&si);
 406     size_t result = (size_t)si.lpMaximumApplicationAddress;
 407     result = result / 1048576; // Convert from bytes to megabytes.
 408     return result;
 409 }
 410 
 411 std::vector<TString> WindowsPlatform::GetLibraryImports(
 412        const TString FileName) {
 413  std::vector<TString> result;
 414     WindowsLibrary library(FileName);
 415     result = library.GetImports();
 416  return result;
 417 }
 418 
 419 std::vector<TString> FilterList(std::vector<TString> &Items,
 420         std::wregex Pattern) {
 421     std::vector<TString> result;
 422 
 423     for (std::vector<TString>::iterator it = Items.begin();
 424             it != Items.end(); ++it) {
 425         TString item = *it;
 426         std::wsmatch match;
 427 
 428         if (std::regex_search(item, match, Pattern)) {
 429             result.push_back(item);
 430         }
 431     }
 432     return result;
 433 }
 434 
 435 std::vector<TString> WindowsPlatform::FilterOutRuntimeDependenciesForPlatform(
 436         std::vector<TString> Imports) {
 437     std::vector<TString> result;
 438     Package& package = Package::GetInstance();
 439     Macros& macros = Macros::GetInstance();
 440     TString runtimeDir = macros.ExpandMacros(package.GetJVMRuntimeDirectory());
 441     std::vector<TString> filelist = FilterList(Imports,
 442             std::wregex(_T("MSVCR.*.DLL"), std::regex_constants::icase));
 443 
 444     for (std::vector<TString>::iterator it = filelist.begin();
 445             it != filelist.end(); ++it) {
 446         TString filename = *it;
 447         TString msvcr100FileName = FilePath::IncludeTrailingSeparator(
 448                 runtimeDir) + _T("jre\\bin\\") + filename;
 449 
 450         if (FilePath::FileExists(msvcr100FileName) == true) {
 451             result.push_back(msvcr100FileName);
 452             break;
 453         }
 454         else {
 455             msvcr100FileName = FilePath::IncludeTrailingSeparator(runtimeDir)
 456                     + _T("bin\\") + filename;
 457 
 458             if (FilePath::FileExists(msvcr100FileName) == true) {
 459                 result.push_back(msvcr100FileName);
 460                 break;
 461             }
 462         }
 463     }
 464 
 465  return result;
 466 }
 467 
 468 Process* WindowsPlatform::CreateProcess() {
 469     return new WindowsProcess();
 470 }
 471 
 472 #ifdef DEBUG
 473 bool WindowsPlatform::IsNativeDebuggerPresent() {
 474     bool result = false;
 475 
 476     if (IsDebuggerPresent() == TRUE) {
 477         result = true;
 478     }
 479 
 480     return result;
 481 }
 482 
 483 int WindowsPlatform::GetProcessID() {
 484     int pid = GetProcessId(GetCurrentProcess());
 485     return pid;
 486 }
 487 #endif //DEBUG
 488 
 489 
 490 FileHandle::FileHandle(std::wstring FileName) {
 491     FHandle = ::CreateFile(FileName.data(), GENERIC_READ, FILE_SHARE_READ,
 492             NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
 493 }
 494 
 495 FileHandle::~FileHandle() {
 496     if (IsValid() == true) {
 497         ::CloseHandle(FHandle);
 498     }
 499 }
 500 
 501 bool FileHandle::IsValid() {
 502     return FHandle != INVALID_HANDLE_VALUE;
 503 }
 504 
 505 HANDLE FileHandle::GetHandle() {
 506     return FHandle;
 507 }
 508 
 509 FileMappingHandle::FileMappingHandle(HANDLE FileHandle) {
 510     FHandle = ::CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL);
 511 }
 512 
 513 bool FileMappingHandle::IsValid() {
 514     return FHandle != NULL;
 515 }
 516 
 517 FileMappingHandle::~FileMappingHandle() {
 518     if (IsValid() == true) {
 519         ::CloseHandle(FHandle);
 520     }
 521 }
 522 
 523 HANDLE FileMappingHandle::GetHandle() {
 524     return FHandle;
 525 }
 526 
 527 FileData::FileData(HANDLE Handle) {
 528     FBaseAddress = ::MapViewOfFile(Handle, FILE_MAP_READ, 0, 0, 0);
 529 }
 530 
 531 FileData::~FileData() {
 532     if (IsValid() == true) {
 533         ::UnmapViewOfFile(FBaseAddress);
 534     }
 535 }
 536 
 537 bool FileData::IsValid() {
 538     return FBaseAddress != NULL;
 539 }
 540 
 541 LPVOID FileData::GetBaseAddress() {
 542     return FBaseAddress;
 543 }
 544 
 545 
 546 WindowsLibrary::WindowsLibrary(std::wstring FileName) {
 547     FFileName = FileName;
 548 }
 549 
 550 std::vector<TString> WindowsLibrary::GetImports() {
 551     std::vector<TString> result;
 552     FileHandle library(FFileName);
 553 
 554     if (library.IsValid() == true) {
 555         FileMappingHandle mapping(library.GetHandle());
 556 
 557         if (mapping.IsValid() == true) {
 558             FileData fileData(mapping.GetHandle());
 559 
 560             if (fileData.IsValid() == true) {
 561                 PIMAGE_DOS_HEADER dosHeader =
 562                         (PIMAGE_DOS_HEADER)fileData.GetBaseAddress();
 563                 PIMAGE_FILE_HEADER pImgFileHdr =
 564                         (PIMAGE_FILE_HEADER)fileData.GetBaseAddress();
 565                 if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) {
 566                     result = DumpPEFile(dosHeader);
 567                 }
 568             }
 569         }
 570     }
 571 
 572     return result;
 573 }
 574 
 575 // Given an RVA, look up the section header that encloses it and return a
 576 // pointer to its IMAGE_SECTION_HEADER
 577 PIMAGE_SECTION_HEADER WindowsLibrary::GetEnclosingSectionHeader(DWORD rva,
 578                                                 PIMAGE_NT_HEADERS pNTHeader) {
 579     PIMAGE_SECTION_HEADER result = 0;
 580     PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader);
 581 
 582     for (unsigned index = 0; index < pNTHeader->FileHeader.NumberOfSections;
 583             index++, section++) {
 584         // Is the RVA is within this section?
 585         if ((rva >= section->VirtualAddress) &&
 586             (rva < (section->VirtualAddress + section->Misc.VirtualSize))) {
 587             result = section;
 588         }
 589     }
 590 
 591     return result;
 592 }
 593 
 594 LPVOID WindowsLibrary::GetPtrFromRVA(DWORD rva, PIMAGE_NT_HEADERS pNTHeader,
 595         DWORD imageBase) {
 596     LPVOID result = 0;
 597     PIMAGE_SECTION_HEADER pSectionHdr = GetEnclosingSectionHeader(rva,
 598             pNTHeader);
 599 
 600     if (pSectionHdr != NULL) {
 601         INT delta = (INT)(
 602                 pSectionHdr->VirtualAddress-pSectionHdr->PointerToRawData);
 603         DWORD_PTR dwp = (DWORD_PTR) (imageBase + rva - delta);
 604         result = reinterpret_cast<LPVOID>(dwp); // VS2017 - FIXME
 605     }
 606 
 607     return result;
 608 }
 609 
 610 std::vector<TString> WindowsLibrary::GetImportsSection(DWORD base,
 611         PIMAGE_NT_HEADERS pNTHeader) {
 612     std::vector<TString> result;
 613 
 614     // Look up where the imports section is located. Normally in
 615     // the .idata section,
 616     // but not necessarily so. Therefore, grab the RVA from the data dir.
 617     DWORD importsStartRVA = pNTHeader->OptionalHeader.DataDirectory[
 618             IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
 619 
 620     if (importsStartRVA != NULL) {
 621         // Get the IMAGE_SECTION_HEADER that contains the imports. This is
 622         // usually the .idata section, but doesn't have to be.
 623         PIMAGE_SECTION_HEADER pSection =
 624                 GetEnclosingSectionHeader(importsStartRVA, pNTHeader);
 625 
 626         if (pSection != NULL) {
 627             PIMAGE_IMPORT_DESCRIPTOR importDesc =
 628                     (PIMAGE_IMPORT_DESCRIPTOR)GetPtrFromRVA(
 629                     importsStartRVA, pNTHeader,base);
 630 
 631             if (importDesc != NULL) {
 632                 while (true)
 633                 {
 634                     // See if we've reached an empty IMAGE_IMPORT_DESCRIPTOR
 635                     if ((importDesc->TimeDateStamp == 0) &&
 636                             (importDesc->Name == 0)) {
 637                         break;
 638                     }
 639 
 640                     std::string filename = (char*)GetPtrFromRVA(
 641                             importDesc->Name, pNTHeader, base);
 642                     result.push_back(PlatformString(filename));
 643                     importDesc++;   // advance to next IMAGE_IMPORT_DESCRIPTOR
 644                 }
 645             }
 646         }
 647     }
 648 
 649     return result;
 650 }
 651 
 652 std::vector<TString> WindowsLibrary::DumpPEFile(PIMAGE_DOS_HEADER dosHeader) {
 653     std::vector<TString> result;
 654     // all of this is VS2017 - FIXME
 655     DWORD_PTR dwDosHeaders = reinterpret_cast<DWORD_PTR>(dosHeader);
 656     DWORD_PTR dwPIHeaders = dwDosHeaders + (DWORD)(dosHeader->e_lfanew);
 657 
 658     PIMAGE_NT_HEADERS pNTHeader =
 659                       reinterpret_cast<PIMAGE_NT_HEADERS>(dwPIHeaders);
 660 
 661     // Verify that the e_lfanew field gave us a reasonable
 662     // pointer and the PE signature.
 663     // TODO: To really fix JDK-8131321 this condition needs to be changed.
 664     // There is a matching change
 665     // in JavaVirtualMachine.cpp that also needs to be changed.
 666     if (pNTHeader->Signature == IMAGE_NT_SIGNATURE) {
 667         DWORD base = (DWORD)(dwDosHeaders);
 668         result = GetImportsSection(base, pNTHeader);
 669     }
 670 
 671     return result;
 672 }
 673 
 674 #include <TlHelp32.h>
 675 
 676 WindowsJob::WindowsJob() {
 677     FHandle = NULL;
 678 }
 679 
 680 WindowsJob::~WindowsJob() {
 681     if (FHandle != NULL) {
 682         CloseHandle(FHandle);
 683     }
 684 }
 685 
 686 HANDLE WindowsJob::GetHandle() {
 687     if (FHandle == NULL) {
 688         FHandle = CreateJobObject(NULL, NULL); // GLOBAL
 689 
 690         if (FHandle == NULL)
 691         {
 692             ::MessageBox( 0, _T("Could not create job object"),
 693                     _T("TEST"), MB_OK);
 694         }
 695         else
 696         {
 697             JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
 698 
 699             // Configure all child processes associated with
 700             // the job to terminate when the
 701             jeli.BasicLimitInformation.LimitFlags =
 702                     JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
 703             if (0 == SetInformationJobObject(FHandle,
 704                     JobObjectExtendedLimitInformation, &jeli, sizeof(jeli))) {
 705                 ::MessageBox( 0, _T("Could not SetInformationJobObject"),
 706                         _T("TEST"), MB_OK);
 707             }
 708         }
 709     }
 710 
 711     return FHandle;
 712 }
 713 
 714 // Initialize static member of WindowsProcess
 715 WindowsJob WindowsProcess::FJob;
 716 
 717 WindowsProcess::WindowsProcess() : Process() {
 718     FRunning = false;
 719 }
 720 
 721 WindowsProcess::~WindowsProcess() {
 722     Terminate();
 723 }
 724 
 725 void WindowsProcess::Cleanup() {
 726     CloseHandle(FProcessInfo.hProcess);
 727     CloseHandle(FProcessInfo.hThread);
 728 }
 729 
 730 bool WindowsProcess::IsRunning() {
 731     bool result = false;
 732 
 733     HANDLE handle = ::CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
 734     if (handle == INVALID_HANDLE_VALUE) {
 735         return false;
 736     }
 737 
 738     PROCESSENTRY32 process = { 0 };
 739     process.dwSize = sizeof(process);
 740 
 741     if (::Process32First(handle, &process)) {
 742         do {
 743             if (process.th32ProcessID == FProcessInfo.dwProcessId) {
 744                 result = true;
 745                 break;
 746             }
 747         }
 748         while (::Process32Next(handle, &process));
 749     }
 750 
 751     CloseHandle(handle);
 752 
 753     return result;
 754 }
 755 
 756 bool WindowsProcess::Terminate() {
 757     bool result = false;
 758 
 759     if (IsRunning() == true && FRunning == true) {
 760         FRunning = false;
 761     }
 762 
 763     return result;
 764 }
 765 
 766 bool WindowsProcess::Execute(const TString Application,
 767         const std::vector<TString> Arguments, bool AWait) {
 768     bool result = false;
 769 
 770     if (FRunning == false) {
 771         FRunning = true;
 772 
 773         STARTUPINFO startupInfo;
 774         ZeroMemory(&startupInfo, sizeof(startupInfo));
 775         startupInfo.cb = sizeof(startupInfo);
 776         ZeroMemory(&FProcessInfo, sizeof(FProcessInfo));
 777 
 778         TString command = Application;
 779 
 780         for (std::vector<TString>::const_iterator iterator = Arguments.begin();
 781                 iterator != Arguments.end(); iterator++) {
 782             command += TString(_T(" ")) + *iterator;
 783         }
 784 
 785         if (::CreateProcess(Application.data(), (wchar_t*)command.data(), NULL,
 786             NULL, FALSE, 0, NULL, NULL, &startupInfo, &FProcessInfo) == FALSE) {
 787             TString message = PlatformString::Format(
 788                     _T("Error: Unable to create process %s"),
 789                      Application.data());
 790             throw Exception(message);
 791         }
 792         else {
 793             if (FJob.GetHandle() != NULL) {
 794                 if (::AssignProcessToJobObject(FJob.GetHandle(),
 795                          FProcessInfo.hProcess) == 0) {
 796                     // Failed to assign process to job. It doesn't prevent
 797                     // anything from continuing so continue.
 798                 }
 799             }
 800 
 801             // Wait until child process exits.
 802             if (AWait == true) {
 803                 Wait();
 804                 // Close process and thread handles.
 805                 Cleanup();
 806             }
 807         }
 808     }
 809 
 810     return result;
 811 }
 812 
 813 bool WindowsProcess::Wait() {
 814     bool result = false;
 815 
 816     WaitForSingleObject(FProcessInfo.hProcess, INFINITE);
 817     return result;
 818 }
 819 
 820 TProcessID WindowsProcess::GetProcessID() {
 821     return FProcessInfo.dwProcessId;
 822 }
 823 
 824 bool WindowsProcess::ReadOutput() {
 825     bool result = false;
 826     // TODO implement
 827     return result;
 828 }
 829 
 830 void WindowsProcess::SetInput(TString Value) {
 831     // TODO implement
 832 }
 833 
 834 std::list<TString> WindowsProcess::GetOutput() {
 835     ReadOutput();
 836     return Process::GetOutput();
 837 }
 838 
 839 #endif // WINDOWS