Отладка silverlight в приложении WPF
Я разрабатываю приложение WPF, которое содержит веб-браузер, который загружает приложение silverlight. Я хотел бы иметь возможность запустить приложение из Visual Studio (F5) и подключить отладчик к коду Silverlight. Однако мне не повезло с этим.
Лучшее, что я могу сейчас сделать - это запустить приложение без подключения, затем, как только оно будет запущено и запущено, подключить к процессу вручную с помощью silverlight в качестве указанного типа кода для отладки, и это работает. (Когда я заставляю веб-браузер загружать приложение silverlight, оно попадает в точки останова в моем коде silverlight). Я написал несколько макросов, чтобы несколько автоматизировать этот запуск / присоединение, но он все еще не самый лучший.
Я попытался указать приложение WPF в качестве внешней программы, запускаемой при запуске / отладке приложения silverlight, но Visual Studio подключается к процессу, желающему отладить управляемый код.NET.
Есть идеи? В идеале мне бы очень хотелось присоединиться к процессу и отладить как управляемый.NET, так и код silverlight, но я не думаю, что это возможно. Я бы очень хотел, чтобы при запуске автоматически подключался к коду silverlight, чтобы можно было легко отлаживать все проблемы с приложением silverlight, включая проблемы, возникающие при загрузке.
3 ответа
Спасибо за идеи Брандорфа и Толстяка. Брандорф почти возвращает меня туда, куда я хотел, но требует, чтобы мое приложение SL могло работать самостоятельно. Я действительно хочу иметь только одно приложение - wpf и silverlight с отлаживаемой стороной SL.
Спустя долгое время после того, как я задал этот вопрос (я забыл, что задал этот вопрос здесь), я фактически собрал решение, которым я действительно доволен. Я использую Visual Studio Automation в WPF/.NET-стороне моего приложения, чтобы найти все работающие экземпляры Visual Studio, выяснить, какой из них создал мой exe-файл (поскольку он обычно находится в папке ниже папки vcproj/sln), а затем используйте Visual Studio Automation, чтобы подключить VS к приложению, отлаживая код Silverlight. После этого я загружаю содержимое Silverlight.
Это работает очень хорошо. В итоге вы получаете приложение, которое находит и находит отладчик, который присоединяется к себе каждый раз, когда он запускается (поэтому вы, вероятно, хотите, чтобы этот код был только в отладочной сборке, или каким-то образом можно было его отключить). Таким образом, вы просто запускаете приложение с помощью Ctrl-F5 (запуск без отладки) из Visual Studio всякий раз, когда вы хотите отладить сторону Silverlight.
Вот мой код:
#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
Если вы не можете добавить проект Silverlight в свое решение (которое автоматически начнет отладку), вы можете использовать этот совет. Он загрузит оба проекта одновременно
http://saraford.net/2008/07/28/did-you-know-you-can-start-debugging-multiple-projects-268/
Это немного похоже на выстрел в темноте, но если ваше приложение Silverlight способно работать самостоятельно, вы можете в настройках своего решения настроить Visual Studio для запуска обоих приложений вместе, и вы должны быть подключены к ним обоим.