如何等待AppDomain在C#中处理异步回调,然后返回结果?

如何等待AppDomain在C#中处理异步回调,然后返回结果?,c#,asynchronous,appdomain,C#,Asynchronous,Appdomain,我有一些加载的代码,AppDomain(称之为domain)调用域中的一个对象函数。目的是使用设备API从usb设备获取项目列表以检索信息。API需要回调来返回信息 var AppDomain.CreateDomain( $"BiometricsDomain{System.IO.Path.GetRandomFileName()}"); var proxy = domain.CreateInstanceAndUnwrap(proxy.Assembly.FullName, proxy.Full

我有一些加载的代码,AppDomain(称之为domain)调用域中的一个对象函数。目的是使用设备API从usb设备获取项目列表以检索信息。API需要回调来返回信息

var AppDomain.CreateDomain(
   $"BiometricsDomain{System.IO.Path.GetRandomFileName()}");
var proxy = domain.CreateInstanceAndUnwrap(proxy.Assembly.FullName, proxy.FullName 
   ?? throw new InvalidOperationException()) as Proxy;
var ids = obj.GetIdentifications();
加载到域中的代理代码如下所示

public class Proxy : MarshalByRefObject
{ 
    public List<String> GetIdentifications()
    {
        var control = new R100DeviceControl();
        control.OnUserDB += Control_OnUserDB;
        control.Open();
        int nResult = control.DownloadUserDB(out int count);
        // need to be able to return the list here but obviously that is not 
        // going to work.
    }
    private void Control_OnUserDB(List<String> result)
    {
        // Get the list of string from here
    }
}
公共类代理:MarshallByRefObject
{ 
公共列表GetIdentifications()
{
var控制=新的R100DeviceControl();
control.OnUserDB+=control_OnUserDB;
控件Open();
int nResult=control.DownloadUserDB(out int count);
//需要能够在这里返回列表,但显然不是这样
//去上班。
}
私有void Control_OnUserDB(列表结果)
{
//从这里获取字符串列表
}
}
是否有一种方法可以在调用回调时在设备上等待并根据需要返回信息?由于
GetIdentifications()
已经返回,我不知道如何获取
注意:虽然异步处理问题可能有更优雅的解决方案,但在子AppDomain中发生这种情况的事实证明了子AppDomain的最佳实践。(见下面的链接)

i、 e

  • 不允许在父域中执行用于子AppDomain的代码
  • 不允许复杂类型冒泡到父AppDomain
  • 不允许异常以自定义异常类型的形式跨越AppDomain边界

  • 作品:


    我用它来容错

    首先,我可能会添加一个
    Open
    或类似的方法,为数据的实现留出时间

    var proxy = domain.CreateInstanceAndUnwrap(proxy.Assembly.FullName, proxy.FullName 
       ?? throw new InvalidOperationException()) as Proxy;
    
    proxy.Open();  // <------ new method here
    .
    . some time later
    .
    var ids = obj.GetIdentifications();
    
    现在,您可能可以改进
    GetNotifications
    以在数据准备就绪之前调用
    GetNotifications
    或在后续数据到达之前调用它时接受超时

    更多

    可以考虑使用基于事件的异步模式(EAP)操作作为一个任务,这样就可以等待事件了。p>

    public class Proxy : MarshalByRefObject {
        public List<String> GetIdentifications() {
            var task = GetIdentificationsAsync();
            return task.Result;
        }
        private Task<List<String>> GetIdentificationsAsync() {
            var tcs = new TaskCompletionSource<List<string>>();
            try {
                var control = new R100DeviceControl();
                Action<List<string>> handler = null;
                handler = result => {
                    // Once event raised then set the 
                    // Result property on the underlying Task.
                    control.OnUserDB -= handler;//optional to unsubscribe from event
                    tcs.TrySetResult(result);
                };
                control.OnUserDB += handler;
                control.Open();
                int count = 0;
                //call async event
                int nResult = control.DownloadUserDB(out count);
            } catch (Exception ex) {
                //Bubble the error up to be handled by calling client
                tcs.TrySetException(ex);
            }
            // Return the underlying Task. The client code
            // waits on the Result property, and handles exceptions
            // in the try-catch block there.
            return tcs.Task;
        }
    }
    

    参考

    不确定为什么不保持一点状态,然后等待调用结果:

    public class Proxy : MarshalByRefObject
    { 
        bool runningCommand;
        int lastResult;
        R100DeviceControl DeviceControl { get{ if(deviceControl == null){ deviceControl = new R100DeviceControl(); deviceControl.OnUserDB += Control_OnUserDB; } return deviceControl; } }
    
    
        public List<String> GetIdentifications()
        {
            if(runningCommand) return null;
            DeviceControl.Open();
            runningCommand = true;
            lastResult = control.DownloadUserDB(out int count);
        }
        private void Control_OnUserDB(List<String> result)
        {
            runningCommand = false;
            // Get the list of string from here
        }
    }
    
    公共类代理:MarshallByRefObject
    { 
    布尔运行命令;
    最后结果;
    R100DeviceControl DeviceControl{get{if(DeviceControl==null){DeviceControl=new R100DeviceControl();DeviceControl.OnUserDB+=Control_OnUserDB;}返回DeviceControl;}
    公共列表GetIdentifications()
    {
    if(runningCommand)返回null;
    DeviceControl.Open();
    runningCommand=true;
    lastResult=control.DownloadUserDB(out int count);
    }
    私有void Control_OnUserDB(列表结果)
    {
    runningCommand=false;
    //从这里获取字符串列表
    }
    }
    

    一旦你有了这样一个模式,你就可以很容易地在异步和其他模式之间切换,而在这之前,由于你集成了异步逻辑,它看起来更难理解,通过这种方式,你可以实现同步方法,然后如果你愿意的话,可以制作一个异步包装器。

    我的直觉说应该有一个更简单更优雅的解决方案。如果我找不到其他的东西,我肯定会考虑这一点,但我现在觉得不太正确。“JWZE,如果你在考虑<代码>任务GISTIGITIONS <代码> 1)为什么要使用AppDealm?2) AppDomains的重点是安全性和容错性。在代理上公开一个可能冒泡到父AppDomain的方法不是一个好主意。看,你相信它吗?,MSDN@jwize我使用它是为了容错,我完全相信代码。@jwize然后你接受了错误的答案。请参考链接和我提到的旧MSDN杂志文章以了解更多信息,我猜这将不起作用,因为任务不是Serializable@jwize您意识到,当您等待任务时,它返回的是对象结果,而不是任务。可序列化的tack与任何事情都有什么关系?那么,我将给出一个tryResult:System.Runtime.Serialization.SerializationException:'Type'System.Threading.Tasks.Task'1'在程序集'mscorlib,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089'中未标记为可序列化。'“冒泡错误以通过调用客户端来处理”-冒泡异常到父AppDomain通常是一种安全风险。子AppDomain的目的是什么?
    List<string> ids = proxy.GetIdentifications();
    
    public class Proxy : MarshalByRefObject
    { 
        bool runningCommand;
        int lastResult;
        R100DeviceControl DeviceControl { get{ if(deviceControl == null){ deviceControl = new R100DeviceControl(); deviceControl.OnUserDB += Control_OnUserDB; } return deviceControl; } }
    
    
        public List<String> GetIdentifications()
        {
            if(runningCommand) return null;
            DeviceControl.Open();
            runningCommand = true;
            lastResult = control.DownloadUserDB(out int count);
        }
        private void Control_OnUserDB(List<String> result)
        {
            runningCommand = false;
            // Get the list of string from here
        }
    }