1 /** 2 * When compiling on Windows with the Microsoft toolchain, try to detect the Visual Studio setup. 3 * 4 * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/link.d, _vsoptions.d) 8 * Documentation: https://dlang.org/phobos/dmd_vsoptions.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/vsoptions.d 10 */ 11 12 module dmd.vsoptions; 13 14 version (Windows): 15 import core.stdc.ctype; 16 import core.stdc.stdlib; 17 import core.stdc.string; 18 import core.sys.windows.winbase; 19 import core.sys.windows.windef; 20 import core.sys.windows.winreg; 21 22 import dmd.env; 23 import dmd.root.file; 24 import dmd.root.filename; 25 import dmd.root.outbuffer; 26 import dmd.root.rmem; 27 28 struct VSOptions 29 { 30 // evaluated once at startup, reflecting the result of vcvarsall.bat 31 // from the current environment or the latest Visual Studio installation 32 const(char)* WindowsSdkDir; 33 const(char)* WindowsSdkVersion; 34 const(char)* UCRTSdkDir; 35 const(char)* UCRTVersion; 36 const(char)* VSInstallDir; 37 const(char)* VCInstallDir; 38 const(char)* VCToolsInstallDir; // used by VS 2017+ 39 40 /** 41 * fill member variables from environment or registry 42 */ 43 void initialize() 44 { 45 detectWindowsSDK(); 46 detectUCRT(); 47 detectVSInstallDir(); 48 detectVCInstallDir(); 49 detectVCToolsInstallDir(); 50 } 51 52 /** 53 * retrieve the name of the default C runtime library 54 * Params: 55 * x64 = target architecture (x86 if false) 56 * Returns: 57 * name of the default C runtime library 58 */ 59 const(char)* defaultRuntimeLibrary(bool x64) 60 { 61 if (VCInstallDir is null) 62 { 63 detectVCInstallDir(); 64 detectVCToolsInstallDir(); 65 } 66 if (getVCLibDir(x64)) 67 return "libcmt"; 68 else 69 return "msvcrt120"; // mingw replacement 70 } 71 72 /** 73 * retrieve options to be passed to the Microsoft linker 74 * Params: 75 * x64 = target architecture (x86 if false) 76 * Returns: 77 * allocated string of options to add to the linker command line 78 */ 79 const(char)* linkOptions(bool x64) 80 { 81 OutBuffer cmdbuf; 82 if (auto vclibdir = getVCLibDir(x64)) 83 { 84 cmdbuf.writestring(" /LIBPATH:\""); 85 cmdbuf.writestring(vclibdir); 86 cmdbuf.writeByte('\"'); 87 88 if (FileName.exists(FileName.combine(vclibdir, "legacy_stdio_definitions.lib"))) 89 { 90 // VS2015 or later use UCRT 91 cmdbuf.writestring(" legacy_stdio_definitions.lib"); 92 if (auto p = getUCRTLibPath(x64)) 93 { 94 cmdbuf.writestring(" /LIBPATH:\""); 95 cmdbuf.writestring(p); 96 cmdbuf.writeByte('\"'); 97 } 98 } 99 } 100 if (auto p = getSDKLibPath(x64)) 101 { 102 cmdbuf.writestring(" /LIBPATH:\""); 103 cmdbuf.writestring(p); 104 cmdbuf.writeByte('\"'); 105 } 106 if (auto p = getenv("DXSDK_DIR")) 107 { 108 // support for old DX SDK installations 109 cmdbuf.writestring(" /LIBPATH:\""); 110 cmdbuf.writestring(p); 111 cmdbuf.writestring(x64 ? `\Lib\x64"` : `\Lib\x86"`); 112 } 113 return cmdbuf.extractChars(); 114 } 115 116 /** 117 * retrieve path to the Microsoft linker executable 118 * also modifies PATH environment variable if necessary to find conditionally loaded DLLs 119 * Params: 120 * x64 = target architecture (x86 if false) 121 * Returns: 122 * absolute path to link.exe, just "link.exe" if not found 123 */ 124 const(char)* linkerPath(bool x64) 125 { 126 const(char)* addpath; 127 if (auto p = getVCBinDir(x64, addpath)) 128 { 129 OutBuffer cmdbuf; 130 cmdbuf.writestring(p); 131 cmdbuf.writestring(r"\link.exe"); 132 if (addpath) 133 { 134 // debug info needs DLLs from $(VSInstallDir)\Common7\IDE for most linker versions 135 // so prepend it too the PATH environment variable 136 const path = getenv("PATH"); 137 const pathlen = strlen(path); 138 const addpathlen = strlen(addpath); 139 140 const length = addpathlen + 1 + pathlen; 141 char* npath = cast(char*)mem.xmalloc(length); 142 memcpy(npath, addpath, addpathlen); 143 npath[addpathlen] = ';'; 144 memcpy(npath + addpathlen + 1, path, pathlen); 145 if (putenvRestorable("PATH", npath[0 .. length])) 146 assert(0); 147 mem.xfree(npath); 148 } 149 return cmdbuf.extractChars(); 150 } 151 152 // try lld-link.exe alongside dmd.exe 153 char[MAX_PATH + 1] dmdpath = void; 154 const len = GetModuleFileNameA(null, dmdpath.ptr, dmdpath.length); 155 if (len <= MAX_PATH) 156 { 157 auto lldpath = FileName.replaceName(dmdpath[0 .. len], "lld-link.exe"); 158 if (FileName.exists(lldpath)) 159 return lldpath.ptr; 160 } 161 162 // search PATH to avoid createProcess preferring "link.exe" from the dmd folder 163 if (auto p = FileName.searchPath(getenv("PATH"), "link.exe"[], false)) 164 return p.ptr; 165 return "link.exe"; 166 } 167 168 private: 169 /** 170 * detect WindowsSdkDir and WindowsSDKVersion from environment or registry 171 */ 172 void detectWindowsSDK() 173 { 174 if (WindowsSdkDir is null) 175 WindowsSdkDir = getenv("WindowsSdkDir"); 176 177 if (WindowsSdkDir is null) 178 { 179 WindowsSdkDir = GetRegistryString(r"Microsoft\Windows Kits\Installed Roots", "KitsRoot10"); 180 if (WindowsSdkDir && !findLatestSDKDir(FileName.combine(WindowsSdkDir, "Include"), r"um\windows.h")) 181 WindowsSdkDir = null; 182 } 183 if (WindowsSdkDir is null) 184 { 185 WindowsSdkDir = GetRegistryString(r"Microsoft\Microsoft SDKs\Windows\v8.1", "InstallationFolder"); 186 if (WindowsSdkDir && !FileName.exists(FileName.combine(WindowsSdkDir, "Lib"))) 187 WindowsSdkDir = null; 188 } 189 if (WindowsSdkDir is null) 190 { 191 WindowsSdkDir = GetRegistryString(r"Microsoft\Microsoft SDKs\Windows\v8.0", "InstallationFolder"); 192 if (WindowsSdkDir && !FileName.exists(FileName.combine(WindowsSdkDir, "Lib"))) 193 WindowsSdkDir = null; 194 } 195 if (WindowsSdkDir is null) 196 { 197 WindowsSdkDir = GetRegistryString(r"Microsoft\Microsoft SDKs\Windows", "CurrentInstallationFolder"); 198 if (WindowsSdkDir && !FileName.exists(FileName.combine(WindowsSdkDir, "Lib"))) 199 WindowsSdkDir = null; 200 } 201 202 if (WindowsSdkVersion is null) 203 WindowsSdkVersion = getenv("WindowsSdkVersion"); 204 205 if (WindowsSdkVersion is null && WindowsSdkDir !is null) 206 { 207 const(char)* rootsDir = FileName.combine(WindowsSdkDir, "Include"); 208 WindowsSdkVersion = findLatestSDKDir(rootsDir, r"um\windows.h"); 209 } 210 } 211 212 /** 213 * detect UCRTSdkDir and UCRTVersion from environment or registry 214 */ 215 void detectUCRT() 216 { 217 if (UCRTSdkDir is null) 218 UCRTSdkDir = getenv("UniversalCRTSdkDir"); 219 220 if (UCRTSdkDir is null) 221 UCRTSdkDir = GetRegistryString(r"Microsoft\Windows Kits\Installed Roots", "KitsRoot10"); 222 223 if (UCRTVersion is null) 224 UCRTVersion = getenv("UCRTVersion"); 225 226 if (UCRTVersion is null && UCRTSdkDir !is null) 227 { 228 const(char)* rootsDir = FileName.combine(UCRTSdkDir, "Lib"); 229 UCRTVersion = findLatestSDKDir(rootsDir, r"ucrt\x86\libucrt.lib"); 230 } 231 } 232 233 /** 234 * detect VSInstallDir from environment or registry 235 */ 236 void detectVSInstallDir() 237 { 238 if (VSInstallDir is null) 239 VSInstallDir = getenv("VSINSTALLDIR"); 240 241 if (VSInstallDir is null) 242 VSInstallDir = detectVSInstallDirViaCOM(); 243 244 if (VSInstallDir is null) 245 VSInstallDir = GetRegistryString(r"Microsoft\VisualStudio\SxS\VS7", "15.0"); // VS2017 246 247 if (VSInstallDir is null) 248 foreach (const(char)* ver; ["14.0".ptr, "12.0", "11.0", "10.0", "9.0"]) 249 { 250 VSInstallDir = GetRegistryString(FileName.combine(r"Microsoft\VisualStudio", ver), "InstallDir"); 251 if (VSInstallDir) 252 break; 253 } 254 } 255 256 /** 257 * detect VCInstallDir from environment or registry 258 */ 259 void detectVCInstallDir() 260 { 261 if (VCInstallDir is null) 262 VCInstallDir = getenv("VCINSTALLDIR"); 263 264 if (VCInstallDir is null) 265 if (VSInstallDir && FileName.exists(FileName.combine(VSInstallDir, "VC"))) 266 VCInstallDir = FileName.combine(VSInstallDir, "VC"); 267 268 // detect from registry (build tools?) 269 if (VCInstallDir is null) 270 foreach (const(char)* ver; ["14.0".ptr, "12.0", "11.0", "10.0", "9.0"]) 271 { 272 auto regPath = FileName.buildPath(r"Microsoft\VisualStudio", ver, r"Setup\VC"); 273 VCInstallDir = GetRegistryString(regPath, "ProductDir"); 274 if (VCInstallDir) 275 break; 276 } 277 } 278 279 /** 280 * detect VCToolsInstallDir from environment or registry (only used by VC 2017) 281 */ 282 void detectVCToolsInstallDir() 283 { 284 if (VCToolsInstallDir is null) 285 VCToolsInstallDir = getenv("VCTOOLSINSTALLDIR"); 286 287 if (VCToolsInstallDir is null && VCInstallDir) 288 { 289 const(char)* defverFile = FileName.combine(VCInstallDir, r"Auxiliary\Build\Microsoft.VCToolsVersion.default.txt"); 290 if (!FileName.exists(defverFile)) // file renamed with VS2019 Preview 2 291 defverFile = FileName.combine(VCInstallDir, r"Auxiliary\Build\Microsoft.VCToolsVersion.v142.default.txt"); 292 if (FileName.exists(defverFile)) 293 { 294 // VS 2017 295 auto readResult = File.read(defverFile); // adds sentinel 0 at end of file 296 if (readResult.success) 297 { 298 auto ver = cast(char*)readResult.buffer.data.ptr; 299 // trim version number 300 while (*ver && isspace(*ver)) 301 ver++; 302 auto p = ver; 303 while (*p == '.' || (*p >= '0' && *p <= '9')) 304 p++; 305 *p = 0; 306 307 if (ver && *ver) 308 VCToolsInstallDir = FileName.buildPath(VCInstallDir, r"Tools\MSVC", ver); 309 } 310 } 311 } 312 } 313 314 /** 315 * get Visual C bin folder 316 * Params: 317 * x64 = target architecture (x86 if false) 318 * addpath = [out] path that needs to be added to the PATH environment variable 319 * Returns: 320 * folder containing the VC executables 321 * 322 * Selects the binary path according to the host and target OS, but verifies 323 * that link.exe exists in that folder and falls back to 32-bit host/target if 324 * missing 325 * Note: differences for the linker binaries are small, they all 326 * allow cross compilation 327 */ 328 const(char)* getVCBinDir(bool x64, out const(char)* addpath) 329 { 330 static const(char)* linkExists(const(char)* p) 331 { 332 auto lp = FileName.combine(p, "link.exe"); 333 return FileName.exists(lp) ? p : null; 334 } 335 336 const bool isHost64 = isWin64Host(); 337 if (VCToolsInstallDir !is null) 338 { 339 if (isHost64) 340 { 341 if (x64) 342 { 343 if (auto p = linkExists(FileName.combine(VCToolsInstallDir, r"bin\HostX64\x64"))) 344 return p; 345 // in case of missing linker, prefer other host binaries over other target architecture 346 } 347 else 348 { 349 if (auto p = linkExists(FileName.combine(VCToolsInstallDir, r"bin\HostX64\x86"))) 350 { 351 addpath = FileName.combine(VCToolsInstallDir, r"bin\HostX64\x64"); 352 return p; 353 } 354 } 355 } 356 if (x64) 357 { 358 if (auto p = linkExists(FileName.combine(VCToolsInstallDir, r"bin\HostX86\x64"))) 359 { 360 addpath = FileName.combine(VCToolsInstallDir, r"bin\HostX86\x86"); 361 return p; 362 } 363 } 364 if (auto p = linkExists(FileName.combine(VCToolsInstallDir, r"bin\HostX86\x86"))) 365 return p; 366 } 367 if (VCInstallDir !is null) 368 { 369 if (isHost64) 370 { 371 if (x64) 372 { 373 if (auto p = linkExists(FileName.combine(VCInstallDir, r"bin\amd64"))) 374 return p; 375 // in case of missing linker, prefer other host binaries over other target architecture 376 } 377 else 378 { 379 if (auto p = linkExists(FileName.combine(VCInstallDir, r"bin\amd64_x86"))) 380 { 381 addpath = FileName.combine(VCInstallDir, r"bin\amd64"); 382 return p; 383 } 384 } 385 } 386 387 if (VSInstallDir) 388 addpath = FileName.combine(VSInstallDir, r"Common7\IDE"); 389 else 390 addpath = FileName.combine(VCInstallDir, r"bin"); 391 392 if (x64) 393 if (auto p = linkExists(FileName.combine(VCInstallDir, r"x86_amd64"))) 394 return p; 395 396 if (auto p = linkExists(FileName.combine(VCInstallDir, r"bin\HostX86\x86"))) 397 return p; 398 } 399 return null; 400 } 401 402 /** 403 * get Visual C Library folder 404 * Params: 405 * x64 = target architecture (x86 if false) 406 * Returns: 407 * folder containing the the VC runtime libraries 408 */ 409 const(char)* getVCLibDir(bool x64) 410 { 411 if (VCToolsInstallDir !is null) 412 return FileName.combine(VCToolsInstallDir, x64 ? r"lib\x64" : r"lib\x86"); 413 if (VCInstallDir !is null) 414 return FileName.combine(VCInstallDir, x64 ? r"lib\amd64" : "lib"); 415 return null; 416 } 417 418 /** 419 * get the path to the universal CRT libraries 420 * Params: 421 * x64 = target architecture (x86 if false) 422 * Returns: 423 * folder containing the universal CRT libraries 424 */ 425 const(char)* getUCRTLibPath(bool x64) 426 { 427 if (UCRTSdkDir && UCRTVersion) 428 return FileName.buildPath(UCRTSdkDir, "Lib", UCRTVersion, x64 ? r"ucrt\x64" : r"ucrt\x86"); 429 return null; 430 } 431 432 /** 433 * get the path to the Windows SDK CRT libraries 434 * Params: 435 * x64 = target architecture (x86 if false) 436 * Returns: 437 * folder containing the Windows SDK libraries 438 */ 439 const(char)* getSDKLibPath(bool x64) 440 { 441 if (WindowsSdkDir) 442 { 443 const(char)* arch = x64 ? "x64" : "x86"; 444 auto sdk = FileName.combine(WindowsSdkDir, "lib"); 445 if (WindowsSdkVersion && 446 FileName.exists(FileName.buildPath(sdk, WindowsSdkVersion, "um", arch, "kernel32.lib"))) // SDK 10.0 447 return FileName.buildPath(sdk, WindowsSdkVersion, "um", arch); 448 else if (FileName.exists(FileName.buildPath(sdk, r"win8\um", arch, "kernel32.lib"))) // SDK 8.0 449 return FileName.buildPath(sdk, r"win8\um", arch); 450 else if (FileName.exists(FileName.buildPath(sdk, r"winv6.3\um", arch, "kernel32.lib"))) // SDK 8.1 451 return FileName.buildPath(sdk, r"winv6.3\um", arch); 452 else if (x64 && FileName.exists(FileName.buildPath(sdk, arch, "kernel32.lib"))) // SDK 7.1 or earlier 453 return FileName.buildPath(sdk, arch); 454 else if (!x64 && FileName.exists(FileName.buildPath(sdk, "kernel32.lib"))) // SDK 7.1 or earlier 455 return sdk; 456 } 457 458 // try mingw fallback relative to phobos library folder that's part of LIB 459 if (auto p = FileName.searchPath(getenv("LIB"), r"mingw\kernel32.lib"[], false)) 460 return FileName.path(p).ptr; 461 462 return null; 463 } 464 465 // iterate through subdirectories named by SDK version in baseDir and return the 466 // one with the largest version that also contains the test file 467 static const(char)* findLatestSDKDir(const(char)* baseDir, const(char)* testfile) 468 { 469 auto allfiles = FileName.combine(baseDir, "*"); 470 WIN32_FIND_DATAA fileinfo; 471 HANDLE h = FindFirstFileA(allfiles, &fileinfo); 472 if (h == INVALID_HANDLE_VALUE) 473 return null; 474 475 char* res = null; 476 do 477 { 478 if (fileinfo.cFileName[0] >= '1' && fileinfo.cFileName[0] <= '9') 479 if (res is null || strcmp(res, fileinfo.cFileName.ptr) < 0) 480 if (FileName.exists(FileName.buildPath(baseDir, fileinfo.cFileName.ptr, testfile))) 481 { 482 const len = strlen(fileinfo.cFileName.ptr) + 1; 483 res = cast(char*) memcpy(mem.xrealloc(res, len), fileinfo.cFileName.ptr, len); 484 } 485 } 486 while(FindNextFileA(h, &fileinfo)); 487 488 if (!FindClose(h)) 489 res = null; 490 return res; 491 } 492 493 pragma(lib, "advapi32.lib"); 494 495 /** 496 * read a string from the 32-bit registry 497 * Params: 498 * softwareKeyPath = path below HKLM\SOFTWARE 499 * valueName = name of the value to read 500 * Returns: 501 * the registry value if it exists and has string type 502 */ 503 const(char)* GetRegistryString(const(char)* softwareKeyPath, const(char)* valueName) 504 { 505 enum x64hive = false; // VS registry entries always in 32-bit hive 506 507 version(Win64) 508 enum prefix = x64hive ? r"SOFTWARE\" : r"SOFTWARE\WOW6432Node\"; 509 else 510 enum prefix = r"SOFTWARE\"; 511 512 char[260] regPath = void; 513 const len = strlen(softwareKeyPath); 514 assert(len + prefix.length < regPath.length); 515 516 memcpy(regPath.ptr, prefix.ptr, prefix.length); 517 memcpy(regPath.ptr + prefix.length, softwareKeyPath, len + 1); 518 519 enum KEY_WOW64_64KEY = 0x000100; // not defined in core.sys.windows.winnt due to restrictive version 520 enum KEY_WOW64_32KEY = 0x000200; 521 HKEY key; 522 LONG lRes = RegOpenKeyExA(HKEY_LOCAL_MACHINE, regPath.ptr, (x64hive ? KEY_WOW64_64KEY : KEY_WOW64_32KEY), KEY_READ, &key); 523 if (FAILED(lRes)) 524 return null; 525 scope(exit) RegCloseKey(key); 526 527 char[260] buf = void; 528 DWORD cnt = buf.length * char.sizeof; 529 DWORD type; 530 int hr = RegQueryValueExA(key, valueName, null, &type, cast(ubyte*) buf.ptr, &cnt); 531 if (hr == 0 && cnt > 0) 532 return buf.dup.ptr; 533 if (hr != ERROR_MORE_DATA || type != REG_SZ) 534 return null; 535 536 scope char[] pbuf = new char[cnt + 1]; 537 RegQueryValueExA(key, valueName, null, &type, cast(ubyte*) pbuf.ptr, &cnt); 538 return pbuf.ptr; 539 } 540 541 /*** 542 * get architecture of host OS 543 */ 544 static bool isWin64Host() 545 { 546 version (Win64) 547 { 548 return true; 549 } 550 else 551 { 552 // running as a 32-bit process on a 64-bit host? 553 alias fnIsWow64Process = extern(Windows) BOOL function(HANDLE, PBOOL); 554 __gshared fnIsWow64Process pIsWow64Process; 555 556 if (!pIsWow64Process) 557 { 558 //IsWow64Process is not available on all supported versions of Windows. 559 pIsWow64Process = cast(fnIsWow64Process) GetProcAddress(GetModuleHandleA("kernel32"), "IsWow64Process"); 560 if (!pIsWow64Process) 561 return false; 562 } 563 BOOL bIsWow64 = FALSE; 564 if (!pIsWow64Process(GetCurrentProcess(), &bIsWow64)) 565 return false; 566 567 return bIsWow64 != 0; 568 } 569 } 570 } 571 572 /////////////////////////////////////////////////////////////////////// 573 // COM interfaces to find VS2017+ installations 574 import core.sys.windows.com; 575 import core.sys.windows.wtypes : BSTR; 576 import core.sys.windows.winnls : WideCharToMultiByte, CP_UTF8; 577 import core.sys.windows.oleauto : SysFreeString; 578 579 pragma(lib, "ole32.lib"); 580 pragma(lib, "oleaut32.lib"); 581 582 interface ISetupInstance : IUnknown 583 { 584 // static const GUID iid = uuid("B41463C3-8866-43B5-BC33-2B0676F7F42E"); 585 static const GUID iid = { 0xB41463C3, 0x8866, 0x43B5, [ 0xBC, 0x33, 0x2B, 0x06, 0x76, 0xF7, 0xF4, 0x2E ] }; 586 587 int GetInstanceId(BSTR* pbstrInstanceId); 588 int GetInstallDate(LPFILETIME pInstallDate); 589 int GetInstallationName(BSTR* pbstrInstallationName); 590 int GetInstallationPath(BSTR* pbstrInstallationPath); 591 int GetInstallationVersion(BSTR* pbstrInstallationVersion); 592 int GetDisplayName(LCID lcid, BSTR* pbstrDisplayName); 593 int GetDescription(LCID lcid, BSTR* pbstrDescription); 594 int ResolvePath(LPCOLESTR pwszRelativePath, BSTR* pbstrAbsolutePath); 595 } 596 597 interface IEnumSetupInstances : IUnknown 598 { 599 // static const GUID iid = uuid("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848"); 600 601 int Next(ULONG celt, ISetupInstance* rgelt, ULONG* pceltFetched); 602 int Skip(ULONG celt); 603 int Reset(); 604 int Clone(IEnumSetupInstances* ppenum); 605 } 606 607 interface ISetupConfiguration : IUnknown 608 { 609 // static const GUID iid = uuid("42843719-DB4C-46C2-8E7C-64F1816EFD5B"); 610 static const GUID iid = { 0x42843719, 0xDB4C, 0x46C2, [ 0x8E, 0x7C, 0x64, 0xF1, 0x81, 0x6E, 0xFD, 0x5B ] }; 611 612 int EnumInstances(IEnumSetupInstances* ppEnumInstances) ; 613 int GetInstanceForCurrentProcess(ISetupInstance* ppInstance); 614 int GetInstanceForPath(LPCWSTR wzPath, ISetupInstance* ppInstance); 615 } 616 617 const GUID iid_SetupConfiguration = { 0x177F0C4A, 0x1CD3, 0x4DE7, [ 0xA3, 0x2C, 0x71, 0xDB, 0xBB, 0x9F, 0xA3, 0x6D ] }; 618 619 const(char)* detectVSInstallDirViaCOM() 620 { 621 CoInitialize(null); 622 scope(exit) CoUninitialize(); 623 624 ISetupConfiguration setup; 625 IEnumSetupInstances instances; 626 ISetupInstance instance; 627 DWORD fetched; 628 629 HRESULT hr = CoCreateInstance(&iid_SetupConfiguration, null, CLSCTX_ALL, &ISetupConfiguration.iid, cast(void**) &setup); 630 if (hr != S_OK || !setup) 631 return null; 632 scope(exit) setup.Release(); 633 634 if (setup.EnumInstances(&instances) != S_OK) 635 return null; 636 scope(exit) instances.Release(); 637 638 while (instances.Next(1, &instance, &fetched) == S_OK && fetched) 639 { 640 BSTR bstrInstallDir; 641 if (instance.GetInstallationPath(&bstrInstallDir) != S_OK) 642 continue; 643 644 char[260] path; 645 int len = WideCharToMultiByte(CP_UTF8, 0, bstrInstallDir, -1, path.ptr, 260, null, null); 646 SysFreeString(bstrInstallDir); 647 648 if (len > 0) 649 return path[0..len].idup.ptr; 650 } 651 return null; 652 }