1 /* 2 * Copyright (c) 2014, 2019, 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 void WindowsPlatform::SetCurrentDirectory(TString Value) { 174 _wchdir(Value.data()); 175 } 176 177 TString WindowsPlatform::GetPackageRootDirectory() { 178 TString filename = GetModuleFileName(); 179 return FilePath::ExtractFilePath(filename); 180 } 181 182 TString WindowsPlatform::GetAppDataDirectory() { 183 TString result; 184 TCHAR path[MAX_PATH]; 185 186 if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, path) == S_OK) { 187 result = path; 188 } 189 190 return result; 191 } 192 193 TString WindowsPlatform::GetAppName() { 194 TString result = GetModuleFileName(); 195 result = FilePath::ExtractFileName(result); 196 result = FilePath::ChangeFileExt(result, _T("")); 197 return result; 198 } 199 200 void WindowsPlatform::ShowMessage(TString title, TString description) { 201 MessageBox(NULL, description.data(), 202 !title.empty() ? title.data() : description.data(), 203 MB_ICONERROR | MB_OK); 204 } 205 206 void WindowsPlatform::ShowMessage(TString description) { 207 TString appname = GetModuleFileName(); 208 appname = FilePath::ExtractFileName(appname); 209 MessageBox(NULL, description.data(), appname.data(), MB_ICONERROR | MB_OK); 210 } 211 212 MessageResponse WindowsPlatform::ShowResponseMessage(TString title, 213 TString description) { 214 MessageResponse result = mrCancel; 215 216 if (::MessageBox(NULL, description.data(), title.data(), MB_OKCANCEL) == 217 IDOK) { 218 result = mrOK; 219 } 220 221 return result; 222 } 223 224 TString WindowsPlatform::GetBundledJVMLibraryFileName(TString RuntimePath) { 225 TString result = FilePath::IncludeTrailingSeparator(RuntimePath) + 226 _T("jre\\bin\\jli.dll"); 227 228 if (FilePath::FileExists(result) == false) { 229 result = FilePath::IncludeTrailingSeparator(RuntimePath) + 230 _T("bin\\jli.dll"); 231 } 232 233 return result; 234 } 235 236 ISectionalPropertyContainer* WindowsPlatform::GetConfigFile(TString FileName) { 237 IniFile *result = new IniFile(); 238 if (result == NULL) { 239 return NULL; 240 } 241 242 if (result->LoadFromFile(FileName) == false) { 243 // New property file format was not found, 244 // attempt to load old property file format. 245 Helpers::LoadOldConfigFile(FileName, result); 246 } 247 248 return result; 249 } 250 251 TString WindowsPlatform::GetModuleFileName() { 252 TString result; 253 DynamicBuffer<wchar_t> buffer(MAX_PATH); 254 if (buffer.GetData() == NULL) { 255 return result; 256 } 257 258 ::GetModuleFileName(NULL, buffer.GetData(), 259 static_cast<DWORD> (buffer.GetSize())); 260 261 while (ERROR_INSUFFICIENT_BUFFER == GetLastError()) { 262 if (!buffer.Resize(buffer.GetSize() * 2)) { 263 return result; 264 } 265 ::GetModuleFileName(NULL, buffer.GetData(), 266 static_cast<DWORD> (buffer.GetSize())); 267 } 268 269 result = buffer.GetData(); 270 return result; 271 } 272 273 Module WindowsPlatform::LoadLibrary(TString FileName) { 274 return ::LoadLibrary(FileName.data()); 275 } 276 277 void WindowsPlatform::FreeLibrary(Module AModule) { 278 ::FreeLibrary((HMODULE) AModule); 279 } 280 281 Procedure WindowsPlatform::GetProcAddress(Module AModule, 282 std::string MethodName) { 283 return ::GetProcAddress((HMODULE) AModule, MethodName.c_str()); 284 } 285 286 bool WindowsPlatform::IsMainThread() { 287 bool result = (FMainThread == ::GetCurrentThreadId()); 288 return result; 289 } 290 291 TString WindowsPlatform::GetTempDirectory() { 292 TString result; 293 PWSTR userDir = 0; 294 295 if (SUCCEEDED(SHGetKnownFolderPath( 296 FOLDERID_Profile, 297 0, 298 NULL, 299 &userDir))) { 300 result = userDir; 301 result += WINDOWS_JPACKAGE_TMP_DIR; 302 CoTaskMemFree(userDir); 303 } 304 305 return result; 306 } 307 308 static BOOL CALLBACK enumWindows(HWND winHandle, LPARAM lParam) { 309 DWORD pid = (DWORD) lParam, wPid = 0; 310 GetWindowThreadProcessId(winHandle, &wPid); 311 if (pid == wPid) { 312 SetForegroundWindow(winHandle); 313 return FALSE; 314 } 315 return TRUE; 316 } 317 318 TPlatformNumber WindowsPlatform::GetMemorySize() { 319 SYSTEM_INFO si; 320 GetSystemInfo(&si); 321 size_t result = (size_t) si.lpMaximumApplicationAddress; 322 result = result / 1048576; // Convert from bytes to megabytes. 323 return result; 324 } 325 326 std::vector<TString> FilterList(std::vector<TString> &Items, 327 std::wregex Pattern) { 328 std::vector<TString> result; 329 330 for (std::vector<TString>::iterator it = Items.begin(); 331 it != Items.end(); ++it) { 332 TString item = *it; 333 std::wsmatch match; 334 335 if (std::regex_search(item, match, Pattern)) { 336 result.push_back(item); 337 } 338 } 339 return result; 340 } 341 342 Process* WindowsPlatform::CreateProcess() { 343 return new WindowsProcess(); 344 } 345 346 void WindowsPlatform::InitStreamLocale(wios *stream) { 347 const std::locale empty_locale = std::locale::empty(); 348 const std::locale utf8_locale = 349 std::locale(empty_locale, new std::codecvt_utf8<wchar_t>()); 350 stream->imbue(utf8_locale); 351 } 352 353 void WindowsPlatform::addPlatformDependencies(JavaLibrary *pJavaLibrary) { 354 if (pJavaLibrary == NULL) { 355 return; 356 } 357 358 if (FilePath::FileExists(_T("msvcr100.dll")) == true) { 359 pJavaLibrary->AddDependency(_T("msvcr100.dll")); 360 } 361 362 TString runtimeBin = GetPackageRuntimeBinDirectory(); 363 SetDllDirectory(runtimeBin.c_str()); 364 } 365 366 void Platform::CopyString(char *Destination, 367 size_t NumberOfElements, const char *Source) { 368 strcpy_s(Destination, NumberOfElements, Source); 369 370 if (NumberOfElements > 0) { 371 Destination[NumberOfElements - 1] = '\0'; 372 } 373 } 374 375 void Platform::CopyString(wchar_t *Destination, 376 size_t NumberOfElements, const wchar_t *Source) { 377 wcscpy_s(Destination, NumberOfElements, Source); 378 379 if (NumberOfElements > 0) { 380 Destination[NumberOfElements - 1] = '\0'; 381 } 382 } 383 384 // Owner must free the return value. 385 MultibyteString Platform::WideStringToMultibyteString( 386 const wchar_t* value) { 387 MultibyteString result; 388 size_t count = 0; 389 390 if (value == NULL) { 391 return result; 392 } 393 394 count = WideCharToMultiByte(CP_UTF8, 0, value, -1, NULL, 0, NULL, NULL); 395 396 if (count > 0) { 397 result.data = new char[count + 1]; 398 result.length = WideCharToMultiByte(CP_UTF8, 0, value, -1, 399 result.data, (int)count, NULL, NULL); 400 } 401 402 return result; 403 } 404 405 // Owner must free the return value. 406 WideString Platform::MultibyteStringToWideString(const char* value) { 407 WideString result; 408 size_t count = 0; 409 410 if (value == NULL) { 411 return result; 412 } 413 414 mbstowcs_s(&count, NULL, 0, value, _TRUNCATE); 415 416 if (count > 0) { 417 result.data = new wchar_t[count + 1]; 418 mbstowcs_s(&result.length, result.data, count, value, count); 419 } 420 421 return result; 422 } 423 424 FileHandle::FileHandle(std::wstring FileName) { 425 FHandle = ::CreateFile(FileName.data(), GENERIC_READ, FILE_SHARE_READ, 426 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 427 } 428 429 FileHandle::~FileHandle() { 430 if (IsValid() == true) { 431 ::CloseHandle(FHandle); 432 } 433 } 434 435 bool FileHandle::IsValid() { 436 return FHandle != INVALID_HANDLE_VALUE; 437 } 438 439 HANDLE FileHandle::GetHandle() { 440 return FHandle; 441 } 442 443 FileMappingHandle::FileMappingHandle(HANDLE FileHandle) { 444 FHandle = ::CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL); 445 } 446 447 bool FileMappingHandle::IsValid() { 448 return FHandle != NULL; 449 } 450 451 FileMappingHandle::~FileMappingHandle() { 452 if (IsValid() == true) { 453 ::CloseHandle(FHandle); 454 } 455 } 456 457 HANDLE FileMappingHandle::GetHandle() { 458 return FHandle; 459 } 460 461 FileData::FileData(HANDLE Handle) { 462 FBaseAddress = ::MapViewOfFile(Handle, FILE_MAP_READ, 0, 0, 0); 463 } 464 465 FileData::~FileData() { 466 if (IsValid() == true) { 467 ::UnmapViewOfFile(FBaseAddress); 468 } 469 } 470 471 bool FileData::IsValid() { 472 return FBaseAddress != NULL; 473 } 474 475 LPVOID FileData::GetBaseAddress() { 476 return FBaseAddress; 477 } 478 479 WindowsLibrary::WindowsLibrary(std::wstring FileName) { 480 FFileName = FileName; 481 } 482 483 std::vector<TString> WindowsLibrary::GetImports() { 484 std::vector<TString> result; 485 FileHandle library(FFileName); 486 487 if (library.IsValid() == true) { 488 FileMappingHandle mapping(library.GetHandle()); 489 490 if (mapping.IsValid() == true) { 491 FileData fileData(mapping.GetHandle()); 492 493 if (fileData.IsValid() == true) { 494 PIMAGE_DOS_HEADER dosHeader = 495 (PIMAGE_DOS_HEADER) fileData.GetBaseAddress(); 496 PIMAGE_FILE_HEADER pImgFileHdr = 497 (PIMAGE_FILE_HEADER) fileData.GetBaseAddress(); 498 if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) { 499 result = DumpPEFile(dosHeader); 500 } 501 } 502 } 503 } 504 505 return result; 506 } 507 508 // Given an RVA, look up the section header that encloses it and return a 509 // pointer to its IMAGE_SECTION_HEADER 510 511 PIMAGE_SECTION_HEADER WindowsLibrary::GetEnclosingSectionHeader(DWORD rva, 512 PIMAGE_NT_HEADERS pNTHeader) { 513 PIMAGE_SECTION_HEADER result = 0; 514 PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader); 515 516 for (unsigned index = 0; index < pNTHeader->FileHeader.NumberOfSections; 517 index++, section++) { 518 // Is the RVA is within this section? 519 if ((rva >= section->VirtualAddress) && 520 (rva < (section->VirtualAddress + section->Misc.VirtualSize))) { 521 result = section; 522 } 523 } 524 525 return result; 526 } 527 528 LPVOID WindowsLibrary::GetPtrFromRVA(DWORD rva, PIMAGE_NT_HEADERS pNTHeader, 529 DWORD imageBase) { 530 LPVOID result = 0; 531 PIMAGE_SECTION_HEADER pSectionHdr = GetEnclosingSectionHeader(rva, 532 pNTHeader); 533 534 if (pSectionHdr != NULL) { 535 INT delta = (INT) ( 536 pSectionHdr->VirtualAddress - pSectionHdr->PointerToRawData); 537 DWORD_PTR dwp = (DWORD_PTR) (imageBase + rva - delta); 538 result = reinterpret_cast<LPVOID> (dwp); // VS2017 - FIXME 539 } 540 541 return result; 542 } 543 544 std::vector<TString> WindowsLibrary::GetImportsSection(DWORD base, 545 PIMAGE_NT_HEADERS pNTHeader) { 546 std::vector<TString> result; 547 548 // Look up where the imports section is located. Normally in 549 // the .idata section, 550 // but not necessarily so. Therefore, grab the RVA from the data dir. 551 DWORD importsStartRVA = pNTHeader->OptionalHeader.DataDirectory[ 552 IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; 553 554 if (importsStartRVA != NULL) { 555 // Get the IMAGE_SECTION_HEADER that contains the imports. This is 556 // usually the .idata section, but doesn't have to be. 557 PIMAGE_SECTION_HEADER pSection = 558 GetEnclosingSectionHeader(importsStartRVA, pNTHeader); 559 560 if (pSection != NULL) { 561 PIMAGE_IMPORT_DESCRIPTOR importDesc = 562 (PIMAGE_IMPORT_DESCRIPTOR) GetPtrFromRVA( 563 importsStartRVA, pNTHeader, base); 564 565 if (importDesc != NULL) { 566 while (true) { 567 // See if we've reached an empty IMAGE_IMPORT_DESCRIPTOR 568 if ((importDesc->TimeDateStamp == 0) && 569 (importDesc->Name == 0)) { 570 break; 571 } 572 573 std::string filename = (char*) GetPtrFromRVA( 574 importDesc->Name, pNTHeader, base); 575 result.push_back(PlatformString(filename)); 576 importDesc++; // advance to next IMAGE_IMPORT_DESCRIPTOR 577 } 578 } 579 } 580 } 581 582 return result; 583 } 584 585 std::vector<TString> WindowsLibrary::DumpPEFile(PIMAGE_DOS_HEADER dosHeader) { 586 std::vector<TString> result; 587 // all of this is VS2017 - FIXME 588 DWORD_PTR dwDosHeaders = reinterpret_cast<DWORD_PTR> (dosHeader); 589 DWORD_PTR dwPIHeaders = dwDosHeaders + (DWORD) (dosHeader->e_lfanew); 590 591 PIMAGE_NT_HEADERS pNTHeader = 592 reinterpret_cast<PIMAGE_NT_HEADERS> (dwPIHeaders); 593 594 // Verify that the e_lfanew field gave us a reasonable 595 // pointer and the PE signature. 596 // TODO: To really fix JDK-8131321 this condition needs to be changed. 597 // There is a matching change 598 // in JavaVirtualMachine.cpp that also needs to be changed. 599 if (pNTHeader->Signature == IMAGE_NT_SIGNATURE) { 600 DWORD base = (DWORD) (dwDosHeaders); 601 result = GetImportsSection(base, pNTHeader); 602 } 603 604 return result; 605 } 606 607 #include <TlHelp32.h> 608 609 WindowsJob::WindowsJob() { 610 FHandle = NULL; 611 } 612 613 WindowsJob::~WindowsJob() { 614 if (FHandle != NULL) { 615 CloseHandle(FHandle); 616 } 617 } 618 619 HANDLE WindowsJob::GetHandle() { 620 if (FHandle == NULL) { 621 FHandle = CreateJobObject(NULL, NULL); // GLOBAL 622 623 if (FHandle == NULL) { 624 ::MessageBox(0, _T("Could not create job object"), 625 _T("TEST"), MB_OK); 626 } else { 627 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0}; 628 629 // Configure all child processes associated with 630 // the job to terminate when the 631 jeli.BasicLimitInformation.LimitFlags = 632 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; 633 if (0 == SetInformationJobObject(FHandle, 634 JobObjectExtendedLimitInformation, &jeli, sizeof (jeli))) { 635 ::MessageBox(0, _T("Could not SetInformationJobObject"), 636 _T("TEST"), MB_OK); 637 } 638 } 639 } 640 641 return FHandle; 642 } 643 644 // Initialize static member of WindowsProcess 645 WindowsJob WindowsProcess::FJob; 646 647 WindowsProcess::WindowsProcess() : Process() { 648 FRunning = false; 649 } 650 651 WindowsProcess::~WindowsProcess() { 652 Terminate(); 653 } 654 655 void WindowsProcess::Cleanup() { 656 CloseHandle(FProcessInfo.hProcess); 657 CloseHandle(FProcessInfo.hThread); 658 } 659 660 bool WindowsProcess::IsRunning() { 661 bool result = false; 662 663 HANDLE handle = ::CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0); 664 if (handle == INVALID_HANDLE_VALUE) { 665 return false; 666 } 667 668 PROCESSENTRY32 process = {0}; 669 process.dwSize = sizeof (process); 670 671 if (::Process32First(handle, &process)) { 672 do { 673 if (process.th32ProcessID == FProcessInfo.dwProcessId) { 674 result = true; 675 break; 676 } 677 } while (::Process32Next(handle, &process)); 678 } 679 680 CloseHandle(handle); 681 682 return result; 683 } 684 685 bool WindowsProcess::Terminate() { 686 bool result = false; 687 688 if (IsRunning() == true && FRunning == true) { 689 FRunning = false; 690 } 691 692 return result; 693 } 694 695 bool WindowsProcess::Execute(const TString Application, 696 const std::vector<TString> Arguments, bool AWait) { 697 bool result = false; 698 699 if (FRunning == false) { 700 FRunning = true; 701 702 STARTUPINFO startupInfo; 703 ZeroMemory(&startupInfo, sizeof (startupInfo)); 704 startupInfo.cb = sizeof (startupInfo); 705 ZeroMemory(&FProcessInfo, sizeof (FProcessInfo)); 706 707 TString command = Application; 708 709 for (std::vector<TString>::const_iterator iterator = Arguments.begin(); 710 iterator != Arguments.end(); iterator++) { 711 command += TString(_T(" ")) + *iterator; 712 } 713 714 if (::CreateProcess(Application.data(), (wchar_t*)command.data(), NULL, 715 NULL, FALSE, 0, NULL, NULL, &startupInfo, &FProcessInfo) == 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 }