1

我正在从不同的语言调用 c# dll 非托管方法。我设置了回调,所以当 c# dll 完成时,我的应用程序会通过它获得结果。

这会在 .Invoke 上导致 SEHException:

public static async Task<IList<Models.ValueSet>> Fetch2(Uri uri)
        {
            ServicePointManager.Expect100Continue = true;
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            var client = new HttpClient();
            var response = await client.GetAsync(uri);
            response.EnsureSuccessStatusCode();
            var content = await response.Content.ReadAsStringAsync();            

            var references = JsonConvert.DeserializeObject<ValueSetReference[]>(content);
            var fetchTasks = references
                .Select( async x =>
                {
                    var perValueSetResponse = await client.GetAsync(new Uri(uri, $"{x.Hash}/"));
                    perValueSetResponse.EnsureSuccessStatusCode();
                    var perValueSetContent = await perValueSetResponse.Content.ReadAsStringAsync();
                    return JsonConvert.DeserializeObject<Models.ValueSet>(perValueSetContent);                    
                });            
            return await Task.WhenAll(fetchTasks);
        }

这不会:

public static async Task<IList<Models.ValueSet>> Fetch(Uri uri)
        {
            ServicePointManager.Expect100Continue = true;
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            
            var client = new HttpClient();
            var response = client.GetAsync(uri).Result;
            response.EnsureSuccessStatusCode();
            var content = await response.Content.ReadAsStringAsync();                        
            var references = JsonConvert.DeserializeObject<ValueSetReference[]>(content);
            var fetchTasks = references
                .Select( async x =>
                {
                    var perValueSetResponse = client.GetAsync(new Uri(uri, $"{x.Hash}/")).Result;
                        perValueSetResponse.EnsureSuccessStatusCode();
                    var perValueSetContent = await perValueSetResponse.Content.ReadAsStringAsync();
                    return JsonConvert.DeserializeObject<Models.ValueSet>(perValueSetContent);                    
                });                        
            return await Task.WhenAll(fetchTasks);
        }

这是 Invoke 上发生的异常:

{
    "ClassName": "System.Runtime.InteropServices.SEHException",
    "Message": "External component has thrown an exception.",
    "Data": null,
    "InnerException": null,
    "HelpURL": null,
    "StackTraceString": "   at EUDCC.Verifier.<VerifyAsync>d__4.MoveNext()",
    "RemoteStackTraceString": null,
    "RemoteStackIndex": 0,
    "ExceptionMethod": "8\nMoveNext\neudcc-verifier.lib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null\nEUDCC.Verifier+<VerifyAsync>d__4\nVoid MoveNext()",
    "HResult": -2147467259,
    "Source": "eudcc-verifier.lib",
    "WatsonBuckets": null
}

这是 c# dll 的全部主要代码:

using EUDCC.Configuration;
using EUDCC.Models;
using EUDCC.Rules;
using EUDCC.Services;
using EUDCC.TrustList;
using Newtonsoft.Json;
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using net.r_eg.DllExport;
using System.Diagnostics;
using System.Threading;

namespace EUDCC
{
    public static class Verifier
                 
    {
        static string result { get; set; }

        [DllExport(CallingConvention = CallingConvention.StdCall, ExportName = "GetResult")]
        [return: MarshalAs(UnmanagedType.BStr)]

        public static string GetResult()
        {
            return result;
        }

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate void QRVerifyResult([MarshalAs(UnmanagedType.BStr)] string result);
        private static QRVerifyResult dQRVerifyResult;

        [DllExport(CallingConvention = CallingConvention.StdCall, ExportName = "SetQRVerifyResultCallback")]
        public static void SetQRVerifyResultCallback(int funcAddr)
        {
            IntPtr cbPtr = new IntPtr(funcAddr);            
            dQRVerifyResult = (QRVerifyResult)Marshal.GetDelegateForFunctionPointer(cbPtr, typeof(QRVerifyResult));            
        }
        public static async void VerifyAsync(string qrCode, string TrustListUri, string ValueSetUri, string RuleSetUri)
        {
            var sc = SynchronizationContext.Current;
            Task<VerificationReport> t = Verify(qrCode, TrustListUri, ValueSetUri, RuleSetUri);
            var verReport = await t;
            result = JsonConvert.SerializeObject(verReport);
            Trace.WriteLine("Result that INVOKE will send: " + result);
            SynchronizationContext.SetSynchronizationContext(sc);
             try
            {                
                dQRVerifyResult?.Invoke(result);
                     
            }
            catch (Exception ex)
            {
                Trace.WriteLine("Invoke Failed: " + ex.Message+" "+ex.GetBaseException());                
            }
            Trace.WriteLine("VerifySync: Invoking done");
        }

        [DllExport(CallingConvention = CallingConvention.StdCall, ExportName = "QRVerify")]
        public static void DoVerify([MarshalAs(UnmanagedType.BStr)] string qrCode,
                                  [MarshalAs(UnmanagedType.BStr)] string TrustListUri,
                                  [MarshalAs(UnmanagedType.BStr)] string ValueSetUri,
                                  [MarshalAs(UnmanagedType.BStr)] string RuleSetUri)
        {
            Trace.WriteLine("****************** START *******************");
            Trace.WriteLine("DoVerify called from CW, before VerifyAsync called");
            try
            {
                VerifyAsync(qrCode, TrustListUri, ValueSetUri, RuleSetUri);
            }
            catch (Exception ex)
            {
                Trace.WriteLine("VerifyAsyncFailed: " + ex.Message);
            }
            Trace.WriteLine("VerifyAsync Done, DoVerify from CW Done");
        }

         public static async Task<VerificationReport> Verify([MarshalAs(UnmanagedType.BStr)] string qrCode,
                                                            [MarshalAs(UnmanagedType.BStr)] string TrustListUri,
                                                            [MarshalAs(UnmanagedType.BStr)] string ValueSetUri,
                                                            [MarshalAs(UnmanagedType.BStr)] string RuleSetUri)
    {
                 Trace.WriteLine("Verify procedure called");
            _ = new Settings(
                trustListUri: new Uri(TrustListUri),
                valueSetUri: new Uri(ValueSetUri),
               rulesetUri: new Uri(RuleSetUri));
            DccCertificate dccCertificate;

            bool signatureVerified = false;
            var errorMessage = string.Empty;
            

            try
            {
                dccCertificate = DccCertificateDecoder.Decode(qrCode);
            }
            catch (Exception ex)
            {
                Trace.WriteLine("exception VerificationReport called");
                errorMessage = $"Unable to decode the QR Code to a valid EU DCC certificate. Error encountered is: {ex.Message}";
                return new VerificationReport(signatureVerified, null, null, errorMessage, null);
            }

            try
            {
                var signatureCertificate = await TrustListRepository.GetByKid(dccCertificate.SignatureData.Kid);
                signatureVerified = SignatureVerifier.Verify(signatureCertificate, dccCertificate);
                Trace.WriteLine("back from .Verify");
            }
            catch (Exception ex)
            {
                Trace.WriteLine("exception signatureVerified called");
                errorMessage = $"DCC certificate was decoded successfully, but verifying certificate signature failed. Error encountered is: {ex.Message}, " + ex.InnerException.Message;
               //return new VerificationReport(signatureVerified, null, null, errorMessage, null);
            }

            if (dccCertificate.MetaData.ExpiringDate < DateTimeOffset.UtcNow)
            {
                Trace.WriteLine("ErrorMessage called");
                errorMessage = "Certificate has expired.";
                Trace.WriteLine("Return Verify ExpiringDate");
                return new VerificationReport(signatureVerified, dccCertificate.HealthCertificate, dccCertificate.MetaData, errorMessage, null);
            } 
            else
            {
                Trace.WriteLine("ExpiringDate checked");
            }

            var healthCertificateJson = JsonConvert.SerializeObject(dccCertificate.HealthCertificate);            
            var rawHealthCertificate = JsonConvert.DeserializeObject<HealthCertificateRaw>(healthCertificateJson);
            var invalidRules = await RuleEngine.Run(rawHealthCertificate, Settings.ValueSetUri, Settings.RulesetUri, Settings.AppName);            
            if (invalidRules.Any())
            {
                errorMessage = "Some validation rules are invalid.";
                Trace.WriteLine("We got invalid rules");

            }
            Trace.WriteLine("Just before Verify return");
            return new VerificationReport(signatureVerified, dccCertificate.HealthCertificate, dccCertificate.MetaData, errorMessage, invalidRules);
        }
    }
}

Clarion 中用于调用 c# dll 的原型:

module('eudcc-verifier.dll')
    QRVerify(bstring,bstring,bstring,bstring), name('QRVerify'), pascal,raw,dll(true)
    SetQRVerifyResultcallback(long),pascal,raw,dll(true)
    GetResult(),bstring, pascal, raw, dll(true)
end
  QRVerifyResult(bstring result), PASCAL                

来电:

qrcodebstr=clip(qrcode)
trustlistbstr='https://...'
ValueSetBstr='https://...'
RuleSetbstr='...'          
SetQRVerifyResultCallback(ADDRESS(QRVerifyResult))
QRVerify(qrcodebstr,trustlistbstr,ValueSetBstr,RuleSetbstr)    

Clarion 中的回调过程(由 C# 中的 Invoke 回调):

QRVerifyResult      PROCEDURE(bstring result)
    CODE        
    message(result)
    return

我不精通C#,但为什么会这样?有没有等待客户仍然有效的解决方法?我更喜欢我的应用程序在 c# dll 下载文件时保持响应。在没有“等待”客户端的情况下,我的应用程序在下载时锁定...

谢谢你。

4

1 回答 1

0

我发现了一个问题和解决方案。在上面的 C# 代码中使用awaitwithHttpClient创建一个新线程。

在这种情况下,当Invoke被调用时,它会从另一个线程调用回调过程,该线程是对 dll 的初始调用。

在 Clarion 的情况下,有一种方法AttachThreadToClarion()可以解决这个问题。您可以将此调用放入 clarion 回调过程或在 C# 中对其进行原型化并从那里调用它。

问题解决了,一切都按应有的方式异步工作。

于 2022-01-12T17:15:52.243 回答