I am developing a WPF app that contains a webbrowser control that loads a silverlight application. I would like to be able to launch the app from visual studio (F5) and have the debugger attach to the silverlight code. However, I've not had any luck with this.
The best I can currently do is to launch the app without attaching, then once it is up and running, attach to the process manually with silverlight as the specified type of code to debug, and this works. (When I cause the web browser control to load the silverlight app, it will hit breakpoints in my silverlight code). I've written some macros to automate this launching/attaching somewhat, but it still isn't the best.
I've tried specifying the WPF app as the external program to run when launching/debugging the silverlight app, but Visual Studio attaches to the process wanting to debug the managed .NET code.
Any ideas? Ideally, I would really like to attach to the process and debug both the managed .NET and the silverlight code, but I don't think this is possible. I'd really like to automatically be attached to the silverlight code at launch so that I can easily debug all issues with the silverlight app, including those that occur on load.
Thanks for your ideas Brandorf and fatty. Brandorf's almost gets me to where I wanted to go, but does require that my SL app be capable of running on its own. I really want to have only the one app, which is both wpf and silverlight, with the SL side being debugged.
A long time after I asked this question (I forgot I had asked it here), I actually pieced together a solution that I'm really happy with. I use visual studio automation within the WPF/.NET side of my app, to find all running instances of visual studio, figure out which one produced my exe (since it typically sits in a folder below the vcproj/sln folder), and then use visual studio automation to have that VS attach to the app, debugging silverlight code. After this is done, I then load my silverlight content.
It works really well. You end up with an app that goes and finds a debugger to attach to itself every time it runs (so you probably want this code only in a debug build, or somehow able to be turned off). So you just launch the app with ctrl-F5 (launch without debugging) from visual studio whenever you want to debug the silverlight side.
Here's my code:
#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
It's a bit of a shot in the dark, but assuming your silverlight app is capable of running on its own, you can, under your solution settings, set visual studio to start both apps together, and you should be attached to both of them.
If you can't add the Silverlight project to your solution (which will start debugging automatically), you might be able to make use of this tip. It will load both projects at the same time
http://saraford.net/2008/07/28/did-you-know-you-can-start-debugging-multiple-projects-268/
Related
I am writing here the same issue posted on Github since I don't see much traffic there recently.
.NET Core Version: 3.1.9 and .Net 5
Windows version: 10.0.18363
Does the bug reproduce also in WPF for .NET Framework 4.8?: AssemblyLoadContext not supported
I am trying to load and unload on demand a Wpf App library (and all the related dependencies). Everything works, but none of the assemblies get unloaded when calling the Unload method.
If I replace the Wpf library with a .Net Core library containing a few sample methods, I can see the library removed from VS Modules window after a couple of GC iterations.
If I'm not wrong I should expect AssemblyLoadContext to load WpfLibrary and related dependencies (PresentationCore, PresentationFramework etc), but WpfLibrary is the only one loaded. All the other dependencies seems be loaded in the default context. May be that I misunderstood how it works, but to me seems that the framework dependencies prevent the unloading.
Also I am not sure if the problem I am reporting is related to this and/or this.
I attached a sample project which is structured like this:
Project 1 (MainApp, a console project with added System.Windows.Forms reference to enable message pump)
class Program
{
class WpfAppAssemblyLoadContext : AssemblyLoadContext
{
public WpfAppAssemblyLoadContext() : base(true) { }
protected override Assembly Load(AssemblyName assemblyName) => null;
}
[MethodImpl(MethodImplOptions.NoInlining),]
public static void TestRun()
{
var context = new WpfAppAssemblyLoadContext();
var assembly = context.LoadFromAssemblyPath($"{Environment.CurrentDirectory}\\WpfLibrary.dll");
var inst = (IProxy) assembly.CreateInstance("WpfLibrary.MainWindow");
inst.ShowWindow();
inst.CloseWindow();
context.Unload();
assembly = null;
context = null;
inst = null;
}
[STAThread,]
static void Main(string[] args)
{
TestRun();
for (var i = 0; i < 100; i++) {
GC.Collect();
GC.WaitForPendingFinalizers();
}
Application.Run();
}
}
Project 2 (ProxyInterface)
namespace ProxyInterface
{
public interface IProxy
{
void ShowWindow();
void CloseWindow();
}
}
Project 3 (a regular wpf library with implementation of interface in Project 2 )
namespace WpfLibrary
{
public partial class MainWindow : Window, IProxy
{
public MainWindow()
{
InitializeComponent();
}
public void ShowWindow() { Show();}
public void CloseWindow() { Close();}
}
}
UnloadWpfLibrary.zip
(Solution file inside "MainApp" folder)
Further updates:
DotNet team added the issue to "Future Milestone", therefore I have to deduce that they recognized this as a bug. I have no idea on when we will see Wpf working with AssemblyLoadContext.
Seems to be that there is a workaround which involve splitting the target assembly into two separate assemblies. I attached the project with the suggested modifications and this time one of the two assemblies is unloaded, but all of the others are still loaded included WpfLibrary.
UnloadWpfLibraryWithWorkaround.zip
I think that for me it's time to give up and recur to IPC (named pipes) although I am not sure if this could be a valid replacement.
May be I missed something and someone more expert can do further progress and attach here the project with the correct modifications, it would be of great benefit for all the users that want to use ALC to load and unload WPF.
It would be a total of 4 projects just to load and unload a wpf assembly on demand and this is not exactly clean, but if the final result is the same it would be acceptable.
I have a very, very large and complex Visual FoxPro 9 application. I'd like to rewrite portions of this application in WPF with the ultimate goal of completing cutting over a couple of years from now. I've spent a considerable amount of time searching google and stackoverflow for an end to end "hello world" type sample of how to implement this with COM interop. Found bits and pieces but nothing complete that shows both the FoxPro side and the WPF side.
My gut tells me I'm likely to run into issues with XAML resources, third party controls, or other normal functionalities of a WPF app if I try to run it via COM Interop.
So, two questions. Can anyone point me to an small end to end sample that either launches WPF windows from FoxPro or lets me drop WPF user controls on a FoxPro form? Or am I right with my concerns of potential interop issues and should avoid this altogether? If so, is there a recommended way for these two executables to communicate with each other?
I have absolutely no idea whether you can run wpf inside visual FoxPro. I would guess you'd have to write it as an activex.
I recently did some work extending a MS Access app.
My wpf app is completely separate and runs alongside the ms access app.
I communicated between the two using MS Message Queue - which is a com component.
Maybe this sort of approach could work for you.
When I wanted to show a screen in wpf from access I added a piece of xml to the local message queue.
The wpf app listens for these messages, casts them to objects which tells it which screen to show and gives it any parameters.
MS Access subscribes to the received message event on the message queue and that way it can tell when the wpf window closes and the user is done.
This was intended to use the strangler pattern to gradually replace functionality in the old access app.
Here's some code for the queuelistener that drives everything. The various pop classes are deserialised from the message and I build that as a string in access.
internal class QueueListener
{
public const string qFromMadge = #".\private$\XXXXQ";
public static string qToMadge = #".\private$\XXXXReturnQ";
private MessageQueue mq;
internal QueueListener()
{
try
{
SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
SubscribeHandler();
var mqSend = setUpQueue(qToMadge);
mqSend.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.InnerException.Message);
}
}
private MessageQueue setUpQueue(string qName)
{
MessageQueue q;
if (MessageQueue.Exists(qName))
{
q = new MessageQueue(qName);
}
else
{
q = MessageQueue.Create(qName);
}
q.ResetPermissions();
q.SetPermissions("Everyone", MessageQueueAccessRights.FullControl, AccessControlEntryType.Set);
q.Purge();
return q;
}
private void ReceiveMsgCompleted(Object source, ReceiveCompletedEventArgs asyncResult)
{
MessageQueue mq = (MessageQueue)source;
Message m = mq.EndReceive(asyncResult.AsyncResult);
m.Formatter = new XmlMessageFormatter(new[] { "XXXX.Popaaaa, XXXX"
, "XXXX.Popbbbb, XXXXX"
, "XXXX.Popcccc, XXXX"
, "XXXX.Popdddd, XXXX"
});
((BasePopView) m.Body).Pop();
mq.BeginReceive();
}
public void UnsubscribeHandler()
{
if (mq == null)
{
return;
}
mq.Close();
mq.ReceiveCompleted -= new ReceiveCompletedEventHandler(ReceiveMsgCompleted);
mq = null;
}
public void SubscribeHandler()
{
if(mq != null)
{
return;
}
mq = setUpQueue(qFromMadge);
mq.ReceiveCompleted += ReceiveMsgCompleted;
mq.BeginReceive();
}
I'm building a custom control for Windows Phone 7+ that can do augmented reality image processing. The control works wonderfully in practice (when I run the app), but because I have the image processing running on a separate thread, it breaks when I try to open the page in Blend or the Visual Studio designer.
Here's an example of the thread I'm trying to run (basically taken from http://msdn.microsoft.com/en-us/library/hh202982(v=vs.92).aspx) :
public override void OnApplyTemplate()
{
// assigning template stuff, initializing my camera
_myManualResetEvent = new ManualResetEvent(true);
_myCameraProcessingThread = new System.Threading.Thread(ProcessingMethod);
_myCameraProcessingThread.Start();
}
void ProcessingMethod()
{
int[] myBuffer = new int[640 * 480];
while(_someCondition)
{
_myManualResetEvent.WaitOne();
_myCamera.GetPreviewBufferArgb32(myBuffer);
// do my processing stuff
_myManualResetEvent.Set();
}
}
This breaks the ever-loving heck out of Blend. Would love to know why.
It looks like you are doing a lot of run-time stuff in the OnApplyTemplate method.
This will get called when Blend or Visual Studio instantiates the design view of your control.
You should either check to see if you are in design mode using the DesignMode:
if (!DesignMode)
{
_myManualResetEvent = new ManualResetEvent(true);
_myCameraProcessingThread = new System.Threading.Thread(ProcessingMethod);
_myCameraProcessingThread.Start();
}
or move this code into a method/event handler that only gets called when the application actually runs.
How do I use the word editor in a WPF application? Is it possible using windows forms hosting in WPF only? Is there another way to accomplish that?
I found AvalonEdit but it does not have features that I need. So using this way, my problem may not be solved.
Also there is some stuffs out there to host a windows forms control in WPF, but it could not be my answer.
I want to understand that is there a way to use word editor in a native way in a wpf app?
Will all APIs be available in that solution?
Thanks in advance.
You can host MS Word (2007/2010 and probably other versions) from within a WebBrowser control, this works in WinForms and should work in WPF too. A .NET API is provided for automating Word, documented here. The required interop assemblies ship with Office 2010, so deployment is a lot simpler than previous Office versions.
See this Microsoft Support article for more details on hosting Word within a WebBrowser control. The Screenshot below shows Word embedded within a host Winforms application.
Note that this only works reliably for a single hosted instance of Word, so you can't show 2 Word documents side by side in the same application. Also, the Ribbon can sometimes go missing - but Word hasn't ever caused the application to crash.
Administrative rights are required to make the required registry updates as there are potential security issues. One easy method to make the registry updates is to write a script, but the following (revised/untested) code shows how this can be done in c# for Word, Excel and PowerPoint:
using System.Security.AccessControl;
private Dictionary<string,uint> OfficeBrowserRegKeys()
{
string[] officeRegKeyArray = new string[]
{
#"SOFTWARE\Classes\Word.Document.12",
#"SOFTWARE\Classes\Word.DocumentMacroEnabled.12",
#"SOFTWARE\Classes\Excel.Sheet.12",
#"SOFTWARE\Classes\Excel.SheetMacroEnabled.12",
#"SOFTWARE\Classes\Excel.SheetBinaryMacroEnabled.12",
#"SOFTWARE\Classes\PowerPoint.Show.12",
#"SOFTWARE\Classes\PowerPoint.ShowMacroEnabled.12",
#"SOFTWARE\Classes\PowerPoint.SlideShow.12",
#"SOFTWARE\Classes\PowerPoint.SlideShowMacroEnabled.12"
};
Dictionary<string,uint> officeRegKeys = new Dictionary<string, uint>();
uint wrdVal = 0x80000024;
uint excelVal = 0x80000A00;
uint powerPtVal = 0x800000A0;
foreach(string keyName in officeRegKeyArray)
{
if (keyName.Contains("Word"))
{
officeRegKeys.Add(keyName, wrdVal);
}
else if (keyName.Contains("Excel"))
{
officeRegKeys.Add(keyName, excelVal);
}
else
{
officeRegKeys.Add(keyName, powerPtVal);
}
}
return officeRegKeys;
}
private void setNewOfficeKeys()
{
uint editFlag = 0x00010000;
Dictionary<string,uint> officeRegKeys = OfficeBrowserRegKeys();
foreach (KeyValuePair<string, uint> kvp in officeRegKeys)
{
try
{
RegistryKey rKey = Registry.LocalMachine.OpenSubKey(kvp.Key,
RegistryKeyPermissionCheck.ReadWriteSubTree,
System.Security.AccessControl.RegistryRights.SetValue);
rKey.SetValue("BrowserFlags", unchecked((int)kvp.Value),
RegistryValueKind.DWord);
rKey.SetValue("EditFlags", unchecked((int)editFlag),
RegistryValueKind.DWord);
}
catch (Exception e) { string msg = e.Message; }
}
}
Well, Word proper isn't technically designed to be hosted by another app, whether it's WPF, WINFORMS or anything else.
You CAN use api tricks (like SetParent) to move the Main Word window into a WPF hosted window. I've done it before, but it's pretty tricky business and it's very easy to miss things that cause GPFs (both in Word and your app).
Is there any reason why it needs to be "Word in your app"? Why not write a little word addin and then launch Word from your app when necessary. then the Addin can communicate with your app, or your DB or whatever as necessary from within Word.
Users may find that to be a more usable approach in any case.
I know that it is possible to load xap modules dynamically using Prism or MEF framework. However, I'd like not to use those frameworks; instead load my xap files manually. So, I created the following class (adapted from internet):
public class XapLoader
{
public event XapLoadedEventHandler Completed;
private string _xapName;
public XapLoader(string xapName)
{
if (string.IsNullOrWhiteSpace(xapName))
throw new ArgumentException("Invalid module name!");
else
_xapName = xapName;
}
public void Begin()
{
Uri uri = new Uri(_xapName, UriKind.Relative);
if (uri != null)
{
WebClient wc = new WebClient();
wc.OpenReadCompleted += onXapLoadingResponse;
wc.OpenReadAsync(uri);
}
}
private void onXapLoadingResponse(object sender, OpenReadCompletedEventArgs e)
{
if ((e.Error == null) && (e.Cancelled == false))
initXap(e.Result);
if (Completed != null)
{
XapLoadedEventArgs args = new XapLoadedEventArgs();
args.Error = e.Error;
args.Cancelled = e.Cancelled;
Completed(this, args);
}
}
private void initXap(Stream stream)
{
string appManifest = new StreamReader(Application.GetResourceStream(
new StreamResourceInfo(stream, null), new Uri("AppManifest.xaml",
UriKind.Relative)).Stream).ReadToEnd();
XElement deploy = XDocument.Parse(appManifest).Root;
List<XElement> parts = (from assemblyParts in deploy.Elements().Elements()
select assemblyParts).ToList();
foreach (XElement xe in parts)
{
string source = xe.Attribute("Source").Value;
AssemblyPart asmPart = new AssemblyPart();
StreamResourceInfo streamInfo = Application.GetResourceStream(
new StreamResourceInfo(stream, "application/binary"),
new Uri(source, UriKind.Relative));
asmPart.Load(streamInfo.Stream);
}
}
}
public delegate void XapLoadedEventHandler(object sender, XapLoadedEventArgs e);
public class XapLoadedEventArgs : EventArgs
{
public Exception Error { get; set; }
public bool Cancelled { get; set; }
}
The above code works fine; I can load any xap the following way:
XapLoader xapLoader = new XapLoader("Sales.xap");
xapLoader.Completed += new XapLoadedEventHandler(xapLoader_Completed);
xapLoader.Begin();
Now, I have a UserControl called InvoiceView in the Sales.xap project, so I would like to instantiate the class. In the current project (Main.xap) I added reference to Sales.xap project, however, since I load it manually I set "Copy Local = False". But when executed, the following code throws TypeLoadException:
Sales.InvoiceView view = new Sales.InvoiceView();
It seems the code can't find InvoiceView class. But I checked that XapLoader's initXap() method was successfully executed. So why the code can't find InvoiceView class? Can someone help me with this problem?
This is based on the asker's self-answer below, rather than the question.
If you delete a project/module the output DLLs/XAP files do hang around. If you click the "show all files" button you will see some these left-over output files in your clientbin, bin and obj folders of related projects.
You can delete them individually from the project, or, when in doubt, search for all BIN and OBJ (e.g. using desktop explorer) and delete all those folders. The BIN/CLIENTBIN/OBJ folders will be recreated when needed (this the job that the "clean" option in Visual Studio should have done!)
Hope this helps.
Ok, I found the cause. The above code works. After creating a new silverlight project (Sales.xap) I happened to compile my solution once. Then I deleted App class in the Sales.xap and renamed default MainPage class to SalesView. However, no matter how many times I compile my solution, Visual Studio's development web server was loading the first version of Sales.xap (where from?), so my code couldn't find SalesView. In my host Asp.Net project I set development server's port to a different port number, and the problem gone. So the problem was with Visual Studio's development server. Apparently it is keeping compiled xap files in some temporary folder, and doesn't always update those xap files when source code changed.
What I do to avoid such problems when executing freshly compiled Silverlight is clear the browser cache, chrome even has a clear silverlight cache ;)
this XAP Cache phenomena is often due to the visual studio embedded web server (ASP.NET Development Server).
Just stop the occurence of this server and the cache will be cleared.
Start again your project and the latest build of your xap is called.