locked
With the information from Microsoft.VisualStudio.Setup.Configuration how can I run a selected VS and use the interop features of VS. RRS feed

  • Question

  • I have been using EnvDTE80.DTE2 with older versions of VS. I need to run the VS found using Microsoft.VisualStudio.Setup.Configuration and then execute some commands. How do I get the EnvDTE variable set?

    Thanks


    Tom G.

    • Moved by May Luo-MSFT Monday, April 22, 2019 6:43 AM general issue
    • Moved by May Luo-MSFT Monday, April 22, 2019 7:24 AM VSX issue
    Friday, April 19, 2019 7:50 PM

Answers

  • Hi Tom,

    Welcome to MSDN forum.

    In which situation you'll use the Microsoft.VisualStudio.Setup.Configuration namespace? In a console app or a VSIX project.

    If what you want is to create the instance outside vs by a console app, maybe this blog can make some help for you. (Each Visual Studio version has its own progID, after you get the vs instance info from ISetupInstance2, you will get the ProgID, such like 12.0 for vs2013).

    And to use features of VS, you can use DTE2.ExecuteCommand() method.

    Hope it helps.

    Best Regards

    Lance


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    Monday, April 22, 2019 9:39 AM

All replies

  • Hi Tom Groszko,

    Welcome to the MSDN forum.

    Based on your description, it seems be related with VSX issue, I will help you move to the appropriate forum for a better support.

    Thank you for your understanding.

    Sincerely

    May


    MSDN Community Support Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com

    Monday, April 22, 2019 7:22 AM
  • Hi Tom,

    Welcome to MSDN forum.

    In which situation you'll use the Microsoft.VisualStudio.Setup.Configuration namespace? In a console app or a VSIX project.

    If what you want is to create the instance outside vs by a console app, maybe this blog can make some help for you. (Each Visual Studio version has its own progID, after you get the vs instance info from ISetupInstance2, you will get the ProgID, such like 12.0 for vs2013).

    And to use features of VS, you can use DTE2.ExecuteCommand() method.

    Hope it helps.

    Best Regards

    Lance


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    Monday, April 22, 2019 9:39 AM
  • Hi Tom,

    Ran into a similar situation a while back, after VS 2017 moved to supporting side by side installs of different SKU's of VS (Community, Pro, Ent, Preview, etc).

    Looks like you already found the right way to find and launch VS. All you need to do now is retrieve the DTE interface from the system's Running Object Table. Each instances of devenv.exe, register's it's DTE interface in the Running Object table, with a moniker that looks something like:

    !VisualStudio.DTE.15.0:<pid>
    

    As you're launching the process, you should be able to ascertain it's Process ID (pid). Then just enumerate the ROT, to find the instances of the DTE interface you're looking for. Below is a simple C++ console app I wrote as a proof of concept a while back, and a quick internet search turned up a blog entry, which looks to do something similar using C#. Mileage may vary, as I haven't actually tested that code out.

    // AutomateSxSInstalls.cpp : Simple console application illustrating how to
    //                           automate side-by-side installs of VS 2017
    //
    
    #include "stdafx.h"
    using namespace std;
    
    #define MAX_TRIES 30
    
    //
    // Uses Nuget Package Microsoft.VisualStudio.Setup.Configuration.Native to retrieve
    // the full paths to devenv.exe for all instances of VS 2017.
    //
    vector<wstring> GetVSInstallPaths()
    {
    	std::vector<wstring> devenvPaths;
    	CComPtr<ISetupConfiguration> spSetupConfig;
    	CComPtr<IEnumSetupInstances> spEnumInstances;
    	CComPtr<ISetupInstance> spSetupInstance;
    
    	int hr = spSetupConfig.CoCreateInstance(__uuidof(SetupConfiguration));
    	hr = spSetupConfig->EnumInstances(&spEnumInstances);
    	hr = spEnumInstances->Next(1, &spSetupInstance, NULL);
    	while (S_OK == hr)
    	{
    		CComBSTR bstrInstallPath;
    		hr = spSetupInstance->GetInstallationPath(&bstrInstallPath);
    		wstring path = (wstring)bstrInstallPath + L"\\Common7\\IDE\\devenv.exe";
    		devenvPaths.push_back(path);
    		spSetupInstance.Release();
    		hr = spEnumInstances->Next(1, &spSetupInstance, NULL);
    	}
    
    	return devenvPaths;
    }
    
    //
    // retrieve the DTE interface from the system's Running Object Table.
    //
    IUnknown* GetDTEFromProcess(DWORD pid)
    {
    	IUnknown* pUnk = NULL;
    	CComPtr<IRunningObjectTable> spROT;
    	int hr = ::GetRunningObjectTable(NULL, &spROT);
    
    	// the IDE registers its _DTE interface in the system's Running Object Table with the
    	// following moniker.
    	wstring displayName(L"!VisualStudio.DTE.15.0:");
    	displayName += to_wstring(pid);
    
    	BOOL bFound = false;
    	int numTries = 0;
    
    	// wait for DTE interface to appear in the ROT, as the ide 
    	// may be busy initializing new extensions, templates etc.
    	while (!bFound && numTries < MAX_TRIES)
    	{
    		CComPtr<IEnumMoniker> spEnumMoniker;
    		CComPtr<IMoniker> spMoniker;
    		CComPtr<IBindCtx> spBindCtx;
    
    		hr = ::CreateBindCtx(NULL, &spBindCtx);
    		hr = spROT->EnumRunning(&spEnumMoniker);
    		hr = spEnumMoniker->Reset();
    
    		hr = spEnumMoniker->Next(1, &spMoniker, NULL);
    		while (S_OK == hr)
    		{
    			OLECHAR* pszDisplayName;
    			hr = spMoniker->GetDisplayName(spBindCtx, NULL, &pszDisplayName);
    			if (displayName.compare(pszDisplayName) == 0)
    			{
    				hr = spROT->GetObjectW(spMoniker, &pUnk);
    				bFound = TRUE;
    			}
    
    			::CoTaskMemFree(pszDisplayName);
    			spMoniker.Release();
    			hr = spEnumMoniker->Next(1, &spMoniker, NULL);
    		}
    
    		if (!bFound)
    		{
    			// wait (longer on each subsequent attempt) and try again
    			numTries++;
    			::Sleep(1000 * numTries);
    		}
    	}
    
    	return pUnk;
    }
    
    //
    // launch devenv via the specified path, retrieve it's DTE 
    // interface, and then invoke a couple of it's methods/properties.
    //
    HRESULT AutomateInstance(wstring path)
    {
    	wcout << L"starting " << path << endl;
    
    	SHELLEXECUTEINFO info;
    	info.cbSize = sizeof(SHELLEXECUTEINFO);
    	info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_WAITFORINPUTIDLE;
    	info.hwnd = NULL;
    	info.lpVerb = NULL;
    	info.lpFile = path.c_str();
    	info.lpParameters = L"-embedding";  // simulate launching via CoCreateInstance
    	info.lpDirectory = NULL;
    	info.nShow = SW_NORMAL;
    	info.hInstApp = NULL;
    	info.hProcess = NULL;
    
    	// launch specified devenv.exe process
    	if (ShellExecuteEx(&info))
    	{
    		DWORD pid = GetProcessId(info.hProcess);
    		IUnknown* pUnk = GetDTEFromProcess(pid);
    		CComQIPtr<EnvDTE::_DTE> spDTE(pUnk);
    
    		CComBSTR bstrFullName;
    		int hr = spDTE->get_FullName(&bstrFullName);
    
    		// proof we have the automation interface
    		wcout << L"   EnvDTE::DTE::FullName = " << bstrFullName.m_str << endl << endl;
    
    		hr = spDTE->Quit();
    	}
    
    	return S_OK;
    }
    
    HRESULT AutomateVSInstances()
    {
    	vector<wstring> paths = GetVSInstallPaths();
    	for (auto it = paths.begin(); it != paths.end(); ++it)
    	{
    		AutomateInstance(*it);
    	}
    
    	return S_OK;
    }
    
    int main()
    {
    	int hr = ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    	hr = AutomateVSInstances();
    	::CoUninitialize();
    
    	return hr;
    }
    

    Sincerely,


    Ed Dore

    Monday, April 22, 2019 5:05 PM
  • Thanks. This get me to where I want to be. I can execute some commands but I am getting different behavior than I did with earlier versions of VS. This is the list of commands.

    string DGMLFileName = @"C:\Users\tomgr\Documents\MTGDictionary\AdventureWorks2008R2_MTGDataDictionary_2018_01_31 14_59_49_TableDiagram.dgml ";
    EnvDTE.Window DGMLImage = VisualStudio.ItemOperations.OpenFile(DGMLFileName);
    System.Threading.Thread.Sleep(8000);
    VisualStudio.MainWindow.Activate();
    VisualStudio.MainWindow.Visible = false;
    DGMLImage.Activate();
    VisualStudio.ExecuteCommand("DirectedGraph.Zoom", "100");
    VisualStudio.ExecuteCommand("Edit.CopyImage");
    

    The "DirectedGraph.Zoom" command fails. When VS is visible it opens the file without creating the graph.

    Thanks


    Tom G.

    Monday, April 22, 2019 5:39 PM