如何从外部64位进程自动化Visual Studio?

时间:2016-12-14 22:23:35

标签: c# visual-studio automation 64-bit

我想从外部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;
    }

有没有人有想法?

0 个答案:

没有答案