我想从外部C#程序自动化Visual Studio。我按照here的说明获取给定processId的DTE对象。然后我按照here的说明从DTE对象获得ServiceProvider和IVsActivityLog。
一切正常x86
,但我在构建为x64
时遇到了麻烦(对于Any CPU
,只有当它作为一部分运行时,它才有效一个32位进程)。在状态栏中设置文本是有效的,但我在转换为IVsActivityLog
:
Unhandled Exception: System.InvalidCastException: Unable to cast COM object of type 'System.__ComObject' to interface type 'Microsoft.VisualStudio.Shell.Interop.IVsActivityLog'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{76AF73F9-A322-42B0-A515-D4D7553508FE}' failed due to the following error: Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).
这是一个简化的例子:
static void Main(string[] args)
{
int processID = 42; // well... replace by the correct PID
_DTE dte = GetDTE(processID);
dte.StatusBar.Text = $"Hello World!";
var serviceProvider = new ServiceProvider((Microsoft.VisualStudio.OLE.Interop.IServiceProvider)dte.DTE);
var log = (IVsActivityLog)serviceProvider.GetService(typeof(SVsActivityLog));
log.LogEntry((uint)__ACTIVITYLOG_ENTRYTYPE.ALE_WARNING, typeof(Program).ToString(), "Test");
}
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);
[DllImport("ole32.dll")]
private static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot);
public static _DTE GetDTE(int processId)
{
IntPtr numFetched = IntPtr.Zero;
IRunningObjectTable runningObjectTable;
IEnumMoniker monikerEnumerator;
IMoniker[] monikers = new IMoniker[1];
GetRunningObjectTable(0, out runningObjectTable);
runningObjectTable.EnumRunning(out monikerEnumerator);
monikerEnumerator.Reset();
while (monikerEnumerator.Next(1, monikers, numFetched) == 0)
{
IBindCtx ctx;
CreateBindCtx(0, out ctx);
string runningObjectName;
monikers[0].GetDisplayName(ctx, null, out runningObjectName);
object runningObjectVal;
runningObjectTable.GetObject(monikers[0], out runningObjectVal);
if (runningObjectVal is _DTE && runningObjectName.StartsWith("!VisualStudio"))
{
int currentProcessId = int.Parse(runningObjectName.Split(':')[1]);
if (currentProcessId == processId)
{
return (_DTE)runningObjectVal;
}
}
}
return null;
}
有没有人有想法?