|
|
@@ -7,9 +7,9 @@
|
|
|
#include "BsDataStream.h"
|
|
|
|
|
|
// Import EnvDTE
|
|
|
-//#pragma warning(disable: 4278)
|
|
|
-//#import "libid:80cc9f66-e7d8-4ddd-85b6-d9e6cd0e93e2" version("8.0") lcid("0") raw_interfaces_only named_guids
|
|
|
-//#pragma warning(default: 4278)
|
|
|
+#pragma warning(disable: 4278)
|
|
|
+#import "libid:80cc9f66-e7d8-4ddd-85b6-d9e6cd0e93e2" version("8.0") lcid("0") raw_interfaces_only named_guids
|
|
|
+#pragma warning(default: 4278)
|
|
|
|
|
|
namespace BansheeEngine
|
|
|
{
|
|
|
@@ -46,366 +46,366 @@ namespace BansheeEngine
|
|
|
* Handles retrying of calls that fail to access Visual Studio. This is due to the weird nature of VS when calling its
|
|
|
* methods from external code. If this message filter isn't registered some calls will just fail silently.
|
|
|
*/
|
|
|
- //class VSMessageFilter : public IMessageFilter
|
|
|
- //{
|
|
|
- // DWORD __stdcall HandleInComingCall(DWORD dwCallType, HTASK htaskCaller, DWORD dwTickCount, LPINTERFACEINFO lpInterfaceInfo) override
|
|
|
- // {
|
|
|
- // return SERVERCALL_ISHANDLED;
|
|
|
- // }
|
|
|
-
|
|
|
- // DWORD __stdcall RetryRejectedCall(HTASK htaskCallee, DWORD dwTickCount, DWORD dwRejectType) override
|
|
|
- // {
|
|
|
- // if (dwRejectType == SERVERCALL_RETRYLATER)
|
|
|
- // {
|
|
|
- // // Retry immediatey
|
|
|
- // return 99;
|
|
|
- // }
|
|
|
- // // Cancel the call
|
|
|
- // return -1;
|
|
|
- // }
|
|
|
-
|
|
|
- // DWORD __stdcall MessagePending(HTASK htaskCallee, DWORD dwTickCount, DWORD dwPendingType) override
|
|
|
- // {
|
|
|
- // return PENDINGMSG_WAITDEFPROCESS;
|
|
|
- // }
|
|
|
-
|
|
|
- // /** COM requirement. Returns instance of an interface of provided type. */
|
|
|
- // HRESULT __stdcall QueryInterface(REFIID iid, void** ppvObject) override
|
|
|
- // {
|
|
|
- // if(iid == IID_IDropTarget || iid == IID_IUnknown)
|
|
|
- // {
|
|
|
- // AddRef();
|
|
|
- // *ppvObject = this;
|
|
|
- // return S_OK;
|
|
|
- // }
|
|
|
- // else
|
|
|
- // {
|
|
|
- // *ppvObject = nullptr;
|
|
|
- // return E_NOINTERFACE;
|
|
|
- // }
|
|
|
- // }
|
|
|
-
|
|
|
- // /** COM requirement. Increments objects reference count. */
|
|
|
- // ULONG __stdcall AddRef() override
|
|
|
- // {
|
|
|
- // return InterlockedIncrement(&mRefCount);
|
|
|
- // }
|
|
|
-
|
|
|
- // /** COM requirement. Decreases the objects reference count and deletes the object if its zero. */
|
|
|
- // ULONG __stdcall Release() override
|
|
|
- // {
|
|
|
- // LONG count = InterlockedDecrement(&mRefCount);
|
|
|
-
|
|
|
- // if(count == 0)
|
|
|
- // {
|
|
|
- // bs_delete(this);
|
|
|
- // return 0;
|
|
|
- // }
|
|
|
- // else
|
|
|
- // {
|
|
|
- // return count;
|
|
|
- // }
|
|
|
- // }
|
|
|
-
|
|
|
- //private:
|
|
|
- // LONG mRefCount;
|
|
|
- //};
|
|
|
+ class VSMessageFilter : public IMessageFilter
|
|
|
+ {
|
|
|
+ DWORD __stdcall HandleInComingCall(DWORD dwCallType, HTASK htaskCaller, DWORD dwTickCount, LPINTERFACEINFO lpInterfaceInfo) override
|
|
|
+ {
|
|
|
+ return SERVERCALL_ISHANDLED;
|
|
|
+ }
|
|
|
+
|
|
|
+ DWORD __stdcall RetryRejectedCall(HTASK htaskCallee, DWORD dwTickCount, DWORD dwRejectType) override
|
|
|
+ {
|
|
|
+ if (dwRejectType == SERVERCALL_RETRYLATER)
|
|
|
+ {
|
|
|
+ // Retry immediatey
|
|
|
+ return 99;
|
|
|
+ }
|
|
|
+ // Cancel the call
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ DWORD __stdcall MessagePending(HTASK htaskCallee, DWORD dwTickCount, DWORD dwPendingType) override
|
|
|
+ {
|
|
|
+ return PENDINGMSG_WAITDEFPROCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** COM requirement. Returns instance of an interface of provided type. */
|
|
|
+ HRESULT __stdcall QueryInterface(REFIID iid, void** ppvObject) override
|
|
|
+ {
|
|
|
+ if(iid == IID_IDropTarget || iid == IID_IUnknown)
|
|
|
+ {
|
|
|
+ AddRef();
|
|
|
+ *ppvObject = this;
|
|
|
+ return S_OK;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ *ppvObject = nullptr;
|
|
|
+ return E_NOINTERFACE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** COM requirement. Increments objects reference count. */
|
|
|
+ ULONG __stdcall AddRef() override
|
|
|
+ {
|
|
|
+ return InterlockedIncrement(&mRefCount);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** COM requirement. Decreases the objects reference count and deletes the object if its zero. */
|
|
|
+ ULONG __stdcall Release() override
|
|
|
+ {
|
|
|
+ LONG count = InterlockedDecrement(&mRefCount);
|
|
|
+
|
|
|
+ if(count == 0)
|
|
|
+ {
|
|
|
+ bs_delete(this);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return count;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private:
|
|
|
+ LONG mRefCount;
|
|
|
+ };
|
|
|
|
|
|
/** Contains various helper classes for interacting with a Visual Studio instance running on this machine. */
|
|
|
- //class VisualStudio
|
|
|
- //{
|
|
|
- //private:
|
|
|
- // static const String SLN_TEMPLATE; /**< Template text used for a solution file. */
|
|
|
- // static const String PROJ_ENTRY_TEMPLATE; /**< Template text used for a project entry in a solution file. */
|
|
|
- // static const String PROJ_PLATFORM_TEMPLATE; /**< Template text used for platform specific information for a project entry in a solution file. */
|
|
|
-
|
|
|
- // static const String PROJ_TEMPLATE; /**< Template XML used for a project file. */
|
|
|
- // static const String REFERENCE_ENTRY_TEMPLATE; /**< Template XML used for a reference to another assembly entry by name. */
|
|
|
- // static const String REFERENCE_PROJECT_ENTRY_TEMPLATE; /**< Template XML used for a reference to another project entry. */
|
|
|
- // static const String REFERENCE_PATH_ENTRY_TEMPLATE; /**< Template XML used for a reference to another assembly entry by name and path. */
|
|
|
- // static const String CODE_ENTRY_TEMPLATE; /**< Template XML used for a single code file entry in a project. */
|
|
|
- // static const String NON_CODE_ENTRY_TEMPLATE; /**< Template XML used for a single non-code file entry in a project. */
|
|
|
-
|
|
|
- //public:
|
|
|
- // /**
|
|
|
- // * Scans the running processes to find a running Visual Studio instance with the specified version and open solution.
|
|
|
- // *
|
|
|
- // * @param[in] clsID Class ID of the specific Visual Studio version we are looking for.
|
|
|
- // * @param[in] solutionPath Path to the solution the instance needs to have open.
|
|
|
- // * @return DTE object that may be used to interact with the Visual Studio instance, or null if
|
|
|
- // * not found.
|
|
|
- // */
|
|
|
- // 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->QueryInterface(__uuidof(EnvDTE::_DTE), (void**)&dte);
|
|
|
-
|
|
|
- // if (dte == nullptr)
|
|
|
- // continue;
|
|
|
-
|
|
|
- // 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;
|
|
|
- // }
|
|
|
-
|
|
|
- // /**
|
|
|
- // * Opens a new Visual Studio instance of the specified version with the provided solution.
|
|
|
- // *
|
|
|
- // * @param[in] clsID Class ID of the specific Visual Studio version to start.
|
|
|
- // * @param[in] solutionPath Path to the solution the instance needs to open.
|
|
|
- // */
|
|
|
- // 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->QueryInterface(__uuidof(EnvDTE::_DTE), (void**)&dte);
|
|
|
-
|
|
|
- // 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;
|
|
|
- // }
|
|
|
-
|
|
|
- // /**
|
|
|
- // * Opens a file on a specific line in a running Visual Studio instance.
|
|
|
- // *
|
|
|
- // * @param[in] dte DTE object retrieved from findRunningInstance() or openInstance().
|
|
|
- // * @param[in] filePath Path of the file to open. File should be a part of the VS solution.
|
|
|
- // * @param[in] line Line on which to focus Visual Studio after the file is open.
|
|
|
- // */
|
|
|
- // 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;
|
|
|
- // }
|
|
|
-
|
|
|
- // /** Generates a Visual Studio project GUID from the project name. */
|
|
|
- // static String getProjectGUID(const WString& projectName)
|
|
|
- // {
|
|
|
- // static const String guidTemplate = "{0}-{1}-{2}-{3}-{4}";
|
|
|
- // String hash = md5(projectName);
|
|
|
-
|
|
|
- // String output = StringUtil::format(guidTemplate, hash.substr(0, 8),
|
|
|
- // hash.substr(8, 4), hash.substr(12, 4), hash.substr(16, 4), hash.substr(20, 12));
|
|
|
- // StringUtil::toUpperCase(output);
|
|
|
-
|
|
|
- // return output;
|
|
|
- // }
|
|
|
-
|
|
|
- // /**
|
|
|
- // * Builds the Visual Studio solution text (.sln) for the provided version, using the provided solution data.
|
|
|
- // *
|
|
|
- // * @param[in] version Visual Studio version for which we're generating the solution file.
|
|
|
- // * @param[in] data Data containing a list of projects and other information required to build the solution text.
|
|
|
- // * @return Generated text of the solution file.
|
|
|
- // */
|
|
|
- // static String writeSolution(VisualStudioVersion version, const CodeSolutionData& data)
|
|
|
- // {
|
|
|
- // struct VersionData
|
|
|
- // {
|
|
|
- // String formatVersion;
|
|
|
- // };
|
|
|
-
|
|
|
- // Map<VisualStudioVersion, VersionData> versionData =
|
|
|
- // {
|
|
|
- // { VisualStudioVersion::VS2008, { "10.00" } },
|
|
|
- // { VisualStudioVersion::VS2010, { "11.00" } },
|
|
|
- // { VisualStudioVersion::VS2012, { "12.00" } },
|
|
|
- // { VisualStudioVersion::VS2013, { "12.00" } },
|
|
|
- // { VisualStudioVersion::VS2015, { "12.00" } }
|
|
|
- // };
|
|
|
-
|
|
|
- // StringStream projectEntriesStream;
|
|
|
- // StringStream projectPlatformsStream;
|
|
|
- // for (auto& project : data.projects)
|
|
|
- // {
|
|
|
- // String guid = getProjectGUID(project.name);
|
|
|
- // String projectName = toString(project.name);
|
|
|
-
|
|
|
- // projectEntriesStream << StringUtil::format(PROJ_ENTRY_TEMPLATE, projectName, projectName + ".csproj", guid);
|
|
|
- // projectPlatformsStream << StringUtil::format(PROJ_PLATFORM_TEMPLATE, guid);
|
|
|
- // }
|
|
|
-
|
|
|
- // String projectEntries = projectEntriesStream.str();
|
|
|
- // String projectPlatforms = projectPlatformsStream.str();
|
|
|
-
|
|
|
- // return StringUtil::format(SLN_TEMPLATE, versionData[version].formatVersion, projectEntries, projectPlatforms);
|
|
|
- // }
|
|
|
-
|
|
|
- // /**
|
|
|
- // * Builds the Visual Studio project text (.csproj) for the provided version, using the provided project data.
|
|
|
- // *
|
|
|
- // * @param[in] version Visual Studio version for which we're generating the project file.
|
|
|
- // * @param[in] projectData Data containing a list of files, references and other information required to
|
|
|
- // * build the project text.
|
|
|
- // * @return Generated text of the project file.
|
|
|
- // */
|
|
|
- // static String writeProject(VisualStudioVersion version, const CodeProjectData& projectData)
|
|
|
- // {
|
|
|
- // struct VersionData
|
|
|
- // {
|
|
|
- // String toolsVersion;
|
|
|
- // };
|
|
|
-
|
|
|
- // Map<VisualStudioVersion, VersionData> versionData =
|
|
|
- // {
|
|
|
- // { VisualStudioVersion::VS2008, { "3.5" } },
|
|
|
- // { VisualStudioVersion::VS2010, { "4.0" } },
|
|
|
- // { VisualStudioVersion::VS2012, { "4.0" } },
|
|
|
- // { VisualStudioVersion::VS2013, { "12.0" } },
|
|
|
- // { VisualStudioVersion::VS2015, { "13.0" } }
|
|
|
- // };
|
|
|
-
|
|
|
- // StringStream tempStream;
|
|
|
- // for (auto& codeEntry : projectData.codeFiles)
|
|
|
- // tempStream << StringUtil::format(CODE_ENTRY_TEMPLATE, codeEntry.toString());
|
|
|
-
|
|
|
- // String codeEntries = tempStream.str();
|
|
|
- // tempStream.str("");
|
|
|
- // tempStream.clear();
|
|
|
-
|
|
|
- // for (auto& nonCodeEntry : projectData.nonCodeFiles)
|
|
|
- // tempStream << StringUtil::format(NON_CODE_ENTRY_TEMPLATE, nonCodeEntry.toString());
|
|
|
-
|
|
|
- // String nonCodeEntries = tempStream.str();
|
|
|
- // tempStream.str("");
|
|
|
- // tempStream.clear();
|
|
|
-
|
|
|
- // for (auto& referenceEntry : projectData.assemblyReferences)
|
|
|
- // {
|
|
|
- // String referenceName = toString(referenceEntry.name);
|
|
|
-
|
|
|
- // if (referenceEntry.path.isEmpty())
|
|
|
- // tempStream << StringUtil::format(REFERENCE_ENTRY_TEMPLATE, referenceName);
|
|
|
- // else
|
|
|
- // tempStream << StringUtil::format(REFERENCE_PATH_ENTRY_TEMPLATE, referenceName, referenceEntry.path.toString());
|
|
|
- // }
|
|
|
-
|
|
|
- // String referenceEntries = tempStream.str();
|
|
|
- // tempStream.str("");
|
|
|
- // tempStream.clear();
|
|
|
-
|
|
|
- // for (auto& referenceEntry : projectData.projectReferences)
|
|
|
- // {
|
|
|
- // String referenceName = toString(referenceEntry.name);
|
|
|
- // String projectGUID = getProjectGUID(referenceEntry.name);
|
|
|
-
|
|
|
- // tempStream << StringUtil::format(REFERENCE_PROJECT_ENTRY_TEMPLATE, referenceName, projectGUID);
|
|
|
- // }
|
|
|
-
|
|
|
- // String projectReferenceEntries = tempStream.str();
|
|
|
- // tempStream.str("");
|
|
|
- // tempStream.clear();
|
|
|
-
|
|
|
- // tempStream << toString(projectData.defines);
|
|
|
-
|
|
|
- // String defines = tempStream.str();
|
|
|
- // String projectGUID = getProjectGUID(projectData.name);
|
|
|
-
|
|
|
- // return StringUtil::format(PROJ_TEMPLATE, versionData[version].toolsVersion, projectGUID,
|
|
|
- // toString(projectData.name), defines, referenceEntries, projectReferenceEntries, codeEntries, nonCodeEntries);
|
|
|
- // }
|
|
|
- //};
|
|
|
-
|
|
|
- /*const String VisualStudio::SLN_TEMPLATE =
|
|
|
+ class VisualStudio
|
|
|
+ {
|
|
|
+ private:
|
|
|
+ static const String SLN_TEMPLATE; /**< Template text used for a solution file. */
|
|
|
+ static const String PROJ_ENTRY_TEMPLATE; /**< Template text used for a project entry in a solution file. */
|
|
|
+ static const String PROJ_PLATFORM_TEMPLATE; /**< Template text used for platform specific information for a project entry in a solution file. */
|
|
|
+
|
|
|
+ static const String PROJ_TEMPLATE; /**< Template XML used for a project file. */
|
|
|
+ static const String REFERENCE_ENTRY_TEMPLATE; /**< Template XML used for a reference to another assembly entry by name. */
|
|
|
+ static const String REFERENCE_PROJECT_ENTRY_TEMPLATE; /**< Template XML used for a reference to another project entry. */
|
|
|
+ static const String REFERENCE_PATH_ENTRY_TEMPLATE; /**< Template XML used for a reference to another assembly entry by name and path. */
|
|
|
+ static const String CODE_ENTRY_TEMPLATE; /**< Template XML used for a single code file entry in a project. */
|
|
|
+ static const String NON_CODE_ENTRY_TEMPLATE; /**< Template XML used for a single non-code file entry in a project. */
|
|
|
+
|
|
|
+ public:
|
|
|
+ /**
|
|
|
+ * Scans the running processes to find a running Visual Studio instance with the specified version and open solution.
|
|
|
+ *
|
|
|
+ * @param[in] clsID Class ID of the specific Visual Studio version we are looking for.
|
|
|
+ * @param[in] solutionPath Path to the solution the instance needs to have open.
|
|
|
+ * @return DTE object that may be used to interact with the Visual Studio instance, or null if
|
|
|
+ * not found.
|
|
|
+ */
|
|
|
+ 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->QueryInterface(__uuidof(EnvDTE::_DTE), (void**)&dte);
|
|
|
+
|
|
|
+ if (dte == nullptr)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Opens a new Visual Studio instance of the specified version with the provided solution.
|
|
|
+ *
|
|
|
+ * @param[in] clsID Class ID of the specific Visual Studio version to start.
|
|
|
+ * @param[in] solutionPath Path to the solution the instance needs to open.
|
|
|
+ */
|
|
|
+ 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->QueryInterface(__uuidof(EnvDTE::_DTE), (void**)&dte);
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Opens a file on a specific line in a running Visual Studio instance.
|
|
|
+ *
|
|
|
+ * @param[in] dte DTE object retrieved from findRunningInstance() or openInstance().
|
|
|
+ * @param[in] filePath Path of the file to open. File should be a part of the VS solution.
|
|
|
+ * @param[in] line Line on which to focus Visual Studio after the file is open.
|
|
|
+ */
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Generates a Visual Studio project GUID from the project name. */
|
|
|
+ static String getProjectGUID(const WString& projectName)
|
|
|
+ {
|
|
|
+ static const String guidTemplate = "{0}-{1}-{2}-{3}-{4}";
|
|
|
+ String hash = md5(projectName);
|
|
|
+
|
|
|
+ String output = StringUtil::format(guidTemplate, hash.substr(0, 8),
|
|
|
+ hash.substr(8, 4), hash.substr(12, 4), hash.substr(16, 4), hash.substr(20, 12));
|
|
|
+ StringUtil::toUpperCase(output);
|
|
|
+
|
|
|
+ return output;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Builds the Visual Studio solution text (.sln) for the provided version, using the provided solution data.
|
|
|
+ *
|
|
|
+ * @param[in] version Visual Studio version for which we're generating the solution file.
|
|
|
+ * @param[in] data Data containing a list of projects and other information required to build the solution text.
|
|
|
+ * @return Generated text of the solution file.
|
|
|
+ */
|
|
|
+ static String writeSolution(VisualStudioVersion version, const CodeSolutionData& data)
|
|
|
+ {
|
|
|
+ struct VersionData
|
|
|
+ {
|
|
|
+ String formatVersion;
|
|
|
+ };
|
|
|
+
|
|
|
+ Map<VisualStudioVersion, VersionData> versionData =
|
|
|
+ {
|
|
|
+ { VisualStudioVersion::VS2008, { "10.00" } },
|
|
|
+ { VisualStudioVersion::VS2010, { "11.00" } },
|
|
|
+ { VisualStudioVersion::VS2012, { "12.00" } },
|
|
|
+ { VisualStudioVersion::VS2013, { "12.00" } },
|
|
|
+ { VisualStudioVersion::VS2015, { "12.00" } }
|
|
|
+ };
|
|
|
+
|
|
|
+ StringStream projectEntriesStream;
|
|
|
+ StringStream projectPlatformsStream;
|
|
|
+ for (auto& project : data.projects)
|
|
|
+ {
|
|
|
+ String guid = getProjectGUID(project.name);
|
|
|
+ String projectName = toString(project.name);
|
|
|
+
|
|
|
+ projectEntriesStream << StringUtil::format(PROJ_ENTRY_TEMPLATE, projectName, projectName + ".csproj", guid);
|
|
|
+ projectPlatformsStream << StringUtil::format(PROJ_PLATFORM_TEMPLATE, guid);
|
|
|
+ }
|
|
|
+
|
|
|
+ String projectEntries = projectEntriesStream.str();
|
|
|
+ String projectPlatforms = projectPlatformsStream.str();
|
|
|
+
|
|
|
+ return StringUtil::format(SLN_TEMPLATE, versionData[version].formatVersion, projectEntries, projectPlatforms);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Builds the Visual Studio project text (.csproj) for the provided version, using the provided project data.
|
|
|
+ *
|
|
|
+ * @param[in] version Visual Studio version for which we're generating the project file.
|
|
|
+ * @param[in] projectData Data containing a list of files, references and other information required to
|
|
|
+ * build the project text.
|
|
|
+ * @return Generated text of the project file.
|
|
|
+ */
|
|
|
+ static String writeProject(VisualStudioVersion version, const CodeProjectData& projectData)
|
|
|
+ {
|
|
|
+ struct VersionData
|
|
|
+ {
|
|
|
+ String toolsVersion;
|
|
|
+ };
|
|
|
+
|
|
|
+ Map<VisualStudioVersion, VersionData> versionData =
|
|
|
+ {
|
|
|
+ { VisualStudioVersion::VS2008, { "3.5" } },
|
|
|
+ { VisualStudioVersion::VS2010, { "4.0" } },
|
|
|
+ { VisualStudioVersion::VS2012, { "4.0" } },
|
|
|
+ { VisualStudioVersion::VS2013, { "12.0" } },
|
|
|
+ { VisualStudioVersion::VS2015, { "13.0" } }
|
|
|
+ };
|
|
|
+
|
|
|
+ StringStream tempStream;
|
|
|
+ for (auto& codeEntry : projectData.codeFiles)
|
|
|
+ tempStream << StringUtil::format(CODE_ENTRY_TEMPLATE, codeEntry.toString());
|
|
|
+
|
|
|
+ String codeEntries = tempStream.str();
|
|
|
+ tempStream.str("");
|
|
|
+ tempStream.clear();
|
|
|
+
|
|
|
+ for (auto& nonCodeEntry : projectData.nonCodeFiles)
|
|
|
+ tempStream << StringUtil::format(NON_CODE_ENTRY_TEMPLATE, nonCodeEntry.toString());
|
|
|
+
|
|
|
+ String nonCodeEntries = tempStream.str();
|
|
|
+ tempStream.str("");
|
|
|
+ tempStream.clear();
|
|
|
+
|
|
|
+ for (auto& referenceEntry : projectData.assemblyReferences)
|
|
|
+ {
|
|
|
+ String referenceName = toString(referenceEntry.name);
|
|
|
+
|
|
|
+ if (referenceEntry.path.isEmpty())
|
|
|
+ tempStream << StringUtil::format(REFERENCE_ENTRY_TEMPLATE, referenceName);
|
|
|
+ else
|
|
|
+ tempStream << StringUtil::format(REFERENCE_PATH_ENTRY_TEMPLATE, referenceName, referenceEntry.path.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ String referenceEntries = tempStream.str();
|
|
|
+ tempStream.str("");
|
|
|
+ tempStream.clear();
|
|
|
+
|
|
|
+ for (auto& referenceEntry : projectData.projectReferences)
|
|
|
+ {
|
|
|
+ String referenceName = toString(referenceEntry.name);
|
|
|
+ String projectGUID = getProjectGUID(referenceEntry.name);
|
|
|
+
|
|
|
+ tempStream << StringUtil::format(REFERENCE_PROJECT_ENTRY_TEMPLATE, referenceName, projectGUID);
|
|
|
+ }
|
|
|
+
|
|
|
+ String projectReferenceEntries = tempStream.str();
|
|
|
+ tempStream.str("");
|
|
|
+ tempStream.clear();
|
|
|
+
|
|
|
+ tempStream << toString(projectData.defines);
|
|
|
+
|
|
|
+ String defines = tempStream.str();
|
|
|
+ String projectGUID = getProjectGUID(projectData.name);
|
|
|
+
|
|
|
+ return StringUtil::format(PROJ_TEMPLATE, versionData[version].toolsVersion, projectGUID,
|
|
|
+ toString(projectData.name), defines, referenceEntries, projectReferenceEntries, codeEntries, nonCodeEntries);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const String VisualStudio::SLN_TEMPLATE =
|
|
|
R"(Microsoft Visual Studio Solution File, Format Version {0}
|
|
|
# Visual Studio 2013
|
|
|
VisualStudioVersion = 12.0.30723.0
|
|
|
@@ -505,7 +505,7 @@ EndProject)";
|
|
|
|
|
|
const String VisualStudio::NON_CODE_ENTRY_TEMPLATE =
|
|
|
R"(
|
|
|
- <None Include="{0}"/>)";*/
|
|
|
+ <None Include="{0}"/>)";
|
|
|
|
|
|
VSCodeEditor::VSCodeEditor(VisualStudioVersion version, const Path& execPath, const WString& CLSID)
|
|
|
:mVersion(version), mExecPath(execPath), mCLSID(CLSID)
|
|
|
@@ -515,7 +515,7 @@ EndProject)";
|
|
|
|
|
|
void VSCodeEditor::openFile(const Path& solutionPath, const Path& filePath, UINT32 lineNumber) const
|
|
|
{
|
|
|
- /*CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
|
|
+ CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
|
|
|
|
|
CLSID clsID;
|
|
|
if (FAILED(CLSIDFromString(mCLSID.c_str(), &clsID)))
|
|
|
@@ -545,12 +545,12 @@ EndProject)";
|
|
|
VisualStudio::openFile(dte, filePath, lineNumber);
|
|
|
CoRegisterMessageFilter(oldFilter, nullptr);
|
|
|
|
|
|
- CoUninitialize();*/
|
|
|
+ CoUninitialize();
|
|
|
}
|
|
|
|
|
|
void VSCodeEditor::syncSolution(const CodeSolutionData& data, const Path& outputPath) const
|
|
|
{
|
|
|
- /*String solutionString = VisualStudio::writeSolution(mVersion, data);
|
|
|
+ String solutionString = VisualStudio::writeSolution(mVersion, data);
|
|
|
solutionString = StringUtil::replaceAll(solutionString, "\n", "\r\n");
|
|
|
Path solutionPath = outputPath;
|
|
|
solutionPath.append(data.name + L".sln");
|
|
|
@@ -570,7 +570,7 @@ EndProject)";
|
|
|
|
|
|
SPtr<DataStream> solutionStream = FileSystem::createAndOpenFile(solutionPath);
|
|
|
solutionStream->write(solutionString.c_str(), solutionString.size() * sizeof(String::value_type));
|
|
|
- solutionStream->close();*/
|
|
|
+ solutionStream->close();
|
|
|
}
|
|
|
|
|
|
VSCodeEditorFactory::VSCodeEditorFactory()
|