cross-AppDomain event issues - prism

I use the following helper class with POS for .Net to get a reference to the hardware in a separate AppDomain (getting around some limitations of requiring <NetFx40_LegacySecurityPolicy enabled="true"/>
public static class PosHelper
{
private static AppDomain _posAppDomain { get; set; }
private static AppDomain PosAppDomain
{
get
{
if (_posAppDomain == null)
{
AppDomainSetup currentAppDomainSetup = AppDomain.CurrentDomain.SetupInformation;
AppDomainSetup newAppDomainSetup = new AppDomainSetup()
{
ApplicationBase = currentAppDomainSetup.ApplicationBase,
LoaderOptimization = currentAppDomainSetup.LoaderOptimization,
ConfigurationFile = currentAppDomainSetup.ConfigurationFile
};
newAppDomainSetup.SetCompatibilitySwitches(new[] { "NetFx40_LegacySecurityPolicy" });
_posAppDomain = AppDomain.CreateDomain("POS Hardware AppDomain", null, newAppDomainSetup);
}
return _posAppDomain;
}
}
public static T GetHardware<T>() where T : PosHardware, new()
{
T hardware = (T)PosAppDomain.CreateInstanceFromAndUnwrap(Assembly.GetAssembly(typeof(T)).Location, typeof(T).FullName);
hardware.FindAndOpenDevice();
return hardware;
}
}
I have a basic class to handle when a POS scanner scans data. In that class I have an event that I want to fire when data is scanned. Here's a snippet:
public class ScannerDevice : PosHardware
{
public event Action<string> DataScanned;
...
_scanner.DataEvent += new DataEventHandler(Scanner_DataEvent);
...
private void Scanner_DataEvent(object sender, DataEventArgs e)
{
ASCIIEncoding encoder = new ASCIIEncoding();
if (DataScanned != null)
DataScanned(encoder.GetString(_scanner.ScanDataLabel));
_scanner.DataEventEnabled = true; // enable for subsequent scans
}
Note that the PosHardware abstract class inherits MarshalByRefObject and is marked [Serializable]
In my main AppDomain I try to use the event like so:
Scanner = PosHelper.GetHardware<ScannerDevice>();
Scanner.DataScanned += m =>
{
Debug.WriteLine(m);
};
When it hits the line trying to add the lambda to the DataScanned event I get this error:
Could not load file or assembly 'MyAssemlyName, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null' or one of its dependencies. The
system cannot find the file specified.
This has to be related to trying to communicate between AppDomains. Not really sure what to do. Do I need to register "MyAssemblyName" in the separate AppDomain used for Pos for .Net?
I use prism, so some modules are loaded at runtime (in a subfolder in my output directory)... including the one in which I use the last code snippet above (Scanner = PosHelper.GetHardware....)

I believe I solved my problem. Since my prism modules are loaded at runtime within a subdirectory I needed to add this to the AppDomain so that the AppDomain could find the assemblies in the subdirectories folder.:
PrivateBinPath = #"Modules"
http://msdn.microsoft.com/en-us/library/system.appdomainsetup.privatebinpath.aspx
Edit
This only partially solved my problem. I also had to override InitializeLifetimeService() and return null so that my MarshalByRefObject's would not be disposed while the program is running (I believe the default timeout is 5 minutes).
Also , this now works:
Scanner.DataScanned += m =>
{
Debug.WriteLine(m);
}
but when I try something like this
Scanner.DataScanned += m =>
{
DoSomething(m);
}
Where DoSomething is not in a Serializable and MarshalByRefObject class, it craps out since all classes that are used in the communication between AppDomain's need to have those. So where I'm at now is looking at using WCF named pipes to pass data around... and other similar solutions.

Related

Windows 10 version 1903 update (NET Framework 4.8) breaking Prism 6.3

First off, we are well aware that this isn't strictly speaking a Prism 6.3 issue; what we're looking for (in case a solution isn't straightforward) are pointers to a solution to the problem -thanks in advance btw-, which is:
Windows 10 version 1903, via .NET Framework 4.8 inclusion, breaks our otherwise perfectly functioning, tried-and-true, production-deployed Prism 6.3-based commercial software. We're using Prism (Core), Prism.WPF, and Prism.MEF (all v6.3). What we get (source code later) is the following runtime error whenever we try to instantiate a registered View:
Prism.Regions.RegionNavigationService.CreateNewRegionItem(String candidateTargetContract) throws an InvalidOperationException: Cannot create navigation target 'xyzView'. Activation error ocurred while tring to get instance of type Object, key 'xyzView'.
Inner exception stems from Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase.GetInstance(Type serviceType, String key), which throws an ActivationException: Activation error ocurred while tring to get instance of type Object, key 'xyzView'.
Devil's in the details so here's some relevant code (xyzView = ExpedicionView or ExpedicionMaestroView, both trigger the Exception):
[ModuleExport(typeof(ExpedicionModulo))]
[Export(typeof(IMenu))]
public class ExpedicionModulo : IModule, IMenu
{
[Import]
public IRegionManager RegionManager;
[Import]
public ILoggerFacade Logger;
[ImportingConstructor]
public ExpedicionModulo(IRegionManager regionManager, ILoggerFacade logger)
{
Logger = logger;
RegionManager = regionManager;
// irrelevant (for our purposes) code omitted here
}
public void Initialize()
{
// Here's how we register views for main region
RegionManager.RegisterViewWithRegion(RegionNames.MainContentRegion, typeof(ExpedicionView));
RegionManager.RegisterViewWithRegion(RegionNames.MainContentRegion, typeof(MaestroExpedicionView));
// Some more registering for our dialog region
RegionManager.RegisterViewWithRegion(RegionNames.DialogRegion, typeof(ExpedicionDetalleView));
// other views registered in the very same fashion
Logger.Log("Expedition Module initialized", Category.Info, Priority.None);
}
We invoke RequestNavigate in this bit of code here:
[Export]
[PartCreationPolicy(CreationPolicy.Shared)]
public class ShellViewModel : BaseViewModel
{
public IRibbonPrincipal RibbonPrincipal { get; set; }
private readonly InteractionRequest<Confirmation> _confirmationInteractionRequest;
private readonly InteractionRequest<Notification> _notificationInteractionRequest;
private string _seccionActiva;
private string _subseccionActiva;
private IRegionManager _regionManager;
[ImportingConstructor]
public ShellViewModel(IRegionManager regionManager, IEventAggregator eventAggregator, ILoggerFacade logger)
: this(eventAggregator, logger)
{
try
{
_regionManager = regionManager;
_regionManager.RegisterViewWithRegion(RegionNames.MainContentRegion, typeof(PrincipalView));
}
catch (Exception ex)
{
this.LoggerError(ex.Message);
}
}
public ShellViewModel(IEventAggregator eventAggregator, ILoggerFacade logger)
: base(eventAggregator, logger)
{
try
{
// Some irrelevant (to our purposes) initialization code omitted here
New = new DelegateCommand(() => { }, () => { return false; }); // etc.
_confirmationInteractionRequest = new InteractionRequest<Confirmation>();
_notificationInteractionRequest = new InteractionRequest<Notification>();
// Events code omitted for brevity sake
EventAggregator.GetEvent<MessageBoxEvent>().Subscribe(ShowMessageBox, ThreadOption.PublisherThread, false); // etc.
// View loading wireup
CambioSeccion = new DelegateCommand<object>(OnCambioSeccion);
CambioSubseccion = new DelegateCommand<object[]>(OnCambioSubseccion);
// some more irrelevant (to our purposes) code omitted here.
}
catch (Exception ex)
{
this.LoggerError(ex.Message);
}
}
/// <summary>
/// This is where we instantiate the View (WAI in .NET Framework <=4.7.2)
/// </summary>
/// <param name="objeto"></param>
private void OnCambioSeccion(object objeto)
{
// Ribbon menu handling here
RibbonPrincipal.ResetRibbon();
IIdentifyViewModel ivm = null;
// We are passing the View's name via clicked TreeViewItem in this case (param objeto)
TreeViewItem treeViewItem = (TreeViewItem)objeto;
if (treeViewItem != null && !string.IsNullOrEmpty(treeViewItem.Name))
{
_seccionActiva = treeViewItem.Name;
// Now we build the actual RequestNavigate invoke
// In our case, _seccionActiva would equal "Expedicion" or "MaestroExpedicion"
_regionManager.RequestNavigate(RegionNames.MainContentRegion, new Uri("/" + _seccionActiva + "View", UriKind.Relative), (r) =>
{
if (!r.Result.HasValue || !r.Result.Value)
{
// error handling code here
}
else
{
ivm = ((FrameworkElement)_regionManager.Regions[RegionNames.MainContentRegion].ActiveViews.First()).DataContext as IIdentifyViewModel;
}
});
// Some event triggering here
if (ivm != null)
{
Seccion.Cambio(EventAggregator, ivm.ID);
}
}
}
Sorry for the long winded post, and thanks in advance.
#mcandal thanks for the write-up. This looks like a recent issue introduced in .NET Framework 4.8 that we have seen with the patterns and practices activation code. The underlying issue is that in 4.8 the constructors for types were returned in a different order resulting in the patterns and practices choice of the first (eg. ctors[0]) sometimes no longer being correct.
There are a few workarounds as we work through a fix in an upcoming release.
This issue only impacts assemlbies that are ngen'd.
Workarounds:
1) Disable ngen for the assembly containing the type:
<configuration>
<runtime>
<disableNativeImageLoad>
<assemblyIdentity name="assembly_name" />
</disableNativeImageLoad>
</runtime>
</configuration>
2) For the type that is being activated only have 1 ctor (the documentation for the patterns and practices assumes there is only 1 ctor)
3) Choose the other provided activation models where you pass in the types of the parameters, to avoid the ambiguity.
As I mentioned, we are working on a fix and if you would like to discuss further you can submit a VS Feedback item and link to this post and we can continue the discussion there.

TypeLoadException: Could not load type from assembly in Xamarin

I have Xamarin Forms PCL application and I am trying to inherit from HttpContent in attempt to follow this reference. I would like to implement progress bar for image upload. Here is my implementation which contains minimum code to implement HttpContent:
public class ProgressableStreamContent : HttpContent
{
public ProgressableStreamContent()
{ }
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
return Task.Run(async () =>
{
});
}
protected override bool TryComputeLength(out long length)
{
length = 0;
return true;
}
}
and I have procedure that is using this ProgressableStreamContent:
public async void UseProgressableStreamContent()
{
var progressableContent = new ProgressableStreamContent();
}
Problem is that this code produces exception:
UNHANDLED EXCEPTION: System.TypeLoadException: Could not load type 'MyApp.Classes.ProgressableStreamContent' from assembly 'MyApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
When I comment out line "var progressableContent = new ProgressableStreamContent();" - everything works good.
When I comment out inheritance ": HttpContent" (and override keywords) - everything works good.
Should I install some package? What else could be a problem?
I resolved issue by changing profile from 259 to 7. Changing profile includes deleting all NuGet packages and installing them again after profile change.

Log all methods called in an app by Xposed

As title said, I want to use xposed to log all methods called in an app from it start till I stop it. I only want to log Class name, Method name, don't want to hook all method.
I try this code, but get error getMethod not found.
findAndHookMethod("java.lang.Class", lpparam.classLoader, "getMethod", String.class, Object.class, new XC_MethodHook()
Thanks in advance!
There is no one line solution like what you seem to be searching.
Hooking all methods will let log what methods were called by app from it start till stop (sort of - see below), but if (for some reason) you don't want to hook all methods, the only solution I can think of is modifying the java VM itself (NOT something I would recommend.)
A solution that (sort of) works
What I did was first use apktool to decompile my apk and get the names of all the methods in all the classes.
Then I used xposed to hook into every single method of every class and print to the dlog the current function name.
Why it only sort of works
Xposed has an overhead whenever it hook a methods. For general usage of xposed apps, it isnt much. But when you start hooking each and every methods of an app, the overhead very quickly becomes ridiculously large - So much so that while the above methods works for small apps, for any large app it very quickly causes the app to hang and then crash.
An alternative that also sort-of works
FRIDA is a way to inject javascript to native apps. Here they show you how to log all function calls. While in the above link they log all function calls in a piece of python code, the same code also works for Android.
There is a way to log all Java methods.Modify XposedBridge.
Xposed hook java method through XposedBridge.java's method
"handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj, thisObject, Object[] args)"
Log.v(TAG, "className " + method.getClass().getName() + ",methodName " + method.getName());
As mentioned before Xposed is not the way to go in this situation due to its overhead.
The simplest solution is just to use dmtracedump as provided by Google. Most x86 Android images and emulator come with the debuggable flag on (ro.debuggable) so you can even use it for closed source apps.
Additionally other tools such as Emma are known to work with Android as well, but these might need modifications to the source code.
I found a solution.
See this code snippet below.
package com.kyunggi.logcalls;
import android.content.pm.*;
import android.util.*;
import dalvik.system.*;
import de.robv.android.xposed.*;
import de.robv.android.xposed.callbacks.XC_LoadPackage.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
import android.app.*;
public class Main implements IXposedHookLoadPackage {
private String TAG = "LogCall";
public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals("com.android.bluetooth")) {
Log.i(TAG, "Not: " + lpparam.packageName);
return;
}
Log.i(TAG, "Yes " + lpparam.packageName);
//Modified https://d3adend.org/blog/?p=589
ApplicationInfo applicationInfo = AndroidAppHelper.currentApplicationInfo();
if (applicationInfo.processName.equals("com.android.bluetooth")) {
Set<String> classes = new HashSet<>();
DexFile dex;
try {
dex = new DexFile(applicationInfo.sourceDir);
Enumeration entries = dex.entries();
while (entries.hasMoreElements()) {
String entry = (String) entries.nextElement();
classes.add(entry);
}
dex.close();
} catch (IOException e) {
Log.e("HookDetection", e.toString());
}
for (String className : classes) {
boolean obex = false;
if (className.startsWith("com.android.bluetooth") || (obex = className.startsWith("javax.obex"))) {
try {
final Class clazz = lpparam.classLoader.loadClass(className);
for (final Method method : clazz.getDeclaredMethods()) {
if (obex) {
if (!Modifier.isPublic(method.getModifiers())) {
continue; //on javax.obex package, hook only public APIs
}
}
XposedBridge.hookMethod(method, new XC_MethodHook() {
final String methodNam = method.getName();
final String classNam = clazz.getName();
final StringBuilder sb = new StringBuilder("[");
final String logstr = "className " + classNam + ",methodName " + methodNam;
#Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
//Method method=(Method)param.args[0];
sb.setLength(0);
sb.append(logstr);
//Log.v(TAG,logstr);
for (Object o : param.args) {
String typnam = "";
String value = "null";
if (o != null) {
typnam = o.getClass().getName();
value = o.toString();
}
sb.append(typnam).append(" ").append(value).append(", ");
}
sb.append("]");
Log.v(TAG, sb.toString());
}
});
}
} catch (ClassNotFoundException e) {
Log.wtf("HookDetection", e.toString());
}
}
}
}
// ClassLoader rootcl=lpparam.classLoader.getSystemClassLoader();
//findAndHookMethod("de.robv.android.xposed.XposedBridge", rootcl, "handleHookedMethod", Member.class, int.class, Object.class, Object.class, Object[].class, );
}
}

Requesting Android permissions in a class (Xamarin)

I'm trying to request a permission at runtime for my app. I use a service provider to talk between the portable class and Android.
I start by calling this code on button press in the PCL:
using (new Busy(this))
{
var locationHelper = scope.Resolve<ILocationHelper>();
locationHelper.GetLocation(this);
}
This calls my Android level service:
public class AndroidLocationHelper : ILocationHelper, ILocationListener
{
readonly string[] PermissionsLocation =
{
Manifest.Permission.AccessCoarseLocation
};
const int RequestLocationId = 0;
public void GetLocation(SearchViewModel viewModel)
{
try
{
const string permission = Manifest.Permission.AccessCoarseLocation;
if (((int)Build.VERSION.SdkInt < 23) || (CheckSelfPermission(permission) == Permission.Granted))
{
}
else
RequestPermissions(PermissionsLocation, RequestLocationId);
}
catch (Exception ex)
{
Debug.WriteLine("Error while getting Location service");
Debug.WriteLine(ex.Message);
Messaging.AlertUser("There was an error with determining your location");
}
}
However, I get two errors on CheckSelfPermission and RequestPermissions. These two methods are only available to activities. The code works fine in MainActivity; however, I want to ask for permissions when the user hits a button, not in OnCreate or OnResume, etc.
Thanks for any help.
In your Android project, You can use this and use the Dependency Service to call it in Xamarin.Forms PCL project later:
var thisActivity = Forms.Context as Activity;
ActivityCompat.RequestPermissions(thisActivity, new string[] {
Manifest.Permission.AccessFineLocation }, 1);
ActivityCompat.RequestPermissions(thisActivity,
new String[] { Manifest.Permission.AccessFineLocation },
1);
You can try with ContextCompat.CheckSelfPermission, passing the application context, like this:
ContextCompat.CheckSelfPermission(Android.App.Application.Context, permission)
Update
In case of ActivityCompat.RequestPermissions, which requires an activity reference, you can keep track of the current activity. There is a very handy lib for that, called "CurrentActivityPlugin". You can find at https://github.com/jamesmontemagno/CurrentActivityPlugin
Rafael came up with a solution but I found another option that is a lot less effort just using MessagingCenter. In the MainActivity's OnCreate add a receiver that runs all the location code, that way you have access to all of the activities methods (and there are a bunch of tutorials on doing location services in MainActivity). Then add the Send inside of your service (the class).
To expound Rafael Steil's answer, I tried the suggested CurrentActivityPlugin and it worked on me. In my case I am trying to execute a voice call which needs CALL_PHONE permission. Here is the code snippet in your case: I used the ContextCompat & ActivityCompat so that I don't need to check the VERSION.SdkInt
using Plugin.CurrentActivity;
public void GetLocation(SearchViewModel viewModel){
var context = CrossCurrentActivity.Current.AppContext;
var activity = CrossCurrentActivity.Current.Activity;
int YOUR_ASSIGNED_REQUEST_CODE = 9;
if (ContextCompat.CheckSelfPermission(context, Manifest.Permission.AccessCoarseLocation) == (int)Android.Content.PM.Permission.Granted)
{
//Permission is granted, execute stuff
}
else
{
ActivityCompat.RequestPermissions(activity, new string[] { Manifest.Permission.AccessCoarseLocation }, YOUR_ASSIGNED_REQUEST_CODE);
}
}
It's dead simple
public bool CheckPermission()
{
const string permission = Manifest.Permission.ReceiveSms;
return ContextCompat.CheckSelfPermission(Forms.Context, permission) == (int) Permission.Granted;
}

Getting "main" Assembly version number

I have a solution with libraries (DLLs) which are used in 2 identical projects (one for WP7, another for WP8). In one of the libraries I have the code which determines the version of the application.
private static Version mVersion;
public static Version Version {
get {
if (mVersion == default(Version)) {
var lcAssembly = Assembly.GetExecutingAssembly();
var parts = lcAssembly.FullName.Split(',');
var lcVersionStr = parts[1].Split('=')[1];
mVersion = new Version(lcVersionStr);
}
return mVersion;
}
}
The problem is that this code returns the version number of the library itself because of this Assembly.GetExecutingAssembly() code. How to get a MAIN Assembly version and not DLL's?
That's a great question on code-sharing between WP7 and WP8.
The simplest way for you to do that would be to read the AppManfiest.xml file at run-time, get the EntryType and use that to get at the entry point Assembly instance. Here's how a sample AppManfiest.xml looks like once MSBuild did its magic on it:
<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" EntryPointAssembly="myAssembly" EntryPointType="myNamespace.App" RuntimeVersion="4.7.50308.0">
<Deployment.Parts>
<AssemblyPart x:Name="myAssembly" Source="myAssembly.dll" />
</Deployment.Parts>
</Deployment>
And here's how you would read the file, get the attributes, then get the entry point type and finally the entry point assembly:
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var appManfiest = XElement.Load("AppManifest.xaml");
var entryAssemblyName = appManfiest.Attribute("EntryPointAssembly").Value;
var entryTypeName = appManfiest.Attribute("EntryPointType").Value;
Type entryType = Type.GetType(entryTypeName + "," + entryAssemblyName);
Assembly entryAssembly = entryType.Assembly;
}
That's a simple solution and it works. However, that isn't the cleanest architectural solution. The way I'd implement this solution is to have an interface declared in the shared library, both WP7 and WP8 implement that interface and register their implementation with an IoC container.
For example, let's say you need to "DoSomething" in the shared library that's platform version specific. First you'll create have an IDoSomething interface. Let's also assume you have an IoC standing by.
public interface IDoSomething
{
}
public static class IoC
{
public static void Register<T>(T t)
{
// use some IoC container
}
public static T Get<T>()
{
// use some IoC container
}
}
In your WP7 app you'll implement the shared Interface for WP7 and register it once the WP7 starts up.
public App()
{
MainPage.IoC.Register(new MainPage.DoSomethingWP7());
}
private class DoSomethingWP7 : IDoSomething
{
}
You'll also do the same for WP8 in the WP8 app. And in your shared library you can then ask for the relevant interface regardless of its platform version specific implementation:
IDoSomething sharedInterface = IoC.Get<IDoSomething>();
I have a simpler answer. I think you are close with what you are doing. I just used your code with one modification so I can use it with the Telerik controls. Here's what I did. I located your code in my project's App class (codebehind of App.Xaml). I made one change that I think will take care of your problem:
private static Version mVersion;
public static Version Version {
get {
if (mVersion == default(Version)) {
var lcAssembly = typeof(App);
var parts = lcAssembly.FullName.Split(',');
var lcVersionStr = parts[1].Split('=')[1];
mVersion = new Version(lcVersionStr);
}
return mVersion;
}
}
Now I can get the version number by calling "App.Version".
This worked for me:
var appAssembly = Application.Current.GetType().Assembly;
var appAssemblyVersion = appAssembly.GetName().Version;
I tested with WP7.1 and WP8.0.

Resources