【问题标题】:C++/CLI System.NullReferenceException being thrown in Native Code在本机代码中抛出 C++/CLI System.NullReferenceException
【发布时间】:2016-07-19 00:34:18
【问题描述】:

我正在开发一个本地 C++ 应用程序,并且我正在尝试利用来自单独程序集的一些托管类来读取/写入最初用 C# 开发的数据容器。互操作层基本上是加载数据,然后将托管数据镜像到功能等效的本机数据容器中以供应用程序使用(然后显然又回来进行写入)。

老实说,尝试执行此互操作并不是一件非常有趣的事情,但它主要是在这一点上工作。但是,有一个翻译步骤可以阻止一切,并且在调试它时,一个托管异常 (System.NullReferenceException) 被抛出在仅本机 C++ 的行上。我完全不明白为什么会这样,我希望有人能理解发生了什么。我什至没想到它会在堆栈的托管部分中,在它抛出的位置......

这是我们代码所做工作的精简版:

//  The native data types.
class cUnmanagedInterpolator
{
    virtual std::vector<std::pair<double, cUnmanagedInterpolator>& GetPoints() { return mPoints; }
    std::vector<std::pair<double, cUnmanagedInterpolator> mPoints;
};

class cUnmanagedInterpolator_Const : public cUnmanagedInterpolator
{
    virtual double GetValue() const { return mValue; }
    double mValue
};

//  The managed data types (please forgive syntax errors here; they're actually written in C# in our software, and this is just to get the point across).
class ManagedInterpolator
{
    property List<KeyValuePair<double, ManagedInterpolator^>^ Points = gcnew List<KeyValuePair<double, ManagedInterpolator^>();
};

class ManagedInterpolator_Const : public ManagedInterpolator
{
    property double DependentValue;
};

//  The function to mirror the managed data container into a native counterpart.
void CopyManagedPoints( ManagedInterpolator^ rhManagedInterpolator, cUnmanagedInterpolator* pUnmanagedInterpolator )
{
    //  Go through each managed point in the interpolator and add a corresponding unmanaged one.
    for each( auto point in rhManagedContainer->Points )
    {
        //  If this is a constant interpolator, just copy the values.
        if( dynamic_cast<ManagedInterpolator_Const^>( point->Value ) != nullptr )
        {
            //  Create a new unmanaged copy of the point.
            //  I even tried making x and r pointers and allocating the doubles on the heap with "new" to make sure they weren't somehow ending up as CLI types, but it didn't make a difference.
            double x = point->Key;
            double r = dynamic_cast<ManagedInterpolator_Const^>( point->Value )->DependentValue;
            std::pair<double, cUnmanagedInterpolator> newPoint( x, cUnmanagedInterpolator_Const( r ) );

            //  The unmanaged point data was looking weird, and this appeared to be where it was happening, so create a message with what it thinks the point is at this point.
            //  ***The next line is where the System.NullReferenceException is thrown.***
            std::string debugMessage = MakeString( newPoint.first ) + ", " + MakeString( dynamic_cast<cUnmanagedInterpolator_Const*>( &( newPoint.second ) )->GetValue() );

            //  Add the copy to the unmanaged interpolator.
            pUnmanagedInterpolator->GetPoints().push_back( newPoint );

            //  ***Trying to reference the newly created point by using pUnmanagedInterpolator->GetPoints().back() also results in an exception.

            //  Show the debug message to the user.
            AfxMessageBox( debugMessage.c_str() );
        }
        //  Otherwise, add a new base class interpolator.
        else
        {
            cUnmanagedInterpolator* pNewInterp = new cUnmanagedInterpolator();

            //  Recurse as deep as it goes.
            if( pNewInterp )
            {
                pUnmanagedInterpolator->GetPoints().push_back( std::make_pair( point->Key, std::move( *pNewInterp ) ) );
                CopyManagedPoints( point->Value, &( pUnmanagedInterpolator->GetPoints().back().second ) );
                delete pNewInterp;
                pNewInterp = nullptr;
            }
        }
    }
}

//  Roughly how the function would be used.
int main()
{
    ManagedInterpolator^ rhManagedInterpolator = gcnew ManagedInterpolator( /*initialization information*/ );

    cUnmanagedInterpolator* pNewInterp = new cUnmanagedInterpolator();

    CopyManagedPoints( rhManagedInterpolator, pNewInterp );

    //  Use the data...

    return 0;
}

异常发生在内部 if 语句中(cmets 前面有三个星号 ("***") 标记出现问题的位置)。

快速总结一下代码,以防不够清楚:

基本上,数据容器是一个“插值器”,其中包含成对的独立值和依赖值(“点”)。从属值可以是另一个插值器或标量值。作为插值器的依赖值可以根据需要深度递归,但必须以固定值结尾,该值是从插值器派生的类。

【问题讨论】:

    标签: c++ c++-cli


    【解决方案1】:

    您正在使用句柄 (^),因此这不是本机代码。问题是您的对newPoint 有一个cUnmanagedInterpolator 对象作为其第二个值,您尝试将dynamic_cast 转换为生成NullReferenceException 的行上的cUnmanagedInterpolator_Const。此强制转换将失败并返回 nullptr,取消引用时将导致异常。

    从根本上说,当您以 cUnmanagedInterpolator_Const 开始时,当您创建该对时,它切片cUnmanagedInterpolator,失去其作为 cUnmanagedInterpolator_Const 的身份并成为 cUnmanagedInterpolator

    【讨论】:

    • Argh...当然,当它通过值传递时它不会保持多态性(更具体地说,当按照this answer 调用复制构造函数时。大概这也适用于移动构造函数? )...我觉得自己很愚蠢;这可能就像 C++ 101 的第一天......无论如何,为什么它在访问本机对象时抛出 CLR 异常(整行仅使用本机构造)?它不应该抛出 C++ 或 SEH 异常(甚至是可能不存在的函数地址的访问冲突)吗?
    • 您正在使用 /clr 选项编译它,因此它不会生成本机代码。
    • @BoatsBoatsBoats:AFAIK,它确实会引发 SEH 异常,然后将其转换为处理程序链中某处的 NullReferenceException(可能仅当您的代码没有首先处理它时)。在调用本机函数时,没有任何地方可以禁用该翻译,特别是如果它发生在“最后一个”可能的处理程序中,转换本应未被处理的 SEH 异常......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-03
    • 1970-01-01
    • 2016-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多