Browse Source

WIP Visual Studio integration

Marko Pintera 10 years ago
parent
commit
5a9433d806

+ 10 - 6
BansheeEditor/BansheeEditor.vcxproj

@@ -139,7 +139,7 @@
     <Link>
     <Link>
       <SubSystem>NotSet</SubSystem>
       <SubSystem>NotSet</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;BansheeEngine.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;BansheeEngine.lib;Advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>..\lib\x86\$(Configuration);..\Dependencies\lib\x86\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <AdditionalLibraryDirectories>..\lib\x86\$(Configuration);..\Dependencies\lib\x86\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <ImportLibrary>..\lib\x86\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <ImportLibrary>..\lib\x86\$(Configuration)\$(TargetName).lib</ImportLibrary>
     </Link>
     </Link>
@@ -158,7 +158,7 @@
     <Link>
     <Link>
       <SubSystem>Windows</SubSystem>
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;BansheeEngine.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;BansheeEngine.lib;Advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>..\lib\$(Platform)\$(Configuration);..\Dependencies\lib\x64\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <AdditionalLibraryDirectories>..\lib\$(Platform)\$(Configuration);..\Dependencies\lib\x64\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <ShowProgress>NotSet</ShowProgress>
       <ShowProgress>NotSet</ShowProgress>
       <ImportLibrary>..\lib\x64\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <ImportLibrary>..\lib\x64\$(Configuration)\$(TargetName).lib</ImportLibrary>
@@ -185,7 +185,7 @@
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <OptimizeReferences>true</OptimizeReferences>
       <AdditionalLibraryDirectories>..\lib\x86\$(Configuration);..\Dependencies\lib\x86\Release</AdditionalLibraryDirectories>
       <AdditionalLibraryDirectories>..\lib\x86\$(Configuration);..\Dependencies\lib\x86\Release</AdditionalLibraryDirectories>
-      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;BansheeEngine.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;BansheeEngine.lib;Advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <ImportLibrary>..\lib\x86\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <ImportLibrary>..\lib\x86\$(Configuration)\$(TargetName).lib</ImportLibrary>
     </Link>
     </Link>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
@@ -210,7 +210,7 @@
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <OptimizeReferences>true</OptimizeReferences>
       <AdditionalLibraryDirectories>..\lib\x86\$(Configuration);..\Dependencies\lib\x86\DebugRelease</AdditionalLibraryDirectories>
       <AdditionalLibraryDirectories>..\lib\x86\$(Configuration);..\Dependencies\lib\x86\DebugRelease</AdditionalLibraryDirectories>
-      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;BansheeEngine.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;BansheeEngine.lib;Advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <ImportLibrary>..\lib\x86\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <ImportLibrary>..\lib\x86\$(Configuration)\$(TargetName).lib</ImportLibrary>
     </Link>
     </Link>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
@@ -235,7 +235,7 @@
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <OptimizeReferences>true</OptimizeReferences>
       <AdditionalLibraryDirectories>..\lib\$(Platform)\$(Configuration);..\Dependencies\lib\x64\Release</AdditionalLibraryDirectories>
       <AdditionalLibraryDirectories>..\lib\$(Platform)\$(Configuration);..\Dependencies\lib\x64\Release</AdditionalLibraryDirectories>
-      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;BansheeEngine.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;BansheeEngine.lib;Advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <ImportLibrary>..\lib\x64\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <ImportLibrary>..\lib\x64\$(Configuration)\$(TargetName).lib</ImportLibrary>
     </Link>
     </Link>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
@@ -260,7 +260,7 @@
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <OptimizeReferences>true</OptimizeReferences>
       <AdditionalLibraryDirectories>..\lib\$(Platform)\$(Configuration);..\Dependencies\lib\x64\DebugRelease</AdditionalLibraryDirectories>
       <AdditionalLibraryDirectories>..\lib\$(Platform)\$(Configuration);..\Dependencies\lib\x64\DebugRelease</AdditionalLibraryDirectories>
-      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;BansheeEngine.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>BansheeCore.lib;BansheeUtility.lib;BansheeEngine.lib;Advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <ImportLibrary>..\lib\x64\$(Configuration)\$(TargetName).lib</ImportLibrary>
       <ImportLibrary>..\lib\x64\$(Configuration)\$(TargetName).lib</ImportLibrary>
     </Link>
     </Link>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
@@ -285,6 +285,7 @@
     <ClInclude Include="Include\BsEditorWidgetLayout.h" />
     <ClInclude Include="Include\BsEditorWidgetLayout.h" />
     <ClInclude Include="Include\BsEditorWidgetLayoutRTTI.h" />
     <ClInclude Include="Include\BsEditorWidgetLayoutRTTI.h" />
     <ClInclude Include="Include\BsEditorWidgetManager.h" />
     <ClInclude Include="Include\BsEditorWidgetManager.h" />
+    <ClInclude Include="Include\BsCodeEditor.h" />
     <ClInclude Include="Include\BsGizmoManager.h" />
     <ClInclude Include="Include\BsGizmoManager.h" />
     <ClInclude Include="Include\BsGUIColor.h" />
     <ClInclude Include="Include\BsGUIColor.h" />
     <ClInclude Include="Include\BsGUIColorField.h" />
     <ClInclude Include="Include\BsGUIColorField.h" />
@@ -340,6 +341,7 @@
     <ClInclude Include="Include\DbgEditorWidget1.h" />
     <ClInclude Include="Include\DbgEditorWidget1.h" />
     <ClInclude Include="Include\DbgEditorWidget2.h" />
     <ClInclude Include="Include\DbgEditorWidget2.h" />
     <ClInclude Include="Include\BsUndoRedo.h" />
     <ClInclude Include="Include\BsUndoRedo.h" />
+    <ClInclude Include="Include\Win32\BsVSCodeEditor.h" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsCmdRecordSO.cpp" />
     <ClCompile Include="Source\BsCmdRecordSO.cpp" />
@@ -358,6 +360,7 @@
     <ClCompile Include="Source\BsEditorWindow.cpp" />
     <ClCompile Include="Source\BsEditorWindow.cpp" />
     <ClCompile Include="Source\BsEditorWindowBase.cpp" />
     <ClCompile Include="Source\BsEditorWindowBase.cpp" />
     <ClCompile Include="Source\BsEditorWindowManager.cpp" />
     <ClCompile Include="Source\BsEditorWindowManager.cpp" />
+    <ClCompile Include="Source\BsCodeEditor.cpp" />
     <ClCompile Include="Source\BsGizmoManager.cpp" />
     <ClCompile Include="Source\BsGizmoManager.cpp" />
     <ClCompile Include="Source\BsGUIColor.cpp" />
     <ClCompile Include="Source\BsGUIColor.cpp" />
     <ClCompile Include="Source\BsGUIColorField.cpp" />
     <ClCompile Include="Source\BsGUIColorField.cpp" />
@@ -406,6 +409,7 @@
     <ClCompile Include="Source\BsTestTextSprite.cpp" />
     <ClCompile Include="Source\BsTestTextSprite.cpp" />
     <ClCompile Include="Source\DbgEditorWidget1.cpp" />
     <ClCompile Include="Source\DbgEditorWidget1.cpp" />
     <ClCompile Include="Source\DbgEditorWidget2.cpp" />
     <ClCompile Include="Source\DbgEditorWidget2.cpp" />
+    <ClCompile Include="Source\Win32\BsVSCodeEditor.cpp" />
   </ItemGroup>
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   <ImportGroup Label="ExtensionTargets">

+ 18 - 0
BansheeEditor/BansheeEditor.vcxproj.filters

@@ -25,6 +25,12 @@
     <Filter Include="Source Files\Editor\Command">
     <Filter Include="Source Files\Editor\Command">
       <UniqueIdentifier>{fc5eed3b-3a94-4c0b-b462-636e84615f94}</UniqueIdentifier>
       <UniqueIdentifier>{fc5eed3b-3a94-4c0b-b462-636e84615f94}</UniqueIdentifier>
     </Filter>
     </Filter>
+    <Filter Include="Source Files\Editor\Win32">
+      <UniqueIdentifier>{482fa361-f45b-45d4-9d09-05ffb91c39b8}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\Editor\Win32">
+      <UniqueIdentifier>{ee1257c5-4335-401a-938c-adc62b074503}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <Text Include="ReadMe.txt" />
     <Text Include="ReadMe.txt" />
@@ -246,6 +252,12 @@
     <ClInclude Include="Include\BsModalWindow.h">
     <ClInclude Include="Include\BsModalWindow.h">
       <Filter>Header Files\Editor</Filter>
       <Filter>Header Files\Editor</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="Include\BsCodeEditor.h">
+      <Filter>Header Files\Editor</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\Win32\BsVSCodeEditor.h">
+      <Filter>Header Files\Editor\Win32</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsEditorWidgetContainer.cpp">
     <ClCompile Include="Source\BsEditorWidgetContainer.cpp">
@@ -440,5 +452,11 @@
     <ClCompile Include="Source\BsModalWindow.cpp">
     <ClCompile Include="Source\BsModalWindow.cpp">
       <Filter>Source Files\Editor</Filter>
       <Filter>Source Files\Editor</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="Source\BsCodeEditor.cpp">
+      <Filter>Source Files\Editor</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\Win32\BsVSCodeEditor.cpp">
+      <Filter>Source Files\Editor\Win32</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 63 - 0
BansheeEditor/Include/BsCodeEditor.h

@@ -0,0 +1,63 @@
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "BsModule.h"
+
+namespace BansheeEngine
+{
+	class CodeEditor;
+	class CodeEditorFactory;
+
+	struct BS_ED_EXPORT CodeProjectReference
+	{
+		WString name;
+		Path path;
+	};
+
+	struct BS_ED_EXPORT CodeProjectData
+	{
+		WString name;
+		Vector<Path> codeFiles;
+		Vector<Path> nonCodeFiles;
+		Vector<WString> defines;
+		Vector<CodeProjectReference> references;
+	};
+
+	struct BS_ED_EXPORT CodeSolutionData
+	{
+		Vector<CodeProjectData> projects;
+	};
+
+	class BS_ED_EXPORT CodeEditorManager : public Module<CodeEditorManager>
+	{
+	public:
+		CodeEditorManager();
+		~CodeEditorManager();
+
+		const Vector<WString>& getAvailableEditors() const { return mEditors; }
+		void setActive(const WString& editor);
+
+		void openFile(const Path& path, UINT32 lineNumber) const;
+		void syncSolution(const CodeSolutionData& data, const Path& outputPath) const; // TODO - Need some kind of project information like: name, list of files, list of references, possibly defines and other?
+
+	private:
+		CodeEditor* mActiveEditor;
+		Map<WString, CodeEditorFactory*> mFactoryPerEditor;
+		Vector<WString> mEditors;
+		Vector<CodeEditorFactory*> mFactories;
+	};
+
+	class BS_ED_EXPORT CodeEditor
+	{
+	public:
+		virtual void openFile(const Path& solutionPath, const Path& path, UINT32 lineNumber) const = 0;
+		virtual void syncSolution(const CodeSolutionData& data, const Path& outputPath) const = 0;
+	};
+
+	class BS_ED_EXPORT CodeEditorFactory
+	{
+	public:
+		virtual const Vector<WString>& getAvailableEditors() const = 0;
+		virtual CodeEditor* create(const WString& editor) const = 0;
+	};
+}

+ 43 - 0
BansheeEditor/Include/Win32/BsVSCodeEditor.h

@@ -0,0 +1,43 @@
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "BsCodeEditor.h"
+#include "BsPath.h"
+
+namespace BansheeEngine
+{
+	class BS_ED_EXPORT VSCodeEditor : public CodeEditor
+	{
+	public:
+		VSCodeEditor(const Path& execPath, const WString& CLSID);
+
+		void openFile(const Path& solutionPath, const Path& filePath, UINT32 lineNumber) const override;
+		void syncSolution(const CodeSolutionData& data, const Path& outputPath) const override;
+
+	private:
+		Path mExecPath;
+		WString mCLSID;
+	};
+
+	class BS_ED_EXPORT VSCodeEditorFactory : public CodeEditorFactory
+	{
+	public:
+		VSCodeEditorFactory();
+
+		const Vector<WString>& getAvailableEditors() const override { return mAvailableEditors; }
+		CodeEditor* create(const WString& editor) const override;
+
+	private:
+		struct VSVersionInfo
+		{
+			WString name;
+			Path execPath;
+			WString CLSID;
+		};
+
+		Map<WString, VSVersionInfo> getAvailableVersions() const;
+
+		Map<WString, VSVersionInfo> mAvailableVersions;
+		Vector<WString> mAvailableEditors;
+	};
+}

+ 64 - 0
BansheeEditor/Source/BsCodeEditor.cpp

@@ -0,0 +1,64 @@
+#include "BsCodeEditor.h"
+
+#if BS_PLATFORM == BS_PLATFORM_WIN32
+#include "Win32/BsVSCodeEditor.h"
+#else
+static_assert("Make sure to add implementations for other platforms.");
+#endif
+
+namespace BansheeEngine
+{
+	CodeEditorManager::CodeEditorManager()
+		:mActiveEditor(nullptr)
+	{
+#if BS_PLATFORM == BS_PLATFORM_WIN32
+		VSCodeEditorFactory* vsCodeEditorFactory = bs_new<VSCodeEditorFactory>();
+		Vector<WString> vsEditors = vsCodeEditorFactory->getAvailableEditors();
+		for(auto& editor : vsEditors)
+		{
+			mFactoryPerEditor[editor] = vsCodeEditorFactory;
+			mEditors.push_back(editor);
+		}
+
+		mFactories.push_back(vsCodeEditorFactory);
+#else
+		static_assert("Make sure to add implementations for other platforms.");
+#endif
+	}
+
+	CodeEditorManager::~CodeEditorManager()
+	{
+		for (auto& factory : mFactories)
+			bs_delete(factory);
+
+		if (mActiveEditor != nullptr)
+			bs_delete(mActiveEditor);
+	}
+
+	void CodeEditorManager::setActive(const WString& editor)
+	{
+		if (mActiveEditor != nullptr)
+		{
+			bs_delete(mActiveEditor);
+			mActiveEditor = nullptr;
+		}
+
+		auto findIter = mFactoryPerEditor.find(editor);
+		if (findIter == mFactoryPerEditor.end())
+			return;
+
+		mActiveEditor = findIter->second->create(editor);
+	}
+
+	void CodeEditorManager::openFile(const Path& path, UINT32 lineNumber) const
+	{
+		if (mActiveEditor != nullptr)
+			mActiveEditor->openFile(path, lineNumber);
+	}
+
+	void CodeEditorManager::syncSolution(const CodeSolutionData& data, const Path& outputPath) const
+	{
+		if (mActiveEditor != nullptr)
+			mActiveEditor->syncSolution(data, outputPath);
+	}
+}

+ 3 - 0
BansheeEditor/Source/BsEditorApplication.cpp

@@ -13,6 +13,7 @@
 #include "BsScenePicking.h"
 #include "BsScenePicking.h"
 #include "BsSelection.h"
 #include "BsSelection.h"
 #include "BsGizmoManager.h"
 #include "BsGizmoManager.h"
+#include "BsCodeEditor.h"
 
 
 // DEBUG ONLY
 // DEBUG ONLY
 #include "DbgEditorWidget1.h"
 #include "DbgEditorWidget1.h"
@@ -91,10 +92,12 @@ namespace BansheeEngine
 		ScenePicking::startUp();
 		ScenePicking::startUp();
 		Selection::startUp();
 		Selection::startUp();
 		GizmoManager::startUp();
 		GizmoManager::startUp();
+		CodeEditorManager::startUp();
 	}
 	}
 
 
 	EditorApplication::~EditorApplication()
 	EditorApplication::~EditorApplication()
 	{
 	{
+		CodeEditorManager::shutDown();
 		GizmoManager::shutDown();
 		GizmoManager::shutDown();
 		Selection::shutDown();
 		Selection::shutDown();
 		ScenePicking::shutDown();
 		ScenePicking::shutDown();

+ 283 - 0
BansheeEditor/Source/Win32/BsVSCodeEditor.cpp

@@ -0,0 +1,283 @@
+#include "Win32/BsVSCodeEditor.h"
+#include <windows.h>
+#include <atlbase.h>
+
+// Import EnvDTE
+#import "libid:80cc9f66-e7d8-4ddd-85b6-d9e6cd0e93e2" version("8.0") lcid("0") raw_interfaces_only named_guids
+
+namespace BansheeEngine
+{
+	enum class VisualStudioVersion
+	{
+		VS2008,
+		VS2010,
+		VS2012,
+		VS2013,
+		VS2015
+	};
+
+	LONG getRegistryStringValue(HKEY hKey, const WString& name, WString& value, const WString& defaultValue)
+	{
+		value = defaultValue;
+
+		wchar_t strBuffer[512];
+		DWORD strBufferSize = sizeof(strBuffer);
+		ULONG result = RegQueryValueExW(hKey, name.c_str(), 0, nullptr, (LPBYTE)strBuffer, &strBufferSize);
+		if (result == ERROR_SUCCESS)
+			value = strBuffer;
+
+		return result;
+	}
+
+	struct VSProjectInfo
+	{
+		WString GUID;
+		WString name;
+		Path path;
+	}
+
+	class VisualStudio
+	{
+	public:
+		static CComPtr<EnvDTE::_DTE> findRunningInstance(const CLSID& clsID, const Path& solutionPath)
+		{
+			CComPtr<IRunningObjectTable> runningObjectTable = nullptr;
+			if (FAILED(GetRunningObjectTable(0, &runningObjectTable)))
+				return nullptr;
+
+			CComPtr<IEnumMoniker> enumMoniker = nullptr;
+			if (FAILED(runningObjectTable->EnumRunning(&enumMoniker)))
+				return nullptr;
+
+			CComPtr<IMoniker> dteMoniker = nullptr;
+			if (FAILED(CreateClassMoniker(clsID, &dteMoniker)))
+				return nullptr;
+
+			CComBSTR bstrSolution(solutionPath.toWString(Path::PathType::Windows).c_str());
+			CComPtr<IMoniker> moniker;
+			ULONG count = 0;
+			while (enumMoniker->Next(1, &moniker, &count) == S_OK)
+			{
+				if (moniker->IsEqual(dteMoniker))
+				{
+					CComPtr<IUnknown> curObject = nullptr;
+					HRESULT result = runningObjectTable->GetObject(moniker, &curObject);
+					moniker = nullptr;
+
+					if (result != S_OK)
+						continue;
+
+					CComPtr<EnvDTE::_DTE> dte = curObject;
+					if (dte == nullptr)
+						return nullptr;
+
+					CComPtr<EnvDTE::_Solution> solution;
+					if (FAILED(dte->get_Solution(&solution)))
+						continue;
+
+					CComBSTR fullName;
+					if (FAILED(solution->get_FullName(&fullName)))
+						continue;
+
+					if (fullName == bstrSolution)
+						return dte;
+				}
+			}
+
+			return nullptr;
+		}
+
+		static CComPtr<EnvDTE::_DTE> openInstance(const CLSID& clsid, const Path& solutionPath)
+		{
+			CComPtr<IUnknown> newInstance = nullptr;
+			if (FAILED(::CoCreateInstance(clsid, nullptr, CLSCTX_LOCAL_SERVER, EnvDTE::IID__DTE, (LPVOID*)&newInstance)))
+				return nullptr;
+
+			CComPtr<EnvDTE::_DTE> dte = newInstance;
+			if (dte == nullptr)
+				return nullptr;
+
+			dte->put_UserControl(TRUE);
+
+			CComPtr<EnvDTE::_Solution> solution;
+			if (FAILED(dte->get_Solution(&solution)))
+				return nullptr;
+
+			CComBSTR bstrSolution(solutionPath.toWString(Path::PathType::Windows).c_str());
+			if (FAILED(solution->Open(bstrSolution)))
+				return nullptr;
+
+			// Wait until VS opens
+			UINT32 elapsed = 0;
+			while (elapsed < 10000)
+			{
+				EnvDTE::Window* window = nullptr;
+				if (SUCCEEDED(dte->get_MainWindow(&window)))
+					return dte;
+
+				Sleep(100);
+				elapsed += 100;
+			}
+
+			return nullptr;
+		}
+
+		static bool openFile(CComPtr<EnvDTE::_DTE> dte, const Path& filePath, UINT32 line)
+		{
+			// Open file
+			CComPtr<EnvDTE::ItemOperations> itemOperations;
+			if (FAILED(dte->get_ItemOperations(&itemOperations)))
+				return false;
+
+			CComBSTR bstrFilePath(filePath.toWString(Path::PathType::Windows).c_str());
+			CComBSTR bstrKind(EnvDTE::vsViewKindPrimary);
+			CComPtr<EnvDTE::Window> window = nullptr;
+			if (FAILED(itemOperations->OpenFile(bstrFilePath, bstrKind, &window)))
+				return false;
+
+			// Scroll to line
+			CComPtr<EnvDTE::Document> activeDocument;
+			if (SUCCEEDED(dte->get_ActiveDocument(&activeDocument)))
+			{
+				CComPtr<IDispatch> selection;
+				if (SUCCEEDED(activeDocument->get_Selection(&selection)))
+				{
+					CComPtr<EnvDTE::TextSelection> textSelection;
+					if (SUCCEEDED(selection->QueryInterface(&textSelection)))
+					{
+						textSelection->GotoLine(line, TRUE);
+					}
+				}
+			}
+
+			// Bring the window in focus
+			window = nullptr;
+			if (SUCCEEDED(dte->get_MainWindow(&window)))
+			{
+				window->Activate();
+
+				HWND hWnd;
+				window->get_HWnd((LONG*)&hWnd);
+				SetForegroundWindow(hWnd);
+			}
+
+			return true;
+		}
+
+		static WString writeSolution(VisualStudioVersion version, const WString& solutionGUID, const Vector<VSProjectInfo>& projects)
+		{
+			WStringStream stream;
+
+
+			const WString header;
+			
+		}
+	};
+
+	VSCodeEditor::VSCodeEditor(const Path& execPath, const WString& CLSID)
+		:mCLSID(CLSID), mExecPath(execPath)
+	{
+		
+	}
+
+	void VSCodeEditor::openFile(const Path& solutionPath, const Path& filePath, UINT32 lineNumber) const
+	{
+		CLSID clsID;
+		if (FAILED(CLSIDFromString(mCLSID.toWString().c_str(), &clsID)))
+			return;
+
+		CComPtr<EnvDTE::_DTE> dte = VisualStudio::findRunningInstance(clsID, solutionPath);
+		if (dte == nullptr)
+			dte = VisualStudio::openInstance(clsID, solutionPath);
+
+		if (dte == nullptr)
+			return;
+
+		VisualStudio::openFile(dte, filePath, lineNumber);
+	}
+
+	void VSCodeEditor::syncSolution(const CodeSolutionData& data, const Path& outputPath) const
+	{
+		// TODO
+	}
+
+	VSCodeEditorFactory::VSCodeEditorFactory()
+		:mAvailableVersions(getAvailableVersions())
+	{ 
+		for (auto& version : mAvailableVersions)
+			mAvailableEditors.push_back(version.first);
+	}
+
+	Map<WString, VSCodeEditorFactory::VSVersionInfo> VSCodeEditorFactory::getAvailableVersions() const
+	{
+#if BS_ARCH_TYPE == BS_ARCHITECTURE_x86_64
+		bool is64bit = true;
+#else
+		bool is64bit = false;
+		IsWow64Process(GetCurrentProcess(), &is64bit);
+#endif
+
+		WString registryKeyRoot;
+		if (is64bit)
+			registryKeyRoot = L"SOFTWARE\\Microsoft";
+		else
+			registryKeyRoot = L"SOFTWARE\\Wow6432Node\\Microsoft";
+
+		struct VersionData
+		{
+			WString registryKey;
+			WString name;
+			WString executable;
+		};
+
+		Map<VisualStudioVersion, VersionData> versionToVersionNumber =
+		{ 
+			{ VisualStudioVersion::VS2008, { L"VisualStudio\\9.0", L"Visual Studio 2008", L"devenv.exe" } },
+			{ VisualStudioVersion::VS2010, { L"VisualStudio\\10.0", L"Visual Studio 2010", L"devenv.exe" } },
+			{ VisualStudioVersion::VS2012, { L"VisualStudio\\11.0", L"Visual Studio 2012", L"devenv.exe" } },
+			{ VisualStudioVersion::VS2013, { L"VisualStudio\\12.0", L"Visual Studio 2013", L"devenv.exe" } },
+			{ VisualStudioVersion::VS2015, { L"VisualStudio\\13.0", L"Visual Studio 2015", L"devenv.exe" } }
+		};
+
+		Map<WString, VSVersionInfo> versionInfo;
+		for(auto version : versionToVersionNumber)
+		{
+			WString registryKey = registryKeyRoot + L"\\" + version.second.registryKey;
+
+			HKEY regKey;
+			LONG result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, registryKeyRoot.c_str(), 0, KEY_READ, &regKey);
+			if (result != ERROR_SUCCESS)
+				continue;
+
+			WString installPath;
+			getRegistryStringValue(regKey, L"InstallDir", installPath, StringUtil::WBLANK);
+			if (installPath.empty())
+				continue;
+
+			WString clsID;
+			getRegistryStringValue(regKey, L"ThisVersionDTECLSID", clsID, StringUtil::WBLANK);
+
+			VSVersionInfo info;
+			info.name = version.second.name;
+			info.execPath = installPath.append(version.second.executable);
+			info.CLSID = clsID;
+
+			versionInfo[info.name] = info;
+		}
+
+		// TODO - Also query for VSExpress and VSCommunity (their registry keys are different)
+
+		return versionInfo;
+	}
+
+	CodeEditor* VSCodeEditorFactory::create(const WString& editor) const
+	{
+		auto findIter = mAvailableVersions.find(editor);
+		if (findIter == mAvailableVersions.end())
+			return nullptr;
+
+		// TODO - Also create VSExpress and VSCommunity editors
+
+		return bs_new<VSCodeEditor>(findIter->second.execPath, findIter->second.CLSID);
+	}
+}