本文介绍了GCHandle,AppDomains托管代码和第三方dll的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经看了很多关于异常的线程,通过AppDomains不能通过GCHandle,但是我仍然没有得到它。



我是使用由DLL驱动的RFID阅读器。我没有这个DLL的源代码,但只有一个示例来显示如何使用它。



该示例工程非常好,但我必须在另一个项目中复制一些代码将读者添加到中间件Microsoft Biztalk。



问题是Microsoft Biztalk的过程在另一个AppDomain中运行。阅读器读取标签时处理事件。但是当我在Microsoft Biztalk下运行它时,我遇到了这个令人讨厌的例外。



我看不到任何解决方案如何使其工作...



这是一些可能有趣的代码:

  //让我们连接结果处理程序。 
//如果命令完成并且答案准备好发送,读者会调用特定于命令的结果处理程序。
//所以让我们告诉读者如果一个结果准备发送,应该调用哪些函数。

//读取EPCs的结果处理程序同步
Reader.KSRWSetResultHandlerSyncGetEPCs(ResultHandlerSyncGetEPCs);

[...]

var readerErrorCode = Reader.KSRWSyncGetEPCs();
if(readerErrorCode == tKSRWReaderErrorCode.KSRW_REC_NoError)
{
//向读者发送命令时不发生错误。让我们等到结果处理程序被调用。
if(ResultHandlerEvent.WaitOne(TimeSpan.FromSeconds(10)))
{
//读者的工作完成并且结果处理程序被调用。我们来检查结果标志,以确保一切正常。
if(_readerResultFlag == tKSRWResultFlag.KSRW_RF_NoError)
{
//读取器成功处理了该命令。
//我们将在结果处理程序中显示结果。
}
else
{
//该命令不能被读者处理。要知道为什么要检查结果标志。
logger.error(Command \KSRWSyncGetEPCs\返回错误{0},_readerResultFlag);
}
}
else
{
//我们在10秒内没有读者回答。
logger.error(Command \KSRWSyncGetEPCs\timed out);
$


private static void ResultHandlerSyncGetEPCs(object sender,tKSRWResultFlag resultFlag,tKSRWExtendedResultFlag extendedResultFlag,tKSRWEPCListEntry [] epcList)
{
if(Reader == sender)
{
//我们将结果标志存储在一个全局变量中,以便从任何地方访问。
_readerResultFlag = resultFlag;

//显示天线字段中的所有可用epcs。
Console.ForegroundColor = ConsoleColor.White;
foreach(var resultListEntry in epcList)
{
handleTagEvent(resultListEntry);
}

//让我们设置事件,以便调用进程知道命令由读者处理,结果准备好进行处理。
ResultHandlerEvent.Set();
}
}


解决方案

你在中遇到问题。它被用于没有人可以看到的代码,在该DLL内。经常使用被设计为与托管代码交互的C ++代码,gcroot>存储对被管理对象的引用。该类使用GCHandle类型来添加引用。 GCHandle.ToIntPtr()方法返回一个C ++代码可以存储的指针。失败的操作是GCHandle.FromIntPtr(),由C ++代码使用以恢复对该对象的引用。



获取此异常有两个基本说明: / p>


  1. 可以准确。当您从一个AppDomain中初始化DLL中的代码并在另一个AppDomain中使用该代码时,将会发生这种情况。 Reader类对象被初始化的片段不清楚,所以有非零的可能性,这就是说明。确保使它靠近使用Reader类的代码。


  2. 可能是由另一个错误引起的,存在于DLL内的C ++代码。非托管代码经常遇到指针错误,即可能会意外覆盖内存的错误。如果发生在存储gcroot<>对象的字段,那么一段时间没有什么问题。直到代码尝试再次恢复对象引用。此时,CLR注意到损坏的指针值不再与实际对象句柄匹配并生成此异常。这肯定是难以解决的错误,因为这种情况发生在代码中,您无法修复,并显示出对该错误进行修改的程序员非常困难,这样的内存损坏问题永远不会重现。


首先追逐子弹#1。 Biztalk在单独的AppDomain中运行您的C#代码有一些不错的可能。并且DLL在AppDomain创建之前或之后加载太快。你可以看到与SysInternals的ProcMon。通过编写一个创建AppDomain并运行测试代码的小测试程序来创建一个这样的操作。如果再现崩溃,那么您将有一个非常好的方式向RFID供应商展示问题,并希望他们能够使用它并进行修复。




I have looking at many threads about the exception "cannot pass a GCHandle across AppDomains" but I still don't get it....

I'm working with an RFID Reader which is driven by a DLL. I don't have source code for this DLL but only a sample to show how to use it.

The sample works great but I have to copy some code in another project to add the reader to the middleware Microsoft Biztalk.

The problem is that the process of Microsoft Biztalk works in another AppDomain. The reader handle events when a tag is read. But when I run it under Microsoft Biztalk I got this annoying exception.

I can't see any solution on how to make it work...

Here is some code that may be interesting :

// Let's connecting the result handlers.
// The reader calls a command-specific result handler if a command is done and the answer is ready to send.
// So let's tell the reader which functions should be called if a result is ready to send.

// result handler for reading EPCs synchronous
Reader.KSRWSetResultHandlerSyncGetEPCs(ResultHandlerSyncGetEPCs);

[...]

var readerErrorCode = Reader.KSRWSyncGetEPCs();
if (readerErrorCode == tKSRWReaderErrorCode.KSRW_REC_NoError)
{
    // No error occurs while sending the command to the reader. Let's wait until the result handler was called.
    if (ResultHandlerEvent.WaitOne(TimeSpan.FromSeconds(10)))
    {
        // The reader's work is done and the result handler was called. Let's check the result flag to make sure everything is ok.
        if (_readerResultFlag == tKSRWResultFlag.KSRW_RF_NoError)
        {
             // The command was successfully processed by the reader.
             // We'll display the result in the result handler.
        }
        else
        {
            // The command can't be proccessed by the reader. To know why check the result flag.
            logger.error("Command \"KSRWSyncGetEPCs\" returns with error {0}", _readerResultFlag);
        }
    }
    else
    {
        // We're getting no answer from the reader within 10 seconds.
        logger.error("Command \"KSRWSyncGetEPCs\" timed out");
    }
}

[...]

private static void ResultHandlerSyncGetEPCs(object sender, tKSRWResultFlag resultFlag, tKSRWExtendedResultFlag extendedResultFlag, tKSRWEPCListEntry[] epcList)
{
    if (Reader == sender)
    {
        // Let's store the result flag in a global variable to get access from everywhere.
        _readerResultFlag = resultFlag;

        // Display all available epcs in the antenna field.
        Console.ForegroundColor = ConsoleColor.White;
        foreach (var resultListEntry in epcList)
        {
            handleTagEvent(resultListEntry);
        }

        // Let's set the event so that the calling process knows the command was processed by reader and the result is ready to get processed.
        ResultHandlerEvent.Set();
    }
}
解决方案

You are having a problem with the gcroot<> helper class. It is used in the code that nobody can see, inside that DLL. It is frequently used by C++ code that was designed to interop with managed code, gcroot<> stores a reference to a managed object. The class uses the GCHandle type to add the reference. The GCHandle.ToIntPtr() method returns a pointer that the C++ code can store. The operation that fails is GCHandle.FromIntPtr(), used by the C++ code to recover the reference to the object.

There are two basic explanations for getting this exception:

  1. It can be accurate. Which will happen when you initialized the code in the DLL from one AppDomain and use it in another. It isn't clear from the snippet where the Reader class object gets initialized so there are non-zero odds that this is the explanation. Be sure to keep it close to the code that uses the Reader class.

  2. It can be caused by another bug, present in the C++ code inside the DLL. Unmanaged code often suffers from pointer bugs, the kind of bug that can accidentally overwrite memory. If that happens with the field that stores the gcroot<> object then nothing goes wrong for a while. Until the code tries to recover the object reference again. At that point the CLR notices that the corrupted pointer value no longer matches an actual object handle and generates this exception. This is certainly the hard kind of bug to solve since this happens in code you cannot fix and showing the programmer that worked on it a repro for the bug is very difficult, such memory corruption problems never repro well.

Chase bullet #1 first. There are decent odds that Biztalk runs your C# code in a separate AppDomain. And that the DLL gets loaded too soon, before or while the AppDomain is created. Something you can see with SysInternals' ProcMon. Create a repro of this by writing a little test program that creates an AppDomain and runs the test code. If that reproduces the crash then you'll have a very good way to demonstrate the issue to the RFID vendor and some hope that they'll use it and work on a fix.

Having a good working relationship with the RFID reader vendor to get to a resolution is going to be very important. That's never not a problem, always a good reason to go shopping elsewhere.

这篇关于GCHandle,AppDomains托管代码和第三方dll的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-15 06:30