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 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 mbstowcs_s(&count, NULL, 0, value, _TRUNCATE); 408 409 if (count > 0) { 410 result.data = new wchar_t[count + 1]; 411 mbstowcs_s(&result.length, result.data, count, value, count); 412 } 413 414 return result; 415 } 416 417 FileHandle::FileHandle(std::wstring FileName) { 418 FHandle = ::CreateFile(FileName.data(), GENERIC_READ, FILE_SHARE_READ, 419 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 420 } 421 422 FileHandle::~FileHandle() { 423 if (IsValid() == true) { 424 ::CloseHandle(FHandle); 425 } 426 } 427 428 bool FileHandle::IsValid() { 429 return FHandle != INVALID_HANDLE_VALUE; 430 } 431 432 HANDLE FileHandle::GetHandle() { 433 return FHandle; 434 } 435 436 FileMappingHandle::FileMappingHandle(HANDLE FileHandle) { 437 FHandle = ::CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL); 438 } 439 440 bool FileMappingHandle::IsValid() { 441 return FHandle != NULL; 442 } 443 444 FileMappingHandle::~FileMappingHandle() { 445 if (IsValid() == true) { 446 ::CloseHandle(FHandle); 447 } 448 } 449 450 HANDLE FileMappingHandle::GetHandle() { 451 return FHandle; 452 } 453 454 FileData::FileData(HANDLE Handle) { 455 FBaseAddress = ::MapViewOfFile(Handle, FILE_MAP_READ, 0, 0, 0); 456 } 457 458 FileData::~FileData() { 459 if (IsValid() == true) { 460 ::UnmapViewOfFile(FBaseAddress); 461 } 462 } 463 464 bool FileData::IsValid() { 465 return FBaseAddress != NULL; 466 } 467 468 LPVOID FileData::GetBaseAddress() { 469 return FBaseAddress; 470 } 471 472 WindowsLibrary::WindowsLibrary(std::wstring FileName) { 473 FFileName = FileName; 474 } 475 476 std::vector<TString> WindowsLibrary::GetImports() { 477 std::vector<TString> result; 478 FileHandle library(FFileName); 479 480 if (library.IsValid() == true) { 481 FileMappingHandle mapping(library.GetHandle()); 482 483 if (mapping.IsValid() == true) { 484 FileData fileData(mapping.GetHandle()); 485 486 if (fileData.IsValid() == true) { 487 PIMAGE_DOS_HEADER dosHeader = 488 (PIMAGE_DOS_HEADER) fileData.GetBaseAddress(); 489 PIMAGE_FILE_HEADER pImgFileHdr = 490 (PIMAGE_FILE_HEADER) fileData.GetBaseAddress(); 491 if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) { 492 result = DumpPEFile(dosHeader); 493 } 494 } 495 } 496 } 497 498 return result; 499 } 500 501 // Given an RVA, look up the section header that encloses it and return a 502 // pointer to its IMAGE_SECTION_HEADER 503 504 PIMAGE_SECTION_HEADER WindowsLibrary::GetEnclosingSectionHeader(DWORD rva, 505 PIMAGE_NT_HEADERS pNTHeader) { 506 PIMAGE_SECTION_HEADER result = 0; 507 PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader); 508 509 for (unsigned index = 0; index < pNTHeader->FileHeader.NumberOfSections; 510 index++, section++) { 511 // Is the RVA is within this section? 512 if ((rva >= section->VirtualAddress) && 513 (rva < (section->VirtualAddress + section->Misc.VirtualSize))) { 514 result = section; 515 } 516 } 517 518 return result; 519 } 520 521 LPVOID WindowsLibrary::GetPtrFromRVA(DWORD rva, PIMAGE_NT_HEADERS pNTHeader, 522 DWORD imageBase) { 523 LPVOID result = 0; 524 PIMAGE_SECTION_HEADER pSectionHdr = GetEnclosingSectionHeader(rva, 525 pNTHeader); 526 527 if (pSectionHdr != NULL) { 528 INT delta = (INT) ( 529 pSectionHdr->VirtualAddress - pSectionHdr->PointerToRawData); 530 DWORD_PTR dwp = (DWORD_PTR) (imageBase + rva - delta); 531 result = reinterpret_cast<LPVOID> (dwp); // VS2017 - FIXME 532 } 533 534 return result; 535 } 536 537 std::vector<TString> WindowsLibrary::GetImportsSection(DWORD base, 538 PIMAGE_NT_HEADERS pNTHeader) { 539 std::vector<TString> result; 540 541 // Look up where the imports section is located. Normally in 542 // the .idata section, 543 // but not necessarily so. Therefore, grab the RVA from the data dir. 544 DWORD importsStartRVA = pNTHeader->OptionalHeader.DataDirectory[ 545 IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; 546 547 if (importsStartRVA != NULL) { 548 // Get the IMAGE_SECTION_HEADER that contains the imports. This is 549 // usually the .idata section, but doesn't have to be. 550 PIMAGE_SECTION_HEADER pSection = 551 GetEnclosingSectionHeader(importsStartRVA, pNTHeader); 552 553 if (pSection != NULL) { 554 PIMAGE_IMPORT_DESCRIPTOR importDesc = 555 (PIMAGE_IMPORT_DESCRIPTOR) GetPtrFromRVA( 556 importsStartRVA, pNTHeader, base); 557 558 if (importDesc != NULL) { 559 while (true) { 560 // See if we've reached an empty IMAGE_IMPORT_DESCRIPTOR 561 if ((importDesc->TimeDateStamp == 0) && 562 (importDesc->Name == 0)) { 563 break; 564 } 565 566 std::string filename = (char*) GetPtrFromRVA( 567 importDesc->Name, pNTHeader, base); 568 result.push_back(PlatformString(filename)); 569 importDesc++; // advance to next IMAGE_IMPORT_DESCRIPTOR 570 } 571 } 572 } 573 } 574 575 return result; 576 } 577 578 std::vector<TString> WindowsLibrary::DumpPEFile(PIMAGE_DOS_HEADER dosHeader) { 579 std::vector<TString> result; 580 // all of this is VS2017 - FIXME 581 DWORD_PTR dwDosHeaders = reinterpret_cast<DWORD_PTR> (dosHeader); 582 DWORD_PTR dwPIHeaders = dwDosHeaders + (DWORD) (dosHeader->e_lfanew); 583 584 PIMAGE_NT_HEADERS pNTHeader = 585 reinterpret_cast<PIMAGE_NT_HEADERS> (dwPIHeaders); 586 587 // Verify that the e_lfanew field gave us a reasonable 588 // pointer and the PE signature. 589 // TODO: To really fix JDK-8131321 this condition needs to be changed. 590 // There is a matching change 591 // in JavaVirtualMachine.cpp that also needs to be changed. 592 if (pNTHeader->Signature == IMAGE_NT_SIGNATURE) { 593 DWORD base = (DWORD) (dwDosHeaders); 594 result = GetImportsSection(base, pNTHeader); 595 } 596 597 return result; 598 } 599 600 #include <TlHelp32.h> 601 602 WindowsJob::WindowsJob() { 603 FHandle = NULL; 604 } 605 606 WindowsJob::~WindowsJob() { 607 if (FHandle != NULL) { 608 CloseHandle(FHandle); 609 } 610 } 611 612 HANDLE WindowsJob::GetHandle() { 613 if (FHandle == NULL) { 614 FHandle = CreateJobObject(NULL, NULL); // GLOBAL 615 616 if (FHandle == NULL) { 617 ::MessageBox(0, _T("Could not create job object"), 618 _T("TEST"), MB_OK); 619 } else { 620 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0}; 621 622 // Configure all child processes associated with 623 // the job to terminate when the 624 jeli.BasicLimitInformation.LimitFlags = 625 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; 626 if (0 == SetInformationJobObject(FHandle, 627 JobObjectExtendedLimitInformation, &jeli, sizeof (jeli))) { 628 ::MessageBox(0, _T("Could not SetInformationJobObject"), 629 _T("TEST"), MB_OK); 630 } 631 } 632 } 633 634 return FHandle; 635 } 636 637 // Initialize static member of WindowsProcess 638 WindowsJob WindowsProcess::FJob; 639 640 WindowsProcess::WindowsProcess() : Process() { 641 FRunning = false; 642 } 643 644 WindowsProcess::~WindowsProcess() { 645 Terminate(); 646 } 647 648 void WindowsProcess::Cleanup() { 649 CloseHandle(FProcessInfo.hProcess); 650 CloseHandle(FProcessInfo.hThread); 651 } 652 653 bool WindowsProcess::IsRunning() { 654 bool result = false; 655 656 HANDLE handle = ::CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0); 657 if (handle == INVALID_HANDLE_VALUE) { 658 return false; 659 } 660 661 PROCESSENTRY32 process = {0}; 662 process.dwSize = sizeof (process); 663 664 if (::Process32First(handle, &process)) { 665 do { 666 if (process.th32ProcessID == FProcessInfo.dwProcessId) { 667 result = true; 668 break; 669 } 670 } while (::Process32Next(handle, &process)); 671 } 672 673 CloseHandle(handle); 674 675 return result; 676 } 677 678 bool WindowsProcess::Terminate() { 679 bool result = false; 680 681 if (IsRunning() == true && FRunning == true) { 682 FRunning = false; 683 } 684 685 return result; 686 } 687 688 bool WindowsProcess::Execute(const TString Application, 689 const std::vector<TString> Arguments, bool AWait) { 690 bool result = false; 691 692 if (FRunning == false) { 693 FRunning = true; 694 695 STARTUPINFO startupInfo; 696 ZeroMemory(&startupInfo, sizeof (startupInfo)); 697 startupInfo.cb = sizeof (startupInfo); 698 ZeroMemory(&FProcessInfo, sizeof (FProcessInfo)); 699 700 TString command = Application; 701 702 for (std::vector<TString>::const_iterator iterator = Arguments.begin(); 703 iterator != Arguments.end(); iterator++) { 704 command += TString(_T(" ")) + *iterator; 705 } 706 707 if (::CreateProcess(Application.data(), (wchar_t*)command.data(), NULL, 708 NULL, FALSE, 0, NULL, NULL, &startupInfo, &FProcessInfo) 709 == FALSE) { 710 TString message = PlatformString::Format( 711 _T("Error: Unable to create process %s"), 712 Application.data()); 713 throw Exception(message); 714 } else { 715 if (FJob.GetHandle() != NULL) { 716 if (::AssignProcessToJobObject(FJob.GetHandle(), 717 FProcessInfo.hProcess) == 0) { 718 // Failed to assign process to job. It doesn't prevent 719 // anything from continuing so continue. 720 } 721 } 722 723 // Wait until child process exits. 724 if (AWait == true) { 725 Wait(); 726 // Close process and thread handles. 727 Cleanup(); 728 } 729 } 730 } 731 732 return result; 733 } 734 735 bool WindowsProcess::Wait() { 736 bool result = false; 737 738 WaitForSingleObject(FProcessInfo.hProcess, INFINITE); 739 return result; 740 } 741 742 TProcessID WindowsProcess::GetProcessID() { 743 return FProcessInfo.dwProcessId; 744 } 745 746 bool WindowsProcess::ReadOutput() { 747 bool result = false; 748 // TODO implement 749 return result; 750 } 751 752 void WindowsProcess::SetInput(TString Value) { 753 // TODO implement 754 } 755 756 std::list<TString> WindowsProcess::GetOutput() { 757 ReadOutput(); 758 return Process::GetOutput(); 759 }