9

我之前问过: 添加对 Visual Studio 宏的 dll 引用

用我的语言 (C#) 创建宏的想法使创建宏变得更加容易。问题是我无法调试 dll

为了解决我尝试过的问题:

  1. 我放在myClassLibrary.pdb旁边,myClassLibrary.dll希望我能够通过介入来调试 dll 中的方法。

  2. 创建了 WCF 服务。因为我不知道如何从 vba 引用服务,所以我从类库中引用它。问题是我需要使用诸如DTE.ActiveDocument这些变量不可序列化的变量,这意味着我无法将它们传递给 wcf 服务。

在 C# 中工作的想法非常好,但无法调试并查看正在发生的事情使得它有些困难。我可能不得不去我的旧选项,在那里我在 C# 上创建我的代码,然后用反射器反编译成 vba。


编辑

我想我快要找到解决方案了。我想为什么不在控制台应用程序中创建宏?我能够获取活动文档文本,但无法更改它。

        EnvDTE80.DTE2 MyDte;
        MyDte = (EnvDTE80.DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject( "VisualStudio.DTE.10.0" );
        Console.WriteLine( "The Edition is " + MyDte.Edition );

        Console.ReadLine( );

        // write to the console the text that is selected. "sometimes it does not work don't know why"
        Console.WriteLine(
            MyDte.ActiveDocument.Selection.Text
        );

请注意,我添加了以下引用以及 vba 宏具有的一次:

在此处输入图像描述

4

5 回答 5

5

最后是解决方案:

在以下步骤中,我将描述如何调试将由宏执行的 dll。

如果您希望能够执行以下操作:

在此处输入图像描述 (注意我在 Visual Studio 上调试 c# 上的宏!!!)

  1. 在 Visual Studio 中创建新的解决方案在此处输入图像描述

  2. 现在向该解决方案添加一个新的类库项目。(这是将执行宏的类)在此处输入图像描述

  3. 添加参考 EnvDTE、EbvDTE100、EnvDTE80、EnvDTE90、EnvDTE90a。基本上与 Visual Studio 宏具有相同的引用:在此处输入图像描述

  4. 创建一个方法来执行您计划在类库上使用的宏。

       namespace ClassLibrary1
       {
           public static class Class1
           {
               public static void Macro1(EnvDTE80.DTE2 DTE)
               {
                   // make sure an active text document is open before calling this method
                   DTE.ActiveDocument.Selection.Insert("Hello World!!!");
               }
           }
       }
    
  5. 添加另一个项目(Visual Studio 加载项)在此处输入图像描述

  6. 按照向导保留默认设置,除了第 4 页选择: 在此处输入图像描述

  7. 继续选择向导上的默认选项,直到创建项目: 在此处输入图像描述

  8. 将该项目设置为启动项目,以便当我们按 f5 时插件运行。

  9. 添加从 MyAddin1 到类库的引用

  10. 一旦我们有了那个引用,我们应该能够从插件执行宏。为了做到这一点,打开Connect.cs并导航到方法Exec添加ClassLibrary1.Class1.Macro1(_applicationObject);,它看起来像: 在此处输入图像描述

  11. 在 Exec 方法的开头添加一个断点,以便我们对其进行调试。

  12. 按 执行 MyAddin1 F5。应该打开一个新的视觉工作室实例。

  13. 在 Visual Studio 的新实例上打开任何解决方案。在这种情况下,我再次打开相同的解决方案>

  14. 转到工具,然后单击 MyAddin1 但确保文档已打开: 在此处输入图像描述

  15. 一旦你点击了我的插件,你应该点击断点! 在此处输入图像描述

15. 注意!出于某种原因,我不得不评论这条线ClassLibrary1.Class1.Macro1(_applicationObject);

所以我注释掉那一行并在那一行上我放置:

 var textDoc = (TextDocument)(_applicationObject.ActiveDocument.Object(string.Empty));
 textDoc.Selection.Insert("Hello world");

最后,当我单击位于工具上的 MyAddin1 时,Hello world 将被插入!


一旦我知道宏运行良好,我可以将类导出到类库并让宏调用 dll 上的方法而不是插件。

于 2012-07-28T17:08:47.590 回答
2

我还有另一个更好的答案!

我创建插件的唯一原因是因为我需要 DTE 的引用。为什么不引用我需要的 dte。

算法如下:

  1. 使用类Ide来获取任何 Visual Studio 实例的 DTE。

  2. 拥有该 dte 后,请创建宏。

这是 Ide 类:

public class Ide
{        
    [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);

    public static DTE2 GetDte(string solutionName)
    {
        DTE2 dte = null;

        GetDte((displayName, x) =>
        {
            if (System.IO.Path.GetFileName(x.Solution.FullName).Contains(solutionName))
            {
                dte = x;
                return false; // we found it stop seraching
            }
            else
            {
                return true; // continue searching
            }

        });

        return dte;
    }

    public static DTE2 GetDte(int processId)
    {
        DTE2 dte = null;

        GetDte((displayName, x) =>
        {
            if (displayName.Contains(processId.ToString()))
            {
                dte = x;
                return false; // stop searching we found matching dte
            }
            else
            {
                return true; // continue searching
            }
        });

        return dte;
    }

    public static List<DTE2> GetAllDte()
    {
        List<DTE2> list = new List<DTE2>();
        GetDte((displayName, x) =>
        {
            list.Add(x);
            return true; // continue serching we want all dte's
        });
        return list;
    }

    private static void GetDte(Func<string, DTE2, bool> foo)
    {
        Dictionary<string, string> dtesProcessIds = new Dictionary<string, string>();

        //rot entry for visual studio running under current process.            
        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);
            object comObject;
            rot.GetObject(moniker[0], out comObject);

            if (comObject != null)
            {
                DTE2 dteCurrent = null;
                try
                {
                    dteCurrent = (EnvDTE80.DTE2)comObject;

                    // if solution is not open continue
                    // this will cause an exception if it is not open
                    var temp = dteCurrent.Solution.IsOpen;

                    string solName = dteCurrent.Solution.FullName;

                    // if there is an instance of visual studio with no solution open continue                        
                    if (string.IsNullOrEmpty(solName))
                    {
                        continue;
                    }

                    // avoid adding duplicate ide's
                    if (dtesProcessIds.ContainsKey(displayName) == false)
                    {
                        dtesProcessIds.Add(displayName, displayName);
                    }
                    else
                    {
                        continue;
                    }

                }
                catch (System.Runtime.InteropServices.COMException e)
                {
                    continue;
                }
                catch (Exception e)
                {
                    continue;
                }
                if (dteCurrent != null)
                {
                    var cont = foo(displayName, dteCurrent);

                    if (cont == false)
                        return;
                }
            }

        }
    }
}

然后,如果我有一个正在运行的 Visual Studio 实例,其中包含一个名称为解决方案的解决方案,ConsoleApp1那么我将能够执行以下操作:

 var dte = Ide.GetDte("ConsoleApp1");
 dte.ActiveDocument.Selection.Insert("My macro is working!");

并且文本My macro is working!将被插入到活动文档中。确保有一个活动文档

于 2012-07-29T21:23:22.280 回答
0

调试 Visual Studio 加载项的方法是打开另一个 Visual Studio 实例并附加到将激活您的加载项的实例。此处对其进行了描述。还要确保加载项是在本地计算机上构建的并且 pdb 可用,否则它不会击中您的断点。

于 2012-08-03T22:56:36.487 回答
0

使用来自其他发布者的 DLL 很容易。但是理解代码很重要。我使用 IlSpy。它是免费的并且可以使用。它反编译 dll 以查看所有方法、类和命名空间。

于 2012-07-31T10:11:54.690 回答
0

远野,

您是否为 wcf 应用程序引用了您的 dll 并使其成为调试版本?

来自: http: //msdn.microsoft.com/en-us/library/ms164704.aspx

无论您如何开始调试,请确保首先构建 DLL 的 Debug 版本,并确保 Debug 版本位于应用程序期望找到它的位置。这似乎很明显,但如果您忘记了这一步,应用程序可能会找到不同版本的 DLL 并加载它。然后程序将继续运行,而您想知道为什么您的断点从未被命中。在调试时,您可以通过打开调试器的“模块”窗口来验证您的程序加载了哪些 DLL。“模块”窗口列出了您正在调试的进程中加载​​的每个 DLL 或 EXE。有关更多信息,请参阅如何:使用模块窗口。


如果您尝试调试引用的 dll,这篇 SO 文章可能会在这里提供帮助:如何调试引用的 dll(具有 pdb)

~乔尔

于 2012-07-25T19:20:50.523 回答