感谢您的想法 Brandorf 和 fat。 Brandorf 几乎可以将我带到我想去的地方,但确实要求我的 SL 应用程序能够独立运行。我真的只想拥有一个应用程序,即 wpf 和 silverlight,并且调试 SL 端。
在我问了这个问题很久之后(我忘了我在这里问过),我实际上拼凑了一个我非常满意的解决方案。我在我的应用程序的 WPF/.NET 端使用 Visual Studio 自动化,查找所有正在运行的 Visual Studio 实例,找出哪个生成了我的 exe(因为它通常位于 vcproj/sln 文件夹下方的文件夹中),然后使用 Visual Studio 自动化将 VS 附加到应用程序,调试 silverlight 代码。完成此操作后,我将加载我的 silverlight 内容。
效果非常好。您最终会得到一个应用程序,该应用程序每次运行时都会找到一个调试器来附加到自身(因此您可能希望此代码仅在调试版本中,或者以某种方式能够被关闭)。因此,只要您想调试 silverlight 端,您只需从 Visual Studio 中使用 ctrl-F5(无需调试即可启动)启动应用程序。
这是我的代码:
#if DEBUG
using System;
using System.Collections.Generic;
using System.Collections;
using System.Runtime.InteropServices;
using System.IO;
namespace Launcher
{
//The core methods in this class to find all running instances of VS are
//taken/inspired from
//http://www.codeproject.com/KB/cs/automatingvisualstudio.aspx
class DebuggingAutomation
{
[DllImport("ole32.dll")]
private static extern int GetRunningObjectTable(int reserved,
out UCOMIRunningObjectTable prot);
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(int reserved,
out UCOMIBindCtx ppbc);
///<summary>
///Get a snapshot of the running object table (ROT).
///</summary>
///<returns>
///A hashtable mapping the name of the object
///in the ROT to the corresponding object
///</returns>
private static Hashtable GetRunningObjectTable()
{
Hashtable result = new Hashtable();
int numFetched;
UCOMIRunningObjectTable runningObjectTable;
UCOMIEnumMoniker monikerEnumerator;
UCOMIMoniker[] monikers = new UCOMIMoniker[1];
GetRunningObjectTable(0, out runningObjectTable);
runningObjectTable.EnumRunning(out monikerEnumerator);
monikerEnumerator.Reset();
while (monikerEnumerator.Next(1, monikers, out numFetched) == 0)
{
UCOMIBindCtx ctx;
CreateBindCtx(0, out ctx);
string runningObjectName;
monikers[0].GetDisplayName(ctx, null, out runningObjectName);
object runningObjectVal;
runningObjectTable.GetObject(monikers[0], out runningObjectVal);
result[runningObjectName] = runningObjectVal;
}
return result;
}
/// <summary>
/// Get a table of the currently running instances of the Visual Studio .NET IDE.
/// </summary>
/// <param name="openSolutionsOnly">
/// Only return instances that have opened a solution
/// </param>
/// <returns>
/// A list of the ides (as DTE objects) present in
/// in the running object table to the corresponding DTE object
/// </returns>
private static List<EnvDTE.DTE> GetIDEInstances(bool openSolutionsOnly)
{
var runningIDEInstances = new List<EnvDTE.DTE>();
Hashtable runningObjects = GetRunningObjectTable();
IDictionaryEnumerator rotEnumerator = runningObjects.GetEnumerator();
while (rotEnumerator.MoveNext())
{
string candidateName = (string)rotEnumerator.Key;
if (!candidateName.StartsWith("!VisualStudio.DTE"))
continue;
EnvDTE.DTE ide = rotEnumerator.Value as EnvDTE.DTE;
if (ide == null)
continue;
if (openSolutionsOnly)
{
try
{
string solutionFile = ide.Solution.FullName;
if (!String.IsNullOrEmpty(solutionFile))
{
runningIDEInstances.Add(ide);
}
}
catch { }
}
else
{
runningIDEInstances.Add(ide);
}
}
return runningIDEInstances;
}
internal static void AttachDebuggerIfPossible()
{
if (System.Diagnostics.Debugger.IsAttached)
{
//Probably debugging host (Desktop .NET side), so don't try to attach to silverlight side
return;
}
var ides = GetIDEInstances(true);
var fullPathToAssembly = System.Reflection.Assembly.GetExecutingAssembly().Location;
var potentials = new List<EnvDTE.DTE>();
foreach (var ide in ides)
{
var solutionPath = ide.Solution.FullName;
var topLevelSolutionDir = Path.GetDirectoryName(solutionPath);
var assemblyName = fullPathToAssembly;
if (assemblyName.StartsWith(topLevelSolutionDir, StringComparison.OrdinalIgnoreCase))
{
potentials.Add(ide);
}
}
EnvDTE.DTE chosenIde = null;
//If you have multiple ides open that can match your exe, you can come up with a scheme to pick a particular one
//(eg, put a file like solution.sln.pickme next to the solution whose ide you want to debug). If this is not a
//concern, just pick the first match.
if (potentials.Count > 0)
{
chosenIde = potentials[0];
}
var dbg = chosenIde != null ? (EnvDTE80.Debugger2)chosenIde.Debugger : null;
if (dbg != null)
{
var trans = dbg.Transports.Item("Default");
var proc = (EnvDTE80.Process2)dbg.GetProcesses(trans, System.Environment.MachineName).Item(Path.GetFileName(fullPathToAssembly));
var engines = new EnvDTE80.Engine[1];
engines[0] = trans.Engines.Item("Silverlight");
proc.Attach2(engines);
}
}
}
}
#endif