cmVSSetupHelper.cxx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmVSSetupHelper.h"
  4. #include "cmSystemTools.h"
  5. #include "cmsys/Encoding.hxx"
  6. #include "cmsys/FStream.hxx"
  7. #ifndef VSSetupConstants
  8. # define VSSetupConstants
  9. /* clang-format off */
  10. const IID IID_ISetupConfiguration = {
  11. 0x42843719, 0xDB4C, 0x46C2,
  12. { 0x8E, 0x7C, 0x64, 0xF1, 0x81, 0x6E, 0xFD, 0x5B }
  13. };
  14. const IID IID_ISetupConfiguration2 = {
  15. 0x26AAB78C, 0x4A60, 0x49D6,
  16. { 0xAF, 0x3B, 0x3C, 0x35, 0xBC, 0x93, 0x36, 0x5D }
  17. };
  18. const IID IID_ISetupPackageReference = {
  19. 0xda8d8a16, 0xb2b6, 0x4487,
  20. { 0xa2, 0xf1, 0x59, 0x4c, 0xcc, 0xcd, 0x6b, 0xf5 }
  21. };
  22. const IID IID_ISetupHelper = {
  23. 0x42b21b78, 0x6192, 0x463e,
  24. { 0x87, 0xbf, 0xd5, 0x77, 0x83, 0x8f, 0x1d, 0x5c }
  25. };
  26. const IID IID_IEnumSetupInstances = {
  27. 0x6380BCFF, 0x41D3, 0x4B2E,
  28. { 0x8B, 0x2E, 0xBF, 0x8A, 0x68, 0x10, 0xC8, 0x48 }
  29. };
  30. const IID IID_ISetupInstance2 = {
  31. 0x89143C9A, 0x05AF, 0x49B0,
  32. { 0xB7, 0x17, 0x72, 0xE2, 0x18, 0xA2, 0x18, 0x5C }
  33. };
  34. const IID IID_ISetupInstance = {
  35. 0xB41463C3, 0x8866, 0x43B5,
  36. { 0xBC, 0x33, 0x2B, 0x06, 0x76, 0xF7, 0xF4, 0x2E }
  37. };
  38. const CLSID CLSID_SetupConfiguration = {
  39. 0x177F0C4A, 0x1CD3, 0x4DE7,
  40. { 0xA3, 0x2C, 0x71, 0xDB, 0xBB, 0x9F, 0xA3, 0x6D }
  41. };
  42. /* clang-format on */
  43. #endif
  44. const WCHAR* Win10SDKComponent =
  45. L"Microsoft.VisualStudio.Component.Windows10SDK";
  46. const WCHAR* Win81SDKComponent =
  47. L"Microsoft.VisualStudio.Component.Windows81SDK";
  48. const WCHAR* ComponentType = L"Component";
  49. std::string VSInstanceInfo::GetInstallLocation() const
  50. {
  51. std::string loc = cmsys::Encoding::ToNarrow(this->VSInstallLocation);
  52. cmSystemTools::ConvertToUnixSlashes(loc);
  53. return loc;
  54. }
  55. cmVSSetupAPIHelper::cmVSSetupAPIHelper(unsigned int version)
  56. : Version(version)
  57. , setupConfig(NULL)
  58. , setupConfig2(NULL)
  59. , setupHelper(NULL)
  60. , initializationFailure(false)
  61. {
  62. comInitialized = CoInitializeEx(NULL, 0);
  63. if (SUCCEEDED(comInitialized)) {
  64. Initialize();
  65. } else {
  66. initializationFailure = true;
  67. }
  68. }
  69. cmVSSetupAPIHelper::~cmVSSetupAPIHelper()
  70. {
  71. setupHelper = NULL;
  72. setupConfig2 = NULL;
  73. setupConfig = NULL;
  74. if (SUCCEEDED(comInitialized))
  75. CoUninitialize();
  76. }
  77. bool cmVSSetupAPIHelper::SetVSInstance(std::string const& vsInstallLocation)
  78. {
  79. this->SpecifiedVSInstallLocation = vsInstallLocation;
  80. cmSystemTools::ConvertToUnixSlashes(this->SpecifiedVSInstallLocation);
  81. chosenInstanceInfo = VSInstanceInfo();
  82. return this->EnumerateAndChooseVSInstance();
  83. }
  84. bool cmVSSetupAPIHelper::IsVSInstalled()
  85. {
  86. return this->EnumerateAndChooseVSInstance();
  87. }
  88. bool cmVSSetupAPIHelper::IsWin10SDKInstalled()
  89. {
  90. return (this->EnumerateAndChooseVSInstance() &&
  91. chosenInstanceInfo.IsWin10SDKInstalled);
  92. }
  93. bool cmVSSetupAPIHelper::IsWin81SDKInstalled()
  94. {
  95. return (this->EnumerateAndChooseVSInstance() &&
  96. chosenInstanceInfo.IsWin81SDKInstalled);
  97. }
  98. bool cmVSSetupAPIHelper::CheckInstalledComponent(
  99. SmartCOMPtr<ISetupPackageReference> package, bool& bWin10SDK,
  100. bool& bWin81SDK)
  101. {
  102. bool ret = false;
  103. bWin10SDK = bWin81SDK = false;
  104. SmartBSTR bstrId;
  105. if (FAILED(package->GetId(&bstrId))) {
  106. return ret;
  107. }
  108. SmartBSTR bstrType;
  109. if (FAILED(package->GetType(&bstrType))) {
  110. return ret;
  111. }
  112. std::wstring id = std::wstring(bstrId);
  113. std::wstring type = std::wstring(bstrType);
  114. // Checks for any version of Win10 SDK. The version is appended at the end of
  115. // the
  116. // component name ex: Microsoft.VisualStudio.Component.Windows10SDK.10240
  117. if (id.find(Win10SDKComponent) != std::wstring::npos &&
  118. type.compare(ComponentType) == 0) {
  119. bWin10SDK = true;
  120. ret = true;
  121. }
  122. if (id.compare(Win81SDKComponent) == 0 && type.compare(ComponentType) == 0) {
  123. bWin81SDK = true;
  124. ret = true;
  125. }
  126. return ret;
  127. }
  128. // Gather additional info such as if VCToolset, WinSDKs are installed, location
  129. // of VS and version information.
  130. bool cmVSSetupAPIHelper::GetVSInstanceInfo(
  131. SmartCOMPtr<ISetupInstance2> pInstance, VSInstanceInfo& vsInstanceInfo)
  132. {
  133. if (pInstance == NULL)
  134. return false;
  135. SmartBSTR bstrId;
  136. if (SUCCEEDED(pInstance->GetInstanceId(&bstrId))) {
  137. vsInstanceInfo.InstanceId = std::wstring(bstrId);
  138. } else {
  139. return false;
  140. }
  141. InstanceState state;
  142. if (FAILED(pInstance->GetState(&state))) {
  143. return false;
  144. }
  145. ULONGLONG ullVersion = 0;
  146. SmartBSTR bstrVersion;
  147. if (FAILED(pInstance->GetInstallationVersion(&bstrVersion))) {
  148. return false;
  149. } else {
  150. vsInstanceInfo.Version = std::wstring(bstrVersion);
  151. if (FAILED(setupHelper->ParseVersion(bstrVersion, &ullVersion))) {
  152. vsInstanceInfo.ullVersion = 0;
  153. } else {
  154. vsInstanceInfo.ullVersion = ullVersion;
  155. }
  156. }
  157. // Reboot may have been required before the installation path was created.
  158. SmartBSTR bstrInstallationPath;
  159. if ((eLocal & state) == eLocal) {
  160. if (FAILED(pInstance->GetInstallationPath(&bstrInstallationPath))) {
  161. return false;
  162. } else {
  163. vsInstanceInfo.VSInstallLocation = std::wstring(bstrInstallationPath);
  164. }
  165. }
  166. // Check if a compiler is installed with this instance.
  167. {
  168. std::string const vcRoot = vsInstanceInfo.GetInstallLocation();
  169. std::string vcToolsVersionFile =
  170. vcRoot + "/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt";
  171. std::string vcToolsVersion;
  172. cmsys::ifstream fin(vcToolsVersionFile.c_str());
  173. if (!fin || !cmSystemTools::GetLineFromStream(fin, vcToolsVersion)) {
  174. return false;
  175. }
  176. vcToolsVersion = cmSystemTools::TrimWhitespace(vcToolsVersion);
  177. std::string const vcToolsDir = vcRoot + "/VC/Tools/MSVC/" + vcToolsVersion;
  178. if (!cmSystemTools::FileIsDirectory(vcToolsDir)) {
  179. return false;
  180. }
  181. vsInstanceInfo.VCToolsetVersion = vcToolsVersion;
  182. }
  183. // Reboot may have been required before the product package was registered
  184. // (last).
  185. if ((eRegistered & state) == eRegistered) {
  186. SmartCOMPtr<ISetupPackageReference> product;
  187. if (FAILED(pInstance->GetProduct(&product)) || !product) {
  188. return false;
  189. }
  190. LPSAFEARRAY lpsaPackages;
  191. if (FAILED(pInstance->GetPackages(&lpsaPackages)) ||
  192. lpsaPackages == NULL) {
  193. return false;
  194. }
  195. int lower = lpsaPackages->rgsabound[0].lLbound;
  196. int upper = lpsaPackages->rgsabound[0].cElements + lower;
  197. IUnknown** ppData = (IUnknown**)lpsaPackages->pvData;
  198. for (int i = lower; i < upper; i++) {
  199. SmartCOMPtr<ISetupPackageReference> package = NULL;
  200. if (FAILED(ppData[i]->QueryInterface(IID_ISetupPackageReference,
  201. (void**)&package)) ||
  202. package == NULL)
  203. continue;
  204. bool win10SDKInstalled = false;
  205. bool win81SDkInstalled = false;
  206. bool ret =
  207. CheckInstalledComponent(package, win10SDKInstalled, win81SDkInstalled);
  208. if (ret) {
  209. vsInstanceInfo.IsWin10SDKInstalled |= win10SDKInstalled;
  210. vsInstanceInfo.IsWin81SDKInstalled |= win81SDkInstalled;
  211. }
  212. }
  213. SafeArrayDestroy(lpsaPackages);
  214. }
  215. return true;
  216. }
  217. bool cmVSSetupAPIHelper::GetVSInstanceInfo(std::string& vsInstallLocation)
  218. {
  219. vsInstallLocation.clear();
  220. bool isInstalled = this->EnumerateAndChooseVSInstance();
  221. if (isInstalled) {
  222. vsInstallLocation = chosenInstanceInfo.GetInstallLocation();
  223. }
  224. return isInstalled;
  225. }
  226. bool cmVSSetupAPIHelper::GetVCToolsetVersion(std::string& vsToolsetVersion)
  227. {
  228. vsToolsetVersion.clear();
  229. bool isInstalled = this->EnumerateAndChooseVSInstance();
  230. if (isInstalled) {
  231. vsToolsetVersion = chosenInstanceInfo.VCToolsetVersion;
  232. }
  233. return isInstalled && !vsToolsetVersion.empty();
  234. }
  235. bool cmVSSetupAPIHelper::IsEWDKEnabled()
  236. {
  237. std::string envEnterpriseWDK, envDisableRegistryUse;
  238. cmSystemTools::GetEnv("EnterpriseWDK", envEnterpriseWDK);
  239. cmSystemTools::GetEnv("DisableRegistryUse", envDisableRegistryUse);
  240. if (!cmSystemTools::Strucmp(envEnterpriseWDK.c_str(), "True") &&
  241. !cmSystemTools::Strucmp(envDisableRegistryUse.c_str(), "True")) {
  242. return true;
  243. }
  244. return false;
  245. }
  246. bool cmVSSetupAPIHelper::EnumerateAndChooseVSInstance()
  247. {
  248. bool isVSInstanceExists = false;
  249. if (chosenInstanceInfo.VSInstallLocation.compare(L"") != 0) {
  250. return true;
  251. }
  252. if (this->IsEWDKEnabled()) {
  253. std::string envWindowsSdkDir81, envVSVersion, envVsInstallDir;
  254. cmSystemTools::GetEnv("WindowsSdkDir_81", envWindowsSdkDir81);
  255. cmSystemTools::GetEnv("VisualStudioVersion", envVSVersion);
  256. cmSystemTools::GetEnv("VSINSTALLDIR", envVsInstallDir);
  257. if (envVSVersion.empty() || envVsInstallDir.empty())
  258. return false;
  259. chosenInstanceInfo.VSInstallLocation =
  260. std::wstring(envVsInstallDir.begin(), envVsInstallDir.end());
  261. chosenInstanceInfo.Version =
  262. std::wstring(envVSVersion.begin(), envVSVersion.end());
  263. chosenInstanceInfo.VCToolsetVersion = envVSVersion;
  264. chosenInstanceInfo.ullVersion = std::stoi(envVSVersion);
  265. chosenInstanceInfo.IsWin10SDKInstalled = true;
  266. chosenInstanceInfo.IsWin81SDKInstalled = !envWindowsSdkDir81.empty();
  267. return true;
  268. }
  269. if (initializationFailure || setupConfig == NULL || setupConfig2 == NULL ||
  270. setupHelper == NULL)
  271. return false;
  272. std::string envVSCommonToolsDir;
  273. std::string envVSCommonToolsDirEnvName =
  274. "VS" + std::to_string(this->Version) + "0COMNTOOLS";
  275. if (cmSystemTools::GetEnv(envVSCommonToolsDirEnvName.c_str(),
  276. envVSCommonToolsDir)) {
  277. cmSystemTools::ConvertToUnixSlashes(envVSCommonToolsDir);
  278. }
  279. std::vector<VSInstanceInfo> vecVSInstances;
  280. SmartCOMPtr<IEnumSetupInstances> enumInstances = NULL;
  281. if (FAILED(
  282. setupConfig2->EnumInstances((IEnumSetupInstances**)&enumInstances)) ||
  283. !enumInstances) {
  284. return false;
  285. }
  286. std::wstring const wantVersion = std::to_wstring(this->Version) + L'.';
  287. SmartCOMPtr<ISetupInstance> instance;
  288. while (SUCCEEDED(enumInstances->Next(1, &instance, NULL)) && instance) {
  289. SmartCOMPtr<ISetupInstance2> instance2 = NULL;
  290. if (FAILED(
  291. instance->QueryInterface(IID_ISetupInstance2, (void**)&instance2)) ||
  292. !instance2) {
  293. instance = NULL;
  294. continue;
  295. }
  296. VSInstanceInfo instanceInfo;
  297. bool isInstalled = GetVSInstanceInfo(instance2, instanceInfo);
  298. instance = instance2 = NULL;
  299. if (isInstalled) {
  300. // We are looking for a specific major version.
  301. if (instanceInfo.Version.size() < wantVersion.size() ||
  302. instanceInfo.Version.substr(0, wantVersion.size()) != wantVersion) {
  303. continue;
  304. }
  305. if (!this->SpecifiedVSInstallLocation.empty()) {
  306. // We are looking for a specific instance.
  307. std::string currentVSLocation = instanceInfo.GetInstallLocation();
  308. if (cmSystemTools::ComparePath(currentVSLocation,
  309. this->SpecifiedVSInstallLocation)) {
  310. chosenInstanceInfo = instanceInfo;
  311. return true;
  312. }
  313. } else {
  314. // We are not looking for a specific instance.
  315. // If we've been given a hint then use it.
  316. if (!envVSCommonToolsDir.empty()) {
  317. std::string currentVSLocation = instanceInfo.GetInstallLocation();
  318. currentVSLocation += "/Common7/Tools";
  319. if (cmSystemTools::ComparePath(currentVSLocation,
  320. envVSCommonToolsDir)) {
  321. chosenInstanceInfo = instanceInfo;
  322. return true;
  323. }
  324. }
  325. // Otherwise, add this to the list of candidates.
  326. vecVSInstances.push_back(instanceInfo);
  327. }
  328. }
  329. }
  330. if (vecVSInstances.size() > 0) {
  331. isVSInstanceExists = true;
  332. int index = ChooseVSInstance(vecVSInstances);
  333. chosenInstanceInfo = vecVSInstances[index];
  334. }
  335. return isVSInstanceExists;
  336. }
  337. int cmVSSetupAPIHelper::ChooseVSInstance(
  338. const std::vector<VSInstanceInfo>& vecVSInstances)
  339. {
  340. if (vecVSInstances.size() == 0)
  341. return -1;
  342. if (vecVSInstances.size() == 1)
  343. return 0;
  344. unsigned int chosenIndex = 0;
  345. for (unsigned int i = 1; i < vecVSInstances.size(); i++) {
  346. // If the current has Win10 SDK but not the chosen one, then choose the
  347. // current VS instance
  348. if (!vecVSInstances[chosenIndex].IsWin10SDKInstalled &&
  349. vecVSInstances[i].IsWin10SDKInstalled) {
  350. chosenIndex = i;
  351. continue;
  352. }
  353. // If the chosen one has Win10 SDK but the current one is not, then look at
  354. // the next VS instance even the current
  355. // instance version may be higher
  356. if (vecVSInstances[chosenIndex].IsWin10SDKInstalled &&
  357. !vecVSInstances[i].IsWin10SDKInstalled) {
  358. continue;
  359. }
  360. // If both chosen one and current one doesn't have Win10 SDK but the
  361. // current one has Win8.1 SDK installed,
  362. // then choose the current one
  363. if (!vecVSInstances[chosenIndex].IsWin10SDKInstalled &&
  364. !vecVSInstances[i].IsWin10SDKInstalled &&
  365. !vecVSInstances[chosenIndex].IsWin81SDKInstalled &&
  366. vecVSInstances[i].IsWin81SDKInstalled) {
  367. chosenIndex = i;
  368. continue;
  369. }
  370. // If there is no difference in WinSDKs then look for the highest version
  371. // of installed VS
  372. if ((vecVSInstances[chosenIndex].IsWin10SDKInstalled ==
  373. vecVSInstances[i].IsWin10SDKInstalled) &&
  374. (vecVSInstances[chosenIndex].IsWin81SDKInstalled ==
  375. vecVSInstances[i].IsWin81SDKInstalled) &&
  376. vecVSInstances[chosenIndex].Version < vecVSInstances[i].Version) {
  377. chosenIndex = i;
  378. continue;
  379. }
  380. }
  381. return chosenIndex;
  382. }
  383. bool cmVSSetupAPIHelper::Initialize()
  384. {
  385. if (initializationFailure)
  386. return false;
  387. if (FAILED(comInitialized)) {
  388. initializationFailure = true;
  389. return false;
  390. }
  391. if (FAILED(setupConfig.CoCreateInstance(CLSID_SetupConfiguration, NULL,
  392. IID_ISetupConfiguration,
  393. CLSCTX_INPROC_SERVER)) ||
  394. setupConfig == NULL) {
  395. initializationFailure = true;
  396. return false;
  397. }
  398. if (FAILED(setupConfig.QueryInterface(IID_ISetupConfiguration2,
  399. (void**)&setupConfig2)) ||
  400. setupConfig2 == NULL) {
  401. initializationFailure = true;
  402. return false;
  403. }
  404. if (FAILED(
  405. setupConfig.QueryInterface(IID_ISetupHelper, (void**)&setupHelper)) ||
  406. setupHelper == NULL) {
  407. initializationFailure = true;
  408. return false;
  409. }
  410. initializationFailure = false;
  411. return true;
  412. }