2
0

BsVSCodeEditor.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. #include "Win32/BsVSCodeEditor.h"
  2. #include <windows.h>
  3. #include <atlbase.h>
  4. #include "BsFileSystem.h"
  5. #include "BsDataStream.h"
  6. // Import EnvDTE
  7. #pragma warning(disable: 4278)
  8. #import "libid:80cc9f66-e7d8-4ddd-85b6-d9e6cd0e93e2" version("8.0") lcid("0") raw_interfaces_only named_guids
  9. #pragma warning(default: 4278)
  10. namespace BansheeEngine
  11. {
  12. /**
  13. * @brief Reads a string value from the specified key in the registry.
  14. *
  15. * @param key Registry key to read from.
  16. * @param name Identifier of the value to read from.
  17. * @param value Output value read from the key.
  18. * @param defaultValue Default value to return if the key or identifier doesn't exist.
  19. */
  20. LONG getRegistryStringValue(HKEY hKey, const WString& name, WString& value, const WString& defaultValue)
  21. {
  22. value = defaultValue;
  23. wchar_t strBuffer[512];
  24. DWORD strBufferSize = sizeof(strBuffer);
  25. ULONG result = RegQueryValueExW(hKey, name.c_str(), 0, nullptr, (LPBYTE)strBuffer, &strBufferSize);
  26. if (result == ERROR_SUCCESS)
  27. value = strBuffer;
  28. return result;
  29. }
  30. /**
  31. * @brief Contains data about a Visual Studio project.
  32. */
  33. struct VSProjectInfo
  34. {
  35. WString GUID;
  36. WString name;
  37. Path path;
  38. };
  39. /**
  40. * @brief Contains various helper classes for interacting with a Visual Studio instance
  41. * running on this machine.
  42. */
  43. class VisualStudio
  44. {
  45. private:
  46. static const String SLN_TEMPLATE; /**< Template text used for a solution file. */
  47. static const String PROJ_ENTRY_TEMPLATE; /**< Template text used for a project entry in a solution file. */
  48. static const String PROJ_PLATFORM_TEMPLATE; /**< Template text used for platform specific information for a project entry in a solution file. */
  49. static const String PROJ_TEMPLATE; /**< Template XML used for a project file. */
  50. static const String REFERENCE_ENTRY_TEMPLATE; /**< Template XML used for a reference to another assembly entry by name. */
  51. static const String REFERENCE_PROJECT_ENTRY_TEMPLATE; /**< Template XML used for a reference to another project entry. */
  52. static const String REFERENCE_PATH_ENTRY_TEMPLATE; /**< Template XML used for a reference to another assembly entry by name and path. */
  53. static const String CODE_ENTRY_TEMPLATE; /**< Template XML used for a single code file entry in a project. */
  54. static const String NON_CODE_ENTRY_TEMPLATE; /**< Template XML used for a single non-code file entry in a project. */
  55. public:
  56. /**
  57. * @brief Scans the running processes to find a running Visual Studio instance with the specified
  58. * version and open solution.
  59. *
  60. * @param clsID Class ID of the specific Visual Studio version we are looking for.
  61. * @param solutionPath Path to the solution the instance needs to have open.
  62. *
  63. * @returns DTE object that may be used to interact with the Visual Studio instance, or null if
  64. * not found.
  65. */
  66. static CComPtr<EnvDTE::_DTE> findRunningInstance(const CLSID& clsID, const Path& solutionPath)
  67. {
  68. CComPtr<IRunningObjectTable> runningObjectTable = nullptr;
  69. if (FAILED(GetRunningObjectTable(0, &runningObjectTable)))
  70. return nullptr;
  71. CComPtr<IEnumMoniker> enumMoniker = nullptr;
  72. if (FAILED(runningObjectTable->EnumRunning(&enumMoniker)))
  73. return nullptr;
  74. CComPtr<IMoniker> dteMoniker = nullptr;
  75. if (FAILED(CreateClassMoniker(clsID, &dteMoniker)))
  76. return nullptr;
  77. CComBSTR bstrSolution(solutionPath.toWString(Path::PathType::Windows).c_str());
  78. CComPtr<IMoniker> moniker;
  79. ULONG count = 0;
  80. while (enumMoniker->Next(1, &moniker, &count) == S_OK)
  81. {
  82. if (moniker->IsEqual(dteMoniker))
  83. {
  84. CComPtr<IUnknown> curObject = nullptr;
  85. HRESULT result = runningObjectTable->GetObject(moniker, &curObject);
  86. moniker = nullptr;
  87. if (result != S_OK)
  88. continue;
  89. CComPtr<EnvDTE::_DTE> dte;
  90. curObject->QueryInterface(__uuidof(EnvDTE::_DTE), (void**)&dte);
  91. if (dte == nullptr)
  92. return nullptr;
  93. CComPtr<EnvDTE::_Solution> solution;
  94. if (FAILED(dte->get_Solution(&solution)))
  95. continue;
  96. CComBSTR fullName;
  97. if (FAILED(solution->get_FullName(&fullName)))
  98. continue;
  99. if (fullName == bstrSolution)
  100. return dte;
  101. }
  102. }
  103. return nullptr;
  104. }
  105. /**
  106. * @brief Opens a new Visual Studio instance of the specified version with the provided solution.
  107. *
  108. * @param clsID Class ID of the specific Visual Studio version to start.
  109. * @param solutionPath Path to the solution the instance needs to open.
  110. */
  111. static CComPtr<EnvDTE::_DTE> openInstance(const CLSID& clsid, const Path& solutionPath)
  112. {
  113. CComPtr<IUnknown> newInstance = nullptr;
  114. if (FAILED(::CoCreateInstance(clsid, nullptr, CLSCTX_LOCAL_SERVER, EnvDTE::IID__DTE, (LPVOID*)&newInstance)))
  115. return nullptr;
  116. CComPtr<EnvDTE::_DTE> dte;
  117. newInstance->QueryInterface(__uuidof(EnvDTE::_DTE), (void**)&dte);
  118. if (dte == nullptr)
  119. return nullptr;
  120. dte->put_UserControl(TRUE);
  121. CComPtr<EnvDTE::_Solution> solution;
  122. if (FAILED(dte->get_Solution(&solution)))
  123. return nullptr;
  124. CComBSTR bstrSolution(solutionPath.toWString(Path::PathType::Windows).c_str());
  125. if (FAILED(solution->Open(bstrSolution)))
  126. return nullptr;
  127. // Wait until VS opens
  128. UINT32 elapsed = 0;
  129. while (elapsed < 10000)
  130. {
  131. EnvDTE::Window* window = nullptr;
  132. if (SUCCEEDED(dte->get_MainWindow(&window)))
  133. return dte;
  134. Sleep(100);
  135. elapsed += 100;
  136. }
  137. return nullptr;
  138. }
  139. /**
  140. * @brief Opens a file on a specific line in a running Visual Studio instance.
  141. *
  142. * @param dte DTE object retrieved from "findRunningInstance" or "openInstance".
  143. * @param filePath Path of the file to open. File should be a part of the VS solution.
  144. * @param line Line on which to focus Visual Studio after the file is open.
  145. */
  146. static bool openFile(CComPtr<EnvDTE::_DTE> dte, const Path& filePath, UINT32 line)
  147. {
  148. // Open file
  149. CComPtr<EnvDTE::ItemOperations> itemOperations;
  150. if (FAILED(dte->get_ItemOperations(&itemOperations)))
  151. return false;
  152. CComBSTR bstrFilePath(filePath.toWString(Path::PathType::Windows).c_str());
  153. CComBSTR bstrKind(EnvDTE::vsViewKindPrimary);
  154. CComPtr<EnvDTE::Window> window = nullptr;
  155. if (FAILED(itemOperations->OpenFile(bstrFilePath, bstrKind, &window)))
  156. return false;
  157. // Scroll to line
  158. CComPtr<EnvDTE::Document> activeDocument;
  159. if (SUCCEEDED(dte->get_ActiveDocument(&activeDocument)))
  160. {
  161. CComPtr<IDispatch> selection;
  162. if (SUCCEEDED(activeDocument->get_Selection(&selection)))
  163. {
  164. CComPtr<EnvDTE::TextSelection> textSelection;
  165. if (SUCCEEDED(selection->QueryInterface(&textSelection)))
  166. {
  167. textSelection->GotoLine(line, TRUE);
  168. }
  169. }
  170. }
  171. // Bring the window in focus
  172. window = nullptr;
  173. if (SUCCEEDED(dte->get_MainWindow(&window)))
  174. {
  175. window->Activate();
  176. HWND hWnd;
  177. window->get_HWnd((LONG*)&hWnd);
  178. SetForegroundWindow(hWnd);
  179. }
  180. return true;
  181. }
  182. /**
  183. * @brief Generates a Visual Studio project GUID from the project name.
  184. */
  185. static String getProjectGUID(const WString& projectName)
  186. {
  187. static const String guidTemplate = "{0}-{1}-{2}-{3}-{4}";
  188. String hash = md5(projectName);
  189. String output = StringUtil::format(guidTemplate, hash.substr(0, 8),
  190. hash.substr(8, 4), hash.substr(12, 4), hash.substr(16, 4), hash.substr(20, 12));
  191. StringUtil::toUpperCase(output);
  192. return output;
  193. }
  194. /**
  195. * @brief Builds the Visual Studio solution text (.sln) for the provided version,
  196. * using the provided solution data.
  197. *
  198. * @param version Visual Studio version for which we're generating the solution file.
  199. * @param data Data containing a list of projects and other information required to
  200. * build the solution text.
  201. *
  202. * @returns Generated text of the solution file.
  203. */
  204. static String writeSolution(VisualStudioVersion version, const CodeSolutionData& data)
  205. {
  206. struct VersionData
  207. {
  208. String formatVersion;
  209. };
  210. Map<VisualStudioVersion, VersionData> versionData =
  211. {
  212. { VisualStudioVersion::VS2008, { "10.00" } },
  213. { VisualStudioVersion::VS2010, { "11.00" } },
  214. { VisualStudioVersion::VS2012, { "12.00" } },
  215. { VisualStudioVersion::VS2013, { "12.00" } },
  216. { VisualStudioVersion::VS2015, { "12.00" } }
  217. };
  218. StringStream projectEntriesStream;
  219. StringStream projectPlatformsStream;
  220. for (auto& project : data.projects)
  221. {
  222. String guid = getProjectGUID(project.name);
  223. String projectName = toString(project.name);
  224. projectEntriesStream << StringUtil::format(PROJ_ENTRY_TEMPLATE, projectName, projectName + ".csproj", guid);
  225. projectPlatformsStream << StringUtil::format(PROJ_PLATFORM_TEMPLATE, guid);
  226. }
  227. String projectEntries = projectEntriesStream.str();
  228. String projectPlatforms = projectPlatformsStream.str();
  229. return StringUtil::format(SLN_TEMPLATE, versionData[version].formatVersion, projectEntries, projectPlatforms);
  230. }
  231. /**
  232. * @brief Builds the Visual Studio project text (.csproj) for the provided version,
  233. * using the provided project data.
  234. *
  235. * @param version Visual Studio version for which we're generating the project file.
  236. * @param projectData Data containing a list of files, references and other information required to
  237. * build the project text.
  238. *
  239. * @returns Generated text of the project file.
  240. */
  241. static String writeProject(VisualStudioVersion version, const CodeProjectData& projectData)
  242. {
  243. struct VersionData
  244. {
  245. String toolsVersion;
  246. };
  247. Map<VisualStudioVersion, VersionData> versionData =
  248. {
  249. { VisualStudioVersion::VS2008, { "3.5" } },
  250. { VisualStudioVersion::VS2010, { "4.0" } },
  251. { VisualStudioVersion::VS2012, { "4.0" } },
  252. { VisualStudioVersion::VS2013, { "12.0" } },
  253. { VisualStudioVersion::VS2015, { "13.0" } }
  254. };
  255. StringStream tempStream;
  256. for (auto& codeEntry : projectData.codeFiles)
  257. tempStream << StringUtil::format(CODE_ENTRY_TEMPLATE, codeEntry.toString());
  258. String codeEntries = tempStream.str();
  259. tempStream.str("");
  260. tempStream.clear();
  261. for (auto& nonCodeEntry : projectData.nonCodeFiles)
  262. tempStream << StringUtil::format(NON_CODE_ENTRY_TEMPLATE, nonCodeEntry.toString());
  263. String nonCodeEntries = tempStream.str();
  264. tempStream.str("");
  265. tempStream.clear();
  266. for (auto& referenceEntry : projectData.assemblyReferences)
  267. {
  268. String referenceName = toString(referenceEntry.name);
  269. if (referenceEntry.path.isEmpty())
  270. tempStream << StringUtil::format(REFERENCE_ENTRY_TEMPLATE, referenceName);
  271. else
  272. tempStream << StringUtil::format(REFERENCE_PATH_ENTRY_TEMPLATE, referenceName, referenceEntry.path.toString());
  273. }
  274. String referenceEntries = tempStream.str();
  275. tempStream.str("");
  276. tempStream.clear();
  277. for (auto& referenceEntry : projectData.projectReferences)
  278. {
  279. String referenceName = toString(referenceEntry.name);
  280. String projectGUID = getProjectGUID(referenceEntry.name);
  281. tempStream << StringUtil::format(REFERENCE_PROJECT_ENTRY_TEMPLATE, referenceName, projectGUID);
  282. }
  283. String projectReferenceEntries = tempStream.str();
  284. tempStream.str("");
  285. tempStream.clear();
  286. tempStream << toString(projectData.defines);
  287. String defines = tempStream.str();
  288. String projectGUID = getProjectGUID(projectData.name);
  289. return StringUtil::format(PROJ_TEMPLATE, versionData[version].toolsVersion, projectGUID,
  290. toString(projectData.name), defines, referenceEntries, projectReferenceEntries, codeEntries, nonCodeEntries);
  291. }
  292. };
  293. const String VisualStudio::SLN_TEMPLATE =
  294. R"(Microsoft Visual Studio Solution File, Format Version {0}
  295. # Visual Studio 2013
  296. VisualStudioVersion = 12.0.30723.0
  297. MinimumVisualStudioVersion = 10.0.40219.1{1}
  298. Global
  299. GlobalSection(SolutionConfigurationPlatforms) = preSolution
  300. Debug|Any CPU = Debug|Any CPU
  301. Release|Any CPU = Release|Any CPU
  302. EndGlobalSection
  303. GlobalSection(ProjectConfigurationPlatforms) = postSolution{2}
  304. EndGlobalSection
  305. GlobalSection(SolutionProperties) = preSolution
  306. HideSolutionNode = FALSE
  307. EndGlobalSection
  308. EndGlobal
  309. )";
  310. const String VisualStudio::PROJ_ENTRY_TEMPLATE =
  311. R"(
  312. Project("\{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC\}") = "{0}", "{1}", "\{{2}\}"
  313. EndProject)";
  314. const String VisualStudio::PROJ_PLATFORM_TEMPLATE =
  315. R"(
  316. \{{0}\}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  317. \{{0}\}.Debug|Any CPU.Build.0 = Debug|Any CPU
  318. \{{0}\}.Release|Any CPU.ActiveCfg = Release|Any CPU
  319. \{{0}\}.Release|Any CPU.Build.0 = Release|Any CPU)";
  320. const String VisualStudio::PROJ_TEMPLATE =
  321. R"literal(<?xml version="1.0" encoding="utf-8"?>
  322. <Project ToolsVersion="{0}" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  323. <Import Project="$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')" />
  324. <PropertyGroup>
  325. <Configuration Condition = " '$(Configuration)' == '' ">Debug</Configuration>
  326. <Platform Condition = " '$(Platform)' == '' ">AnyCPU</Platform>
  327. <ProjectGuid>\{{1}\}</ProjectGuid>
  328. <OutputType>Library</OutputType>
  329. <AppDesignerFolder>Properties</AppDesignerFolder>
  330. <RootNamespace></RootNamespace>
  331. <AssemblyName>{2}</AssemblyName>
  332. <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
  333. <FileAlignment>512</FileAlignment>
  334. <BaseDirectory>Resources</BaseDirectory>
  335. <SchemaVersion>2.0</SchemaVersion>
  336. </PropertyGroup>
  337. <PropertyGroup Condition = " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
  338. <DebugSymbols>true</DebugSymbols>
  339. <DebugType>full</DebugType>
  340. <Optimize>false</Optimize>
  341. <OutputPath>Internal\\Temp\\Assemblies\\Debug\\</OutputPath>
  342. <BaseIntermediateOutputPath>Internal\\Temp\\Assemblies\\</BaseIntermediateOutputPath>
  343. <DefineConstants>DEBUG;TRACE;{3}</DefineConstants>
  344. <ErrorReport>prompt</ErrorReport>
  345. <WarningLevel>4</WarningLevel >
  346. </PropertyGroup>
  347. <PropertyGroup Condition = " '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
  348. <DebugType>pdbonly</DebugType>
  349. <Optimize>true</Optimize>
  350. <OutputPath>Internal\\Temp\\Assemblies\\Release\\</OutputPath>
  351. <BaseIntermediateOutputPath>Internal\\Temp\\Assemblies\\</BaseIntermediateOutputPath>
  352. <DefineConstants>TRACE;{3}</DefineConstants>
  353. <ErrorReport>prompt</ErrorReport>
  354. <WarningLevel>4</WarningLevel>
  355. </PropertyGroup>
  356. <ItemGroup>{4}
  357. </ItemGroup>
  358. <ItemGroup>{5}
  359. </ItemGroup>
  360. <ItemGroup>{6}
  361. </ItemGroup>
  362. <ItemGroup>{7}
  363. </ItemGroup>
  364. <Import Project = "$(MSBuildToolsPath)\\Microsoft.CSharp.targets"/>
  365. </Project>)literal";
  366. const String VisualStudio::REFERENCE_ENTRY_TEMPLATE =
  367. R"(
  368. <Reference Include="{0}"/>)";
  369. const String VisualStudio::REFERENCE_PATH_ENTRY_TEMPLATE =
  370. R"(
  371. <Reference Include="{0}">
  372. <HintPath>{1}</HintPath>
  373. </Reference>)";
  374. const String VisualStudio::REFERENCE_PROJECT_ENTRY_TEMPLATE =
  375. R"(
  376. <ProjectReference Include="{0}.csproj">
  377. <Project>\{{1}\}</Project>
  378. <Name>{0}</Name>
  379. </ProjectReference>)";
  380. const String VisualStudio::CODE_ENTRY_TEMPLATE =
  381. R"(
  382. <Compile Include="{0}"/>)";
  383. const String VisualStudio::NON_CODE_ENTRY_TEMPLATE =
  384. R"(
  385. <None Include="{0}"/>)";
  386. VSCodeEditor::VSCodeEditor(VisualStudioVersion version, const Path& execPath, const WString& CLSID)
  387. :mCLSID(CLSID), mExecPath(execPath), mVersion(version)
  388. {
  389. }
  390. void VSCodeEditor::openFile(const Path& solutionPath, const Path& filePath, UINT32 lineNumber) const
  391. {
  392. CLSID clsID;
  393. if (FAILED(CLSIDFromString(mCLSID.c_str(), &clsID)))
  394. return;
  395. CComPtr<EnvDTE::_DTE> dte = VisualStudio::findRunningInstance(clsID, solutionPath);
  396. if (dte == nullptr)
  397. dte = VisualStudio::openInstance(clsID, solutionPath);
  398. if (dte == nullptr)
  399. return;
  400. VisualStudio::openFile(dte, filePath, lineNumber);
  401. }
  402. void VSCodeEditor::syncSolution(const CodeSolutionData& data, const Path& outputPath) const
  403. {
  404. String solutionString = VisualStudio::writeSolution(mVersion, data);
  405. solutionString = StringUtil::replaceAll(solutionString, "\n", "\r\n");
  406. Path solutionPath = outputPath;
  407. solutionPath.append(data.name + L".sln");
  408. for (auto& project : data.projects)
  409. {
  410. String projectString = VisualStudio::writeProject(mVersion, project);
  411. projectString = StringUtil::replaceAll(projectString, "\n", "\r\n");
  412. Path projectPath = outputPath;
  413. projectPath.append(project.name + L".csproj");
  414. DataStreamPtr projectStream = FileSystem::createAndOpenFile(projectPath);
  415. projectStream->write(projectString.c_str(), projectString.size() * sizeof(String::value_type));
  416. projectStream->close();
  417. }
  418. DataStreamPtr solutionStream = FileSystem::createAndOpenFile(solutionPath);
  419. solutionStream->write(solutionString.c_str(), solutionString.size() * sizeof(String::value_type));
  420. solutionStream->close();
  421. }
  422. VSCodeEditorFactory::VSCodeEditorFactory()
  423. :mAvailableVersions(getAvailableVersions())
  424. {
  425. for (auto& version : mAvailableVersions)
  426. mAvailableEditors.push_back(version.first);
  427. }
  428. Map<CodeEditorType, VSCodeEditorFactory::VSVersionInfo> VSCodeEditorFactory::getAvailableVersions() const
  429. {
  430. #if BS_ARCH_TYPE == BS_ARCHITECTURE_x86_64
  431. bool is64bit = true;
  432. #else
  433. bool is64bit = false;
  434. IsWow64Process(GetCurrentProcess(), &is64bit);
  435. #endif
  436. WString registryKeyRoot;
  437. if (is64bit)
  438. registryKeyRoot = L"SOFTWARE\\Wow6432Node\\Microsoft";
  439. else
  440. registryKeyRoot = L"SOFTWARE\\Microsoft";
  441. struct VersionData
  442. {
  443. CodeEditorType type;
  444. WString registryKey;
  445. WString name;
  446. WString executable;
  447. };
  448. Map<VisualStudioVersion, VersionData> versionToVersionNumber =
  449. {
  450. { VisualStudioVersion::VS2008, { CodeEditorType::VS2008, L"VisualStudio\\9.0", L"Visual Studio 2008", L"devenv.exe" } },
  451. { VisualStudioVersion::VS2010, { CodeEditorType::VS2010, L"VisualStudio\\10.0", L"Visual Studio 2010", L"devenv.exe" } },
  452. { VisualStudioVersion::VS2012, { CodeEditorType::VS2012, L"VisualStudio\\11.0", L"Visual Studio 2012", L"devenv.exe" } },
  453. { VisualStudioVersion::VS2013, { CodeEditorType::VS2013, L"VisualStudio\\12.0", L"Visual Studio 2013", L"devenv.exe" } },
  454. { VisualStudioVersion::VS2015, { CodeEditorType::VS2015, L"VisualStudio\\13.0", L"Visual Studio 2015", L"devenv.exe" } }
  455. };
  456. Map<CodeEditorType, VSVersionInfo> versionInfo;
  457. for(auto version : versionToVersionNumber)
  458. {
  459. WString registryKey = registryKeyRoot + L"\\" + version.second.registryKey;
  460. HKEY regKey;
  461. LONG result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, registryKey.c_str(), 0, KEY_READ, &regKey);
  462. if (result != ERROR_SUCCESS)
  463. continue;
  464. WString installPath;
  465. getRegistryStringValue(regKey, L"InstallDir", installPath, StringUtil::WBLANK);
  466. if (installPath.empty())
  467. continue;
  468. WString clsID;
  469. getRegistryStringValue(regKey, L"ThisVersionDTECLSID", clsID, StringUtil::WBLANK);
  470. VSVersionInfo info;
  471. info.name = version.second.name;
  472. info.execPath = installPath.append(version.second.executable);
  473. info.CLSID = clsID;
  474. info.version = version.first;
  475. versionInfo[version.second.type] = info;
  476. }
  477. // TODO - Also query for VSExpress and VSCommunity (their registry keys are different)
  478. return versionInfo;
  479. }
  480. CodeEditor* VSCodeEditorFactory::create(CodeEditorType type) const
  481. {
  482. auto findIter = mAvailableVersions.find(type);
  483. if (findIter == mAvailableVersions.end())
  484. return nullptr;
  485. // TODO - Also create VSExpress and VSCommunity editors
  486. return bs_new<VSCodeEditor>(findIter->second.version, findIter->second.execPath, findIter->second.CLSID);
  487. }
  488. }