我之前问过:Add dll reference to visual studio macros
用我的语言(C#)创建宏的想法使得创建宏更容易。 问题是我无法调试dll
要解决我尝试过的问题:
我将myClassLibrary.pdb
放在myClassLibrary.dll
旁边,希望我可以通过逐步调试dll中的方法来调试它们。
创建了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宏具有的onces:
答案 0 :(得分:5)
最后这是解决方案:
在以下步骤中,我将描述如何调试将由宏执行的dll。
(注意我在视觉工作室上调试了一个关于c#的宏!)
在visual studio
现在为该解决方案添加一个新的类库Project。 (这是将执行宏的类)
添加参考文献EnvDTE,EbvDTE100,EnvDTE80,EnvDTE90,EnvDTE90a。基本上与visual studio宏具有相同的参考:
创建一个方法,执行您计划在类库中使用的宏。
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!!!");
}
}
}
添加另一个项目(Visual Studio加载项)
按照wizzard保留默认值,第4页除外:
继续选择向导上的默认选项,直到创建项目:
将该项目设置为启动项目,以便当我们按f5时,插件运行。
将MyAddin1的引用添加到类库
一旦我们有了这个引用,我们应该能够从addin执行宏。要执行此操作,请打开Connect.cs
并导航到方法Exec
添加ClassLibrary1.Class1.Macro1(_applicationObject);
,如下所示:
在Exec方法的开头添加一个断点,以便我们进行调试。
按F5
执行MyAddin1。应该打开一个新的visual studio实例。
在Visual Studio的新实例上打开任何解决方案。在这种情况下,我再次打开相同的解决方案>
获取工具然后单击MyAddin1但确保文档已打开:
点击我的插件后,你应该点击断点!
ClassLibrary1.Class1.Macro1(_applicationObject);
所以我在那条线上发表评论并放在那条线上:
var textDoc = (TextDocument)(_applicationObject.ActiveDocument.Object(string.Empty));
textDoc.Selection.Insert("Hello world");
最后当我点击工具上的MyAddin1时,会插入Hello world!
一旦我知道宏运行正常,我可以将类导出到类库并让宏调用dll上的方法而不是插件。
答案 1 :(得分:2)
我还有一个更好的答案!
我创建插件的唯一原因是因为我需要DTE的参考。为什么不参考我需要的dte。
算法如下:
使用类Ide
获取visual studio的任何实例的DTE。
一旦你有了这个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;
}
}
}
}
}
然后,如果我有一个包含名为ConsoleApp1
的解决方案的visual studio runing实例,那么我将能够做到:
var dte = Ide.GetDte("ConsoleApp1");
dte.ActiveDocument.Selection.Insert("My macro is working!");
,文本My macro is working!
将插入活动文档中。确保有活跃的文件
答案 2 :(得分:0)
远野,
您是否为wcf应用程序引用了您的dll并将其作为调试版本?
来自:http://msdn.microsoft.com/en-us/library/ms164704.aspx:
无论您如何开始调试,请确保构建Debug 首先是DLL版本,并确保调试版本在 应用程序期望找到它的位置。这可能看起来像 很明显,但是如果你忘了这一步,应用程序可能会找到一个 不同版本的DLL并加载它。然后该程序将 继续运行,而你想知道为什么你的断点从未被击中。 在调试时,您可以验证程序具有哪些DLL 通过打开调试器的模块窗口加载。模块窗口 列出正在调试的进程中加载的每个DLL或EXE。对于 有关更多信息,请参见如何:使用模块窗口。
如果您尝试调试引用的dll,这篇SO文章可能对此有所帮助:How to debug a referenced dll (having pdb)
〜JOL
答案 3 :(得分:0)
使用其他发布商的DLL很容易。但是理解代码很重要。 我用IlSpy。它是免费的并且可以使用。 它反编译dll以查看所有方法,类和命名空间。
答案 4 :(得分:0)
调试visual studio加载项的方法是打开另一个可视工作室实例并附加到将激活加载项的实例。 它被描述为here。还要确保加载项是在本地计算机上构建的,并且pdbs可用,否则它将不会到达您的断点。