/// Execute is part of the Microsoft.Build.Framework.ITask interface. /// When it's called, any input parameters have already been set on the task's properties. /// It returns true or false to indicate success or failure. /// public override bool Execute() { try { Log.LogMessage(MessageImportance.Normal, "Executing Visual Studio command: " + CommandName + " " + CommandArguments); int devenvPid = GetParentDevenvProcessId(Process.GetCurrentProcess()); // Register the IOleMessageFilter to handle any threading errors. // See https://msdn.microsoft.com/en-us/library/ms228772 MessageFilter.Register(); EnvDTE.DTE dte = GetDTE(devenvPid); if (dte == null) { Log.LogError("Error: Couldn't get the current Visual Studio automation object."); } if (CommandArguments == null) { Log.LogMessage(MessageImportance.Normal, "No command arguments supplied"); dte.ExecuteCommand(CommandName); } else { dte.ExecuteCommand(CommandName, CommandArguments); } Log.LogMessage(MessageImportance.Normal, "The command was successfully executed."); } catch (Exception ex) { Log.LogError("Command " + CommandName + ". " + ex.Message); } finally { // turn off the IOleMessageFilter. MessageFilter.Revoke(); } // Log.HasLoggedErrors is true if the task logged any errors -- even if they were logged // from a task's constructor or property setter. As long as this task is written to always log an error // when it fails, we can reliably return HasLoggedErrors. return !Log.HasLoggedErrors; } private int GetParentDevenvProcessId(Process proc) { try { using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Process WHERE ProcessId = " + proc.Id)) { foreach (ManagementObject obj in searcher.Get()) { int parentId = Convert.ToInt32((UInt32)obj["ParentProcessId"]); if (Process.GetProcessById(parentId).ProcessName.ToLower().Contains("devenv") ) { return parentId; } } } } catch (Exception ex) { } return -1; } [DllImport("ole32.dll")] private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc); public static EnvDTE.DTE GetDTE(int processId) { string progId = "!VisualStudio.DTE.10.0:" + processId.ToString(); object runningObject = null; IBindCtx bindCtx = null; IRunningObjectTable rot = null; IEnumMoniker enumMonikers = null; try { Marshal.ThrowExceptionForHR(CreateBindCtx(reserved: 0, ppbc: out bindCtx)); bindCtx.GetRunningObjectTable(out rot); rot.EnumRunning(out enumMonikers); IMoniker[] moniker = new IMoniker[1]; IntPtr numberFetched = IntPtr.Zero; while (enumMonikers.Next(1, moniker, numberFetched) == 0) { IMoniker runningObjectMoniker = moniker[0]; string name = null; try { if (runningObjectMoniker != null) { runningObjectMoniker.GetDisplayName(bindCtx, null, out name); } } catch (UnauthorizedAccessException) { // Do nothing, there is something in the ROT that we do not have access to. } Regex monikerRegex = new Regex(@"!VisualStudio.DTE\.\d+\.\d+\:" + processId, RegexOptions.IgnoreCase); if (!string.IsNullOrEmpty(name) && monikerRegex.IsMatch(name)) { Marshal.ThrowExceptionForHR(rot.GetObject(runningObjectMoniker, out runningObject)); break; } } } finally { if (enumMonikers != null) { Marshal.ReleaseComObject(enumMonikers); } if (rot != null) { Marshal.ReleaseComObject(rot); } if (bindCtx != null) { Marshal.ReleaseComObject(bindCtx); } } return runningObject as EnvDTE.DTE; } /// /// See https://msdn.microsoft.com/en-us/library/ms228772 /// 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); } } ]]>