0

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....)

Chris Klepeis
  • 9,783
  • 16
  • 83
  • 149
  • By the way I don't think you should add the serializable attribute on a MarshalByRefObject. Serializable objects should be the ones that are passed from one AppDomain to the other. MarshalByRefObject based objects stay put on their AppDomain and the whole communication is taking place through proxy objects created transparently by the .NET Remoting mechanism. – Panos Rontogiannis Sep 26 '12 at 15:38

1 Answers1

0

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.

Chris Klepeis
  • 9,783
  • 16
  • 83
  • 149
  • This article is an exciting read!! Check out my article on Sending Events Across AppDomain https://blog.vcillusion.co.in/sending-events-through-application-domain-boundary/ Hope It Helps! – vCillusion May 29 '18 at 21:31