Skip to content

Instantly share code, notes, and snippets.

@astrohart
Last active January 11, 2021 14:48
Show Gist options
  • Save astrohart/54e96e513ec0ac8ee346bc1772289d7c to your computer and use it in GitHub Desktop.
Save astrohart/54e96e513ec0ac8ee346bc1772289d7c to your computer and use it in GitHub Desktop.
LINQPad Query Boilerplate to Access VS 2019 via EnvDTE from LINQpad
<Query Kind="Program">
<Reference>&lt;RuntimeDirectory&gt;\System.Windows.Forms.dll</Reference>
<NuGetReference>EnvDTE</NuGetReference>
<NuGetReference>Microsoft.VisualStudio.Setup.Configuration.Interop</NuGetReference>
<Namespace>EnvDTE</Namespace>
<Namespace>Microsoft.VisualStudio.Setup.Configuration</Namespace>
<Namespace>System</Namespace>
<Namespace>System.Collections.Generic</Namespace>
<Namespace>System.Diagnostics</Namespace>
<Namespace>System.IO</Namespace>
<Namespace>System.Linq</Namespace>
<Namespace>System.Runtime.InteropServices</Namespace>
<Namespace>System.Runtime.InteropServices.ComTypes</Namespace>
<Namespace>System.Threading</Namespace>
<Namespace>System.Windows.Forms</Namespace>
</Query>
// boilerplate: clone this query and then customize to your liking
// make sure the NuGets and query properties get cloned.
//
// Thank you https://docs.microsoft.com/en-us/visualstudio/extensibility/launch-visual-studio-dte?view=vs-2019
void Main()
{
// Launch Visual Studio, and then find the reference to the DTE interface
// that is attached to the newly-spawned VS instance.
System.Diagnostics.Process vsProcess = null; // handle to running VS2019 process
// Create a new instance of the IDE.
EnvDTE.DTE dte = LaunchVsDte(isPreRelease: false, out vsProcess);
// Register the IOleMessageFilter to handle any threading
// errors.
MessageFilter.Register();
/* Play with the launched Visual Studio through the dte variable */
// Display the Visual Studio IDE.
dte.MainWindow.Activate();
// =====================================
// ==Insert your automation code here.==
// =====================================
Solution soln = (Solution)dte.Solution;
MessageBox.Show
("Solution count: " + soln.Count);
// =====================================
/* Close/
exit Visual Studio */
dte.Quit();
/* Dispose the process object */
vsProcess?.Dispose();
vsProcess = null;
}
private static EnvDTE.DTE LaunchVsDte(bool isPreRelease, out System.Diagnostics.Process process)
{
ISetupInstance setupInstance = GetSetupInstance(isPreRelease);
string installationPath = setupInstance.GetInstallationPath();
string executablePath = Path.Combine(installationPath, @"Common7\IDE\devenv.exe");
System.Diagnostics.Process vsProcess = System.Diagnostics.Process.Start(executablePath);
process = vsProcess;
string runningObjectDisplayName = $"VisualStudio.DTE.16.0:{vsProcess.Id}";
IEnumerable<string> runningObjectDisplayNames = null;
object runningObject;
for (int i = 0; i < 60; i++)
{
try
{
runningObject = GetRunningObject(runningObjectDisplayName, out runningObjectDisplayNames);
}
catch
{
runningObject = null;
}
if (runningObject != null)
{
return (EnvDTE.DTE)runningObject;
}
System.Threading.Thread.Sleep(millisecondsTimeout: 1000);
}
throw new TimeoutException($"Failed to retrieve DTE object. Current running objects: {string.Join(";", runningObjectDisplayNames)}");
}
private static object GetRunningObject(string displayName, out IEnumerable<string> runningObjectDisplayNames)
{
IBindCtx bindContext = null;
NativeMethods.CreateBindCtx(0, out bindContext);
IRunningObjectTable runningObjectTable = null;
bindContext.GetRunningObjectTable(out runningObjectTable);
IEnumMoniker monikerEnumerator = null;
runningObjectTable.EnumRunning(out monikerEnumerator);
object runningObject = null;
List<string> runningObjectDisplayNameList = new List<string>();
IMoniker[] monikers = new IMoniker[1];
IntPtr numberFetched = IntPtr.Zero;
while (monikerEnumerator.Next(1, monikers, numberFetched) == 0)
{
IMoniker moniker = monikers[0];
string objectDisplayName = null;
try
{
moniker.GetDisplayName(bindContext, null, out objectDisplayName);
}
catch (UnauthorizedAccessException)
{
// Some ROT objects require elevated permissions.
}
if (!string.IsNullOrWhiteSpace(objectDisplayName))
{
runningObjectDisplayNameList.Add(objectDisplayName);
if (objectDisplayName.EndsWith(displayName, StringComparison.Ordinal))
{
runningObjectTable.GetObject(moniker, out runningObject);
if (runningObject == null)
{
throw new InvalidOperationException($"Failed to get running object with display name {displayName}");
}
}
}
}
runningObjectDisplayNames = runningObjectDisplayNameList;
return runningObject;
}
private static ISetupInstance GetSetupInstance(bool isPreRelease)
{
return GetSetupInstances().First(i => IsPreRelease(i) == isPreRelease);
}
private static IEnumerable<ISetupInstance> GetSetupInstances()
{
ISetupConfiguration setupConfiguration = new SetupConfiguration();
IEnumSetupInstances enumerator = setupConfiguration.EnumInstances();
int count;
do
{
ISetupInstance[] setupInstances = new ISetupInstance[1];
enumerator.Next(1, setupInstances, out count);
if (count == 1 &&
setupInstances != null &&
setupInstances.Length == 1 &&
setupInstances[0] != null)
{
yield return setupInstances[0];
}
}
while (count == 1);
}
private static bool IsPreRelease(ISetupInstance setupInstance)
{
ISetupInstanceCatalog setupInstanceCatalog = (ISetupInstanceCatalog)setupInstance;
return setupInstanceCatalog.IsPrerelease();
}
private static class NativeMethods
{
[DllImport("ole32.dll")]
public static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
[DllImport("ole32.dll")]
public static extern void GetRunningObjectTable(int reserved, out IRunningObjectTable prot);
}
public class MessageFilter : IOleMessageFilter
{
//
// Class containing the IOleMessageFilter
// thread error-handling functions.
// Start the filter.
public static void Register()
{
IOleMessageFilter newFilter = new MessageFilter();
IOleMessageFilter oldFilter = null;
CoRegisterMessageFilter(newFilter, out oldFilter);
}
// Done with the filter, close it.
public static void Revoke()
{
IOleMessageFilter oldFilter = null;
CoRegisterMessageFilter(null, out oldFilter);
}
//
// IOleMessageFilter functions.
// Handle incoming thread requests.
int IOleMessageFilter.HandleInComingCall(int dwCallType,
System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr
lpInterfaceInfo)
{
//Return the flag SERVERCALL_ISHANDLED.
return 0;
}
// Thread call was rejected, so try again.
int IOleMessageFilter.RetryRejectedCall(System.IntPtr
hTaskCallee, int dwTickCount, int dwRejectType)
{
if (dwRejectType == 2)
// flag = SERVERCALL_RETRYLATER.
{
// Retry the thread call immediately if return >=0 &
// <100.
return 99;
}
// Too busy; cancel call.
return -1;
}
int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee,
int dwTickCount, int dwPendingType)
{
//Return the flag PENDINGMSG_WAITDEFPROCESS.
return 2;
}
// Implement the IOleMessageFilter interface.
[DllImport("Ole32.dll")]
private static extern int
CoRegisterMessageFilter(IOleMessageFilter newFilter, out
IOleMessageFilter oldFilter);
}
[ComImport(), Guid("00000016-0000-0000-C000-000000000046"),
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
interface IOleMessageFilter
{
[PreserveSig]
int HandleInComingCall(
int dwCallType,
IntPtr hTaskCaller,
int dwTickCount,
IntPtr lpInterfaceInfo);
[PreserveSig]
int RetryRejectedCall(
IntPtr hTaskCallee,
int dwTickCount,
int dwRejectType);
[PreserveSig]
int MessagePending(
IntPtr hTaskCallee,
int dwTickCount,
int dwPendingType);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment