6

我查看了许多关于异常“无法通过 AppDomains 传递 GCHandle”的线程,但我仍然不明白......

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

该示例效果很好,但我必须在另一个项目中复制一些代码才能将阅读器添加到中间件 Microsoft Biztalk。

问题是 Microsoft Biztalk 的进程在另一个 AppDomain 中工作。阅读器在读取标签时处理事件。但是当我在 Microsoft Biztalk 下运行它时,我得到了这个烦人的异常。

我看不到任何关于如何使它工作的解决方案......

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

// 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();
    }
}
4

1 回答 1

1

您遇到了gcroot<> helper class的问题。它用于该 DLL 中没有人可以看到的代码中。它经常被旨在与托管代码互操作的 C++ 代码使用,gcroot<> 存储对托管对象的引用。该类使用 GCHandle 类型来添加引用。GCHandle.ToIntPtr() 方法返回 C++ 代码可以存储的指针。失败的操作是 GCHandle.FromIntPtr(),C++ 代码使用它来恢复对对象的引用。

获得此异常有两种基本解释:

  1. 它可以是准确的。当您从一个 AppDomain 初始化 DLL 中的代码并在另一个 AppDomain 中使用它时,就会发生这种情况。从片段中不清楚 Reader 类对象在哪里被初始化,所以这是解释的非零几率。请务必将其靠近使用 Reader 类的代码。

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

首先追逐子弹#1。Biztalk 很有可能在单独的 AppDomain 中运行您的 C# 代码。并且 DLL 在创建 AppDomain 之前或期间加载得太快。您可以通过 SysInternals 的 ProcMon 看到一些东西。通过编写一个创建 AppDomain 并运行测试代码的小测试程序来创建一个重现。如果这重现了崩溃,那么您将有一个非常好的方法来向 RFID 供应商展示该问题,并且有些人希望他们会使用它并进行修复。

与 RFID 阅读器供应商建立良好的合作关系以达成解决方案将非常重要。这从来都不是问题,始终是去别处购物的好理由。

于 2013-08-22T13:08:58.367 回答