Execute a Method within WPF Application using .Net Standard Project - wpf

My .Net standard project is calling a method within a WPF application using reflection.
Method Calling Code in my .NET standard Project...
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Google.Cast.DesktopV2.exe");
var asm = Assembly.LoadFile(path);
Type t = asm.GetType("Google.Cast.DesktopV2.Utility.PlayMedia");
MethodInfo methodInfo = t.GetMethod("FindMediaPlayer");
var o = Activator.CreateInstance(t);
methodInfo.Invoke(o);
and here is my Method within WPF Application.
public MediaElement FindMediaPlayer()
{
return ((MainWindow)System.Windows.Application.Current.MainWindow).VideoPlayer;
}
VideoPlayer is a MediaElement within my XAML file. I am getting the error "The calling thread cannot access this object because a different thread owns it.'" , is there anyway I can achieve this using reflection?

Related

AssemblyLoadContext.Unload does not unload a Wpf Library

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.

How Do I Add Background Thread To Silverlight Custom Control?

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.

VS2010 Design-Time error when using WCF to load design-time data

gentlemens.
I have following definition in the Silverlight project, MainPage.xaml:
<UserControl
xmlns:model="clr-namespace:Engine.Silverlight.Web.Views;assembly=Engine.Login.Model"
d:DataContext="{d:DesignInstance Type=model:DesignTimeModel, IsDesignTimeCreatable=True}">...
And class in the Engine.Login.Model project, which used for design-time data binding (everything works fine for pre-initialized properties, but):
public class DesignTimeModel : INotifyPropertyChanged
{
public DesignTimeModel()
{
var d = Deployment.Current.Dispatcher;
d.BeginInvoke(
() =>
{
CacheClient c = new CacheClient();
c.GetResourcesCompleted +=(s,e)=>
{
d.BeginInvoke(
() => this.Resources = e.Result);
};
c.GetResourcesAsync();
}
);
Unfortunately, I got a System.ObjectDisposedException after WCF request completed (I tried to debug using different instance of VS by attaching to 1st VS instance process, but it does not help - same error, no additional info):
System.ObjectDisposedException
Cannot access a disposed object.
Object name: 'Dispatcher'.
at System.ServiceModel.AsyncResult.End[TAsyncResult](IAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
I assume the Dispatcher behavior different in the design mode.
Can you help me how to resolve the problem to get design-time data using WCF in VS2010 XAML designer?
First, I belive that making a WCF call in a design time class isn't really a best practice! You should instead put some static dummy data.
For your problem, try using Deployment.Current.Dispatcher directly instead of pointing a variable to it.

Debugging silverlight in a WPF app

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/

WPF control hosted in Windows Forms: Is it possible to access resource files?

I have a WPF control hosted in Windows Forms and I would like to access it's resources, specifically images. What is the best way to do that?
I was thinking of using a ResourceDictionary, but I'm not sure how I can access it from within a Windows Form.
This is a standalone WPF control, in a DLL? There are two ways that the resources can be embedded... as part of a .resources file (e.g. the Project's "Resoruces" tab), or as files included as "Embedded Resources"...
To get the assembly reference to the DLL this is usually the easiest:
var ass = typeof(ClassInOtherDll).Assembly;
In the Resources
If this is part of the default project resources, and not a different .resources file included inside the DLL, you can use the default base-name.
var ass = typeof(ClassInTargetDLL).Assembly;
var rm = new ResourceManager("...BaseName...", ass);
BaseName for default project resources is :
C# := Namespace.Properties.Resources
VB := Namespace.Resources
After that you can just call GetObject() and do a cast back:
var myImage = rm.GetObject("check_16");
return myImage as Bitmap;
If you want to find out whats in there, get the assembly reference and call
ass.GetManifestResourceNames()
.resources files can be used with a ResourceManager
embedded resources will just show up as a list. Use the other method for these.
And this is all assuming they default culture =)
As Embedded Resources
You can use the regular methods on the assembly to get an embedded resource. You find it by name, and then Basically you will need to convert the stream back into your desired type.
GetManifestResourceNames()
GetManifestResourceStream(name)
help getting the desired file with a helper function to find files by name.
I usually use these like this:
// called GetMp3 in the post, but it returns a stream. is the same thing
var stream = GetResourceStream("navigation.xml");
var reader = New XmlTextReader(stream);
return reader;
To get an image that is an embedded resource, this works for me:
var stream = GetResoureStram("check_32.png");
var bmp = new Bitmap(stream);
this.MyButton.Image = bmp;

Resources