I felt the following points are unnerving, so I have addressed them and found a solution that works for me:
GetActiveObject("VisualStudio.DTE.10.0")
only works for the first opened (I suppose) Visual Studio
- The
internal static DTE2 GetCurrent()
method of Dennis' answer needs the Visual Studio process Id. That is fine if you run the code from add-ins (I think), but does not work e.g. in unit tests.
- Problems in debug mode
I also started out with the GetCurrent method, taken from here. Problem was, I didn't know how to get to the ProcessId of the right VisualStudio process (usually multiple instances are running). So the approach I took was getting all VisualStudio ROT entries and their DTE2, then comparing DTE2.Solution.FullName to the executing assembly location (do you see a better choice?). While I readily admit this is not very exact science, it should work if you do not have rather special Output Path configurations.
Then I found that running my code in debug mode and accessing the DTE2 COM objects threw the following exception: System.Runtime.InteropServices.COMException: Call was rejected by callee. (Exception from HRESULT: 0x80010001 (RPC_E_CALL_REJECTED))
. There is a remedy for that though, called MessageFilter. I have included the code at the bottom for completeness sake.
Test class containing a test method (usage example), the adjusted GetCurrent
method and a helper method for string comparison:
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using EnvDTE80;
using EnvDTE;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
[TestClass]
public class ProjectSettingsTest
{
/// <summary>
/// Tests that the platform for Mixed Platforms and Any CPU configurations
/// is Any CPU for all projects of this solution
/// </summary>
[TestMethod]
public void TestReleaseBuildIsAnyCPU()
{
MessageFilter.Register();
DTE2 dte2 = GetCurrent();
Assert.IsNotNull(dte2);
foreach (SolutionConfiguration2 config in dte2.Solution.SolutionBuild.SolutionConfigurations)
{
if (config.PlatformName.Contains("Mixed Platforms") || config.PlatformName.Contains("Any CPU"))
{
foreach (SolutionContext context in config.SolutionContexts)
Assert.AreEqual("Any CPU", context.PlatformName, string.Format("{0} is configured {1} in {2}/{3}", context.ProjectName, context.PlatformName, config.PlatformName, config.Name));
}
}
MessageFilter.Revoke();
}
[DllImport("ole32.dll")]
private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);
[DllImport("ole32.dll")]
private static extern void GetRunningObjectTable(int reserved, out IRunningObjectTable prot);
/// <summary>
/// Gets the current visual studio's solution DTE2
/// </summary>
public static DTE2 GetCurrent()
{
List<DTE2> dte2s = new List<DTE2>();
IRunningObjectTable rot;
GetRunningObjectTable(0, out rot);
IEnumMoniker enumMoniker;
rot.EnumRunning(out enumMoniker);
enumMoniker.Reset();
IntPtr fetched = IntPtr.Zero;
IMoniker[] moniker = new IMoniker[1];
while (enumMoniker.Next(1, moniker, fetched) == 0)
{
IBindCtx bindCtx;
CreateBindCtx(0, out bindCtx);
string displayName;
moniker[0].GetDisplayName(bindCtx, null, out displayName);
// add all VisualStudio ROT entries to list
if (displayName.StartsWith("!VisualStudio"))
{
object comObject;
rot.GetObject(moniker[0], out comObject);
dte2s.Add((DTE2)comObject);
}
}
// get path of the executing assembly (assembly that holds this code) - you may need to adapt that to your setup
string thisPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
// compare dte solution paths to find best match
KeyValuePair<DTE2, int> maxMatch = new KeyValuePair<DTE2, int>(null, 0);
foreach (DTE2 dte2 in dte2s)
{
int matching = GetMatchingCharsFromStart(thisPath, dte2.Solution.FullName);
if (matching > maxMatch.Value)
maxMatch = new KeyValuePair<DTE2, int>(dte2, matching);
}
return (DTE2)maxMatch.Key;
}
/// <summary>
/// Gets index of first non-equal char for two strings
/// Not case sensitive.
/// </summary>
private static int GetMatchingCharsFromStart(string a, string b)
{
a = (a ?? string.Empty).ToLower();
b = (b ?? string.Empty).ToLower();
int matching = 0;
for (int i = 0; i < Math.Min(a.Length, b.Length); i++)
{
if (!char.Equals(a[i], b[i]))
break;
matching++;
}
return matching;
}
}
MessageFilter class:
/// <summary>
/// Class containing the IOleMessageFilter
/// thread error-handling functions.
/// </summary>
public class MessageFilter : IOleMessageFilter
{
// 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 0; //Return the flag SERVERCALL_ISHANDLED.
}
// Thread call was rejected, so try again.
int IOleMessageFilter.RetryRejectedCall(System.IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
{
if (dwRejectType == 2)
// flag = SERVERCALL_RETRYLATER.
{
return 99; // Retry the thread call immediately if return >=0 & <100.
}
return -1; // Too busy; cancel call.
}
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);
}