1 /* 2 * Copyright (c) 2014, 2020, 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 #include "JavaVirtualMachine.h" 29 #include "WindowsPlatform.h" 30 #include "Package.h" 31 #include "Helpers.h" 32 #include "PlatformString.h" 33 #include "Macros.h" 34 35 #include <map> 36 #include <vector> 37 #include <regex> 38 #include <fstream> 39 #include <locale> 40 #include <codecvt> 41 42 using namespace std; 43 44 #define WINDOWS_JPACKAGE_TMP_DIR \ 45 L"\\AppData\\Local\\Java\\JPackage\\tmp" 46 47 class Registry { 48 private: 49 HKEY FKey; 50 HKEY FOpenKey; 51 bool FOpen; 52 53 public: 54 55 Registry(HKEY Key) { 56 FOpen = false; 57 FKey = Key; 58 } 59 60 ~Registry() { 61 Close(); 62 } 63 64 void Close() { 65 if (FOpen == true) { 66 RegCloseKey(FOpenKey); 67 } 68 } 69 70 bool Open(TString SubKey) { 71 bool result = false; 72 Close(); 73 74 if (RegOpenKeyEx(FKey, SubKey.data(), 0, KEY_READ, &FOpenKey) == 75 ERROR_SUCCESS) { 76 result = true; 77 } 78 79 return result; 80 } 81 82 std::list<TString> GetKeys() { 83 std::list<TString> result; 84 DWORD count; 85 86 if (RegQueryInfoKey(FOpenKey, NULL, NULL, NULL, NULL, NULL, NULL, 87 &count, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { 88 89 DWORD length = 255; 90 DynamicBuffer<TCHAR> buffer(length); 91 if (buffer.GetData() == NULL) { 92 return result; 93 } 94 95 for (unsigned int index = 0; index < count; index++) { 96 buffer.Zero(); 97 DWORD status = RegEnumValue(FOpenKey, index, buffer.GetData(), 98 &length, NULL, NULL, NULL, NULL); 99 100 while (status == ERROR_MORE_DATA) { 101 length = length * 2; 102 if (!buffer.Resize(length)) { 103 return result; 104 } 105 status = RegEnumValue(FOpenKey, index, buffer.GetData(), 106 &length, NULL, NULL, NULL, NULL); 107 } 108 109 if (status == ERROR_SUCCESS) { 110 TString value = buffer.GetData(); 111 result.push_back(value); 112 } 113 } 114 } 115 116 return result; 117 } 118 119 TString ReadString(TString Name) { 120 TString result; 121 DWORD length; 122 DWORD dwRet; 123 DynamicBuffer<wchar_t> buffer(0); 124 length = 0; 125 126 dwRet = RegQueryValueEx(FOpenKey, Name.data(), NULL, NULL, NULL, 127 &length); 128 if (dwRet == ERROR_MORE_DATA || dwRet == 0) { 129 if (!buffer.Resize(length + 1)) { 130 return result; 131 } 132 dwRet = RegQueryValueEx(FOpenKey, Name.data(), NULL, NULL, 133 (LPBYTE) buffer.GetData(), &length); 134 result = buffer.GetData(); 135 } 136 137 return result; 138 } 139 }; 140 141 WindowsPlatform::WindowsPlatform(void) : Platform() { 142 FMainThread = ::GetCurrentThreadId(); 143 } 144 145 WindowsPlatform::~WindowsPlatform(void) { 146 } 147 148 TString WindowsPlatform::GetPackageAppDirectory() { 149 return FilePath::IncludeTrailingSeparator( 150 GetPackageRootDirectory()) + _T("app"); 151 } 152 153 TString WindowsPlatform::GetPackageLauncherDirectory() { 154 return GetPackageRootDirectory(); 155 } 156 157 TString WindowsPlatform::GetPackageRuntimeBinDirectory() { 158 return FilePath::IncludeTrailingSeparator(GetPackageRootDirectory()) + _T("runtime\\bin"); 159 } 160 161 TCHAR* WindowsPlatform::ConvertStringToFileSystemString(TCHAR* Source, 162 bool &release) { 163 // Not Implemented. 164 return NULL; 165 } 166 167 TCHAR* WindowsPlatform::ConvertFileSystemStringToString(TCHAR* Source, 168 bool &release) { 169 // Not Implemented. 170 return NULL; 171 } 172 173 TString WindowsPlatform::GetPackageRootDirectory() { 174 TString result; 175 TString filename = GetModuleFileName(); 176 return FilePath::ExtractFilePath(filename); 177 } 178 179 TString WindowsPlatform::GetAppDataDirectory() { 180 TString result; 181 TCHAR path[MAX_PATH]; 182 183 if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, path) == S_OK) { 184 result = path; 185 } 186 187 return result; 188 } 189 190 TString WindowsPlatform::GetAppName() { 191 TString result = GetModuleFileName(); 192 result = FilePath::ExtractFileName(result); 193 result = FilePath::ChangeFileExt(result, _T("")); 194 return result; 195 } 196 197 void WindowsPlatform::ShowMessage(TString title, TString description) { 198 MessageBox(NULL, description.data(), 199 !title.empty() ? title.data() : description.data(), 200 MB_ICONERROR | MB_OK); 201 } 202 203 void WindowsPlatform::ShowMessage(TString description) { 204 TString appname = GetModuleFileName(); 205 appname = FilePath::ExtractFileName(appname); 206 MessageBox(NULL, description.data(), appname.data(), MB_ICONERROR | MB_OK); 207 } 208 209 MessageResponse WindowsPlatform::ShowResponseMessage(TString title, 210 TString description) { 211 MessageResponse result = mrCancel; 212 213 if (::MessageBox(NULL, description.data(), title.data(), MB_OKCANCEL) == 214 IDOK) { 215 result = mrOK; 216 } 217 218 return result; 219 } 220 221 TString WindowsPlatform::GetBundledJavaLibraryFileName(TString RuntimePath) { 222 TString result = FilePath::IncludeTrailingSeparator(RuntimePath) + 223 _T("jre\\bin\\jli.dll"); 224 225 if (FilePath::FileExists(result) == false) { 226 result = FilePath::IncludeTrailingSeparator(RuntimePath) + 227 _T("bin\\jli.dll"); 228 } 229 230 return result; 231 } 232 233 ISectionalPropertyContainer* WindowsPlatform::GetConfigFile(TString FileName) { 234 IniFile *result = new IniFile(); 235 if (result == NULL) { 236 return NULL; 237 } 238 239 result->LoadFromFile(FileName); 240 241 return result; 242 } 243 244 TString WindowsPlatform::GetModuleFileName() { 245 TString result; 246 DynamicBuffer<wchar_t> buffer(MAX_PATH); 247 if (buffer.GetData() == NULL) { 248 return result; 249 } 250 251 ::GetModuleFileName(NULL, buffer.GetData(), 252 static_cast<DWORD> (buffer.GetSize())); 253 254 while (ERROR_INSUFFICIENT_BUFFER == GetLastError()) { 255 if (!buffer.Resize(buffer.GetSize() * 2)) { 256 return result; 257 } 258 ::GetModuleFileName(NULL, buffer.GetData(), 259 static_cast<DWORD> (buffer.GetSize())); 260 } 261 262 result = buffer.GetData(); 263 return result; 264 } 265 266 Module WindowsPlatform::LoadLibrary(TString FileName) { 267 return ::LoadLibrary(FileName.data()); 268 } 269 270 void WindowsPlatform::FreeLibrary(Module AModule) { 271 ::FreeLibrary((HMODULE) AModule); 272 } 273 274 Procedure WindowsPlatform::GetProcAddress(Module AModule, 275 std::string MethodName) { 276 return ::GetProcAddress((HMODULE) AModule, MethodName.c_str()); 277 } 278 279 bool WindowsPlatform::IsMainThread() { 280 bool result = (FMainThread == ::GetCurrentThreadId()); 281 return result; 282 } 283 284 TString WindowsPlatform::GetTempDirectory() { 285 TString result; 286 PWSTR userDir = 0; 287 288 if (SUCCEEDED(SHGetKnownFolderPath( 289 FOLDERID_Profile, 290 0, 291 NULL, 292 &userDir))) { 293 result = userDir; 294 result += WINDOWS_JPACKAGE_TMP_DIR; 295 CoTaskMemFree(userDir); 296 } 297 298 return result; 299 } 300 301 static BOOL CALLBACK enumWindows(HWND winHandle, LPARAM lParam) { 302 DWORD pid = (DWORD) lParam, wPid = 0; 303 GetWindowThreadProcessId(winHandle, &wPid); 304 if (pid == wPid) { 305 SetForegroundWindow(winHandle); 306 return FALSE; 307 } 308 return TRUE; 309 } 310 311 TPlatformNumber WindowsPlatform::GetMemorySize() { 312 SYSTEM_INFO si; 313 GetSystemInfo(&si); 314 size_t result = (size_t) si.lpMaximumApplicationAddress; 315 result = result / 1048576; // Convert from bytes to megabytes. 316 return result; 317 } 318 319 std::vector<TString> FilterList(std::vector<TString> &Items, 320 std::wregex Pattern) { 321 std::vector<TString> result; 322 323 for (std::vector<TString>::iterator it = Items.begin(); 324 it != Items.end(); ++it) { 325 TString item = *it; 326 std::wsmatch match; 327 328 if (std::regex_search(item, match, Pattern)) { 329 result.push_back(item); 330 } 331 } 332 return result; 333 } 334 335 Process* WindowsPlatform::CreateProcess() { 336 return new WindowsProcess(); 337 } 338 339 void WindowsPlatform::InitStreamLocale(wios *stream) { 340 const std::locale empty_locale = std::locale::empty(); 341 const std::locale utf8_locale = 342 std::locale(empty_locale, new std::codecvt_utf8<wchar_t>()); 343 stream->imbue(utf8_locale); 344 } 345 346 void WindowsPlatform::addPlatformDependencies(JavaLibrary *pJavaLibrary) { 347 if (pJavaLibrary == NULL) { 348 return; 349 } 350 351 if (FilePath::FileExists(_T("msvcr100.dll")) == true) { 352 pJavaLibrary->AddDependency(_T("msvcr100.dll")); 353 } 354 355 TString runtimeBin = GetPackageRuntimeBinDirectory(); 356 SetDllDirectory(runtimeBin.c_str()); 357 } 358 359 void Platform::CopyString(char *Destination, 360 size_t NumberOfElements, const char *Source) { 361 strcpy_s(Destination, NumberOfElements, Source); 362 363 if (NumberOfElements > 0) { 364 Destination[NumberOfElements - 1] = '\0'; 365 } 366 } 367 368 void Platform::CopyString(wchar_t *Destination, 369 size_t NumberOfElements, const wchar_t *Source) { 370 wcscpy_s(Destination, NumberOfElements, Source); 371 372 if (NumberOfElements > 0) { 373 Destination[NumberOfElements - 1] = '\0'; 374 } 375 } 376 377 // Owner must free the return value. 378 MultibyteString Platform::WideStringToMultibyteString( 379 const wchar_t* value) { 380 MultibyteString result; 381 size_t count = 0; 382 383 if (value == NULL) { 384 return result; 385 } 386 387 count = WideCharToMultiByte(CP_UTF8, 0, value, -1, NULL, 0, NULL, NULL); 388 389 if (count > 0) { 390 result.data = new char[count + 1]; 391 result.length = WideCharToMultiByte(CP_UTF8, 0, value, -1, 392 result.data, (int)count, NULL, NULL); 393 } 394 395 return result; 396 } 397 398 // Owner must free the return value. 399 WideString Platform::MultibyteStringToWideString(const char* value) { 400 WideString result; 401 size_t count = 0; 402 403 if (value == NULL) { 404 return result; 405 } 406 407 count = MultiByteToWideChar(CP_THREAD_ACP, MB_ERR_INVALID_CHARS, 408 value, -1, NULL, 0); 409 410 if (count > 0) { 411 result.data = new wchar_t[count]; 412 result.length = MultiByteToWideChar(CP_THREAD_ACP, MB_ERR_INVALID_CHARS, 413 value, -1, result.data, (int)count); 414 if (result.length == 0) { 415 delete[] result.data; 416 result.data = NULL; 417 } 418 } 419 420 return result; 421 } 422 423 FileHandle::FileHandle(std::wstring FileName) { 424 FHandle = ::CreateFile(FileName.data(), GENERIC_READ, FILE_SHARE_READ, 425 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 426 } 427 428 FileHandle::~FileHandle() { 429 if (IsValid() == true) { 430 ::CloseHandle(FHandle); 431 } 432 } 433 434 bool FileHandle::IsValid() { 435 return FHandle != INVALID_HANDLE_VALUE; 436 } 437 438 HANDLE FileHandle::GetHandle() { 439 return FHandle; 440 } 441 442 FileMappingHandle::FileMappingHandle(HANDLE FileHandle) { 443 FHandle = ::CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL); 444 } 445 446 bool FileMappingHandle::IsValid() { 447 return FHandle != NULL; 448 } 449 450 FileMappingHandle::~FileMappingHandle() { 451 if (IsValid() == true) { 452 ::CloseHandle(FHandle); 453 } 454 } 455 456 HANDLE FileMappingHandle::GetHandle() { 457 return FHandle; 458 } 459 460 FileData::FileData(HANDLE Handle) { 461 FBaseAddress = ::MapViewOfFile(Handle, FILE_MAP_READ, 0, 0, 0); 462 } 463 464 FileData::~FileData() { 465 if (IsValid() == true) { 466 ::UnmapViewOfFile(FBaseAddress); 467 } 468 } 469 470 bool FileData::IsValid() { 471 return FBaseAddress != NULL; 472 } 473 474 LPVOID FileData::GetBaseAddress() { 475 return FBaseAddress; 476 } 477 478 WindowsLibrary::WindowsLibrary(std::wstring FileName) { 479 FFileName = FileName; 480 } 481 482 std::vector<TString> WindowsLibrary::GetImports() { 483 std::vector<TString> result; 484 FileHandle library(FFileName); 485 486 if (library.IsValid() == true) { 487 FileMappingHandle mapping(library.GetHandle()); 488 489 if (mapping.IsValid() == true) { 490 FileData fileData(mapping.GetHandle()); 491 492 if (fileData.IsValid() == true) { 493 PIMAGE_DOS_HEADER dosHeader = 494 (PIMAGE_DOS_HEADER) fileData.GetBaseAddress(); 495 PIMAGE_FILE_HEADER pImgFileHdr = 496 (PIMAGE_FILE_HEADER) fileData.GetBaseAddress(); 497 if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) { 498 result = DumpPEFile(dosHeader); 499 } 500 } 501 } 502 } 503 504 return result; 505 } 506 507 // Given an RVA, look up the section header that encloses it and return a 508 // pointer to its IMAGE_SECTION_HEADER 509 510 PIMAGE_SECTION_HEADER WindowsLibrary::GetEnclosingSectionHeader(DWORD rva, 511 PIMAGE_NT_HEADERS pNTHeader) { 512 PIMAGE_SECTION_HEADER result = 0; 513 PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader); 514 515 for (unsigned index = 0; index < pNTHeader->FileHeader.NumberOfSections; 516 index++, section++) { 517 // Is the RVA is within this section? 518 if ((rva >= section->VirtualAddress) && 519 (rva < (section->VirtualAddress + section->Misc.VirtualSize))) { 520 result = section; 521 } 522 } 523 524 return result; 525 } 526 527 LPVOID WindowsLibrary::GetPtrFromRVA(DWORD rva, PIMAGE_NT_HEADERS pNTHeader, 528 DWORD imageBase) { 529 LPVOID result = 0; 530 PIMAGE_SECTION_HEADER pSectionHdr = GetEnclosingSectionHeader(rva, 531 pNTHeader); 532 533 if (pSectionHdr != NULL) { 534 INT delta = (INT) ( 535 pSectionHdr->VirtualAddress - pSectionHdr->PointerToRawData); 536 DWORD_PTR dwp = (DWORD_PTR) (imageBase + rva - delta); 537 result = reinterpret_cast<LPVOID> (dwp); // VS2017 - FIXME 538 } 539 540 return result; 541 } 542 543 std::vector<TString> WindowsLibrary::GetImportsSection(DWORD base, 544 PIMAGE_NT_HEADERS pNTHeader) { 545 std::vector<TString> result; 546 547 // Look up where the imports section is located. Normally in 548 // the .idata section, 549 // but not necessarily so. Therefore, grab the RVA from the data dir. 550 DWORD importsStartRVA = pNTHeader->OptionalHeader.DataDirectory[ 551 IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; 552 553 if (importsStartRVA != NULL) { 554 // Get the IMAGE_SECTION_HEADER that contains the imports. This is 555 // usually the .idata section, but doesn't have to be. 556 PIMAGE_SECTION_HEADER pSection = 557 GetEnclosingSectionHeader(importsStartRVA, pNTHeader); 558 559 if (pSection != NULL) { 560 PIMAGE_IMPORT_DESCRIPTOR importDesc = 561 (PIMAGE_IMPORT_DESCRIPTOR) GetPtrFromRVA( 562 importsStartRVA, pNTHeader, base); 563 564 if (importDesc != NULL) { 565 while (true) { 566 // See if we've reached an empty IMAGE_IMPORT_DESCRIPTOR 567 if ((importDesc->TimeDateStamp == 0) && 568 (importDesc->Name == 0)) { 569 break; 570 } 571 572 std::string filename = (char*) GetPtrFromRVA( 573 importDesc->Name, pNTHeader, base); 574 result.push_back(PlatformString(filename)); 575 importDesc++; // advance to next IMAGE_IMPORT_DESCRIPTOR 576 } 577 } 578 } 579 } 580 581 return result; 582 } 583 584 std::vector<TString> WindowsLibrary::DumpPEFile(PIMAGE_DOS_HEADER dosHeader) { 585 std::vector<TString> result; 586 // all of this is VS2017 - FIXME 587 DWORD_PTR dwDosHeaders = reinterpret_cast<DWORD_PTR> (dosHeader); 588 DWORD_PTR dwPIHeaders = dwDosHeaders + (DWORD) (dosHeader->e_lfanew); 589 590 PIMAGE_NT_HEADERS pNTHeader = 591 reinterpret_cast<PIMAGE_NT_HEADERS> (dwPIHeaders); 592 593 // Verify that the e_lfanew field gave us a reasonable 594 // pointer and the PE signature. 595 // TODO: To really fix JDK-8131321 this condition needs to be changed. 596 // There is a matching change 597 // in JavaVirtualMachine.cpp that also needs to be changed. 598 if (pNTHeader->Signature == IMAGE_NT_SIGNATURE) { 599 DWORD base = (DWORD) (dwDosHeaders); 600 result = GetImportsSection(base, pNTHeader); 601 } 602 603 return result; 604 } 605 606 #include <TlHelp32.h> 607 608 WindowsJob::WindowsJob() { 609 FHandle = NULL; 610 } 611 612 WindowsJob::~WindowsJob() { 613 if (FHandle != NULL) { 614 CloseHandle(FHandle); 615 } 616 } 617 618 HANDLE WindowsJob::GetHandle() { 619 if (FHandle == NULL) { 620 FHandle = CreateJobObject(NULL, NULL); // GLOBAL 621 622 if (FHandle == NULL) { 623 ::MessageBox(0, _T("Could not create job object"), 624 _T("TEST"), MB_OK); 625 } else { 626 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0}; 627 628 // Configure all child processes associated with 629 // the job to terminate when the 630 jeli.BasicLimitInformation.LimitFlags = 631 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; 632 if (0 == SetInformationJobObject(FHandle, 633 JobObjectExtendedLimitInformation, &jeli, sizeof (jeli))) { 634 ::MessageBox(0, _T("Could not SetInformationJobObject"), 635 _T("TEST"), MB_OK); 636 } 637 } 638 } 639 640 return FHandle; 641 } 642 643 // Initialize static member of WindowsProcess 644 WindowsJob WindowsProcess::FJob; 645 646 WindowsProcess::WindowsProcess() : Process() { 647 FRunning = false; 648 } 649 650 WindowsProcess::~WindowsProcess() { 651 Terminate(); 652 } 653 654 void WindowsProcess::Cleanup() { 655 CloseHandle(FProcessInfo.hProcess); 656 CloseHandle(FProcessInfo.hThread); 657 } 658 659 bool WindowsProcess::IsRunning() { 660 bool result = false; 661 662 HANDLE handle = ::CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0); 663 if (handle == INVALID_HANDLE_VALUE) { 664 return false; 665 } 666 667 PROCESSENTRY32 process = {0}; 668 process.dwSize = sizeof (process); 669 670 if (::Process32First(handle, &process)) { 671 do { 672 if (process.th32ProcessID == FProcessInfo.dwProcessId) { 673 result = true; 674 break; 675 } 676 } while (::Process32Next(handle, &process)); 677 } 678 679 CloseHandle(handle); 680 681 return result; 682 } 683 684 bool WindowsProcess::Terminate() { 685 bool result = false; 686 687 if (IsRunning() == true && FRunning == true) { 688 FRunning = false; 689 } 690 691 return result; 692 } 693 694 bool WindowsProcess::Execute(const TString Application, 695 const std::vector<TString> Arguments, bool AWait) { 696 bool result = false; 697 698 if (FRunning == false) { 699 FRunning = true; 700 701 STARTUPINFO startupInfo; 702 ZeroMemory(&startupInfo, sizeof (startupInfo)); 703 startupInfo.cb = sizeof (startupInfo); 704 ZeroMemory(&FProcessInfo, sizeof (FProcessInfo)); 705 706 TString command = Application; 707 708 for (std::vector<TString>::const_iterator iterator = Arguments.begin(); 709 iterator != Arguments.end(); iterator++) { 710 command += TString(_T(" ")) + *iterator; 711 } 712 713 if (::CreateProcess(Application.data(), (wchar_t*)command.data(), NULL, 714 NULL, FALSE, 0, NULL, NULL, &startupInfo, &FProcessInfo) 715 == FALSE) { 716 TString message = PlatformString::Format( 717 _T("Error: Unable to create process %s"), 718 Application.data()); 719 throw Exception(message); 720 } else { 721 if (FJob.GetHandle() != NULL) { 722 if (::AssignProcessToJobObject(FJob.GetHandle(), 723 FProcessInfo.hProcess) == 0) { 724 // Failed to assign process to job. It doesn't prevent 725 // anything from continuing so continue. 726 } 727 } 728 729 // Wait until child process exits. 730 if (AWait == true) { 731 Wait(); 732 // Close process and thread handles. 733 Cleanup(); 734 } 735 } 736 } 737 738 return result; 739 } 740 741 bool WindowsProcess::Wait() { 742 bool result = false; 743 744 WaitForSingleObject(FProcessInfo.hProcess, INFINITE); 745 return result; 746 } 747 748 TProcessID WindowsProcess::GetProcessID() { 749 return FProcessInfo.dwProcessId; 750 } 751 752 bool WindowsProcess::ReadOutput() { 753 bool result = false; 754 // TODO implement 755 return result; 756 } 757 758 void WindowsProcess::SetInput(TString Value) { 759 // TODO implement 760 } 761 762 std::list<TString> WindowsProcess::GetOutput() { 763 ReadOutput(); 764 return Process::GetOutput(); 765 }