将位图从C ++ DLL传递回C#时的行为不一致

时间:2012-02-18 23:15:00

标签: c# c++ dll bitmap

我有一个C#应用程序需要从C ++ DLL传回可变长度的数据。通常我解决这个问题如下:对于每个实体类型(例如字符串或位图),我有一个适当类型的持久C ++变量。然后C#app调用类似“QueryBitmapByName(string bitmapName)”的东西,而“契约”是C#必须在调用C ++ DLL中的另一个函数之前处理这个变量。这在过去总是有效,但我最近遇到了一个绊脚石,我不知道什么时候返回多个位图。在发布模式下,行为很好,但在调试模式下,如果快速连续查询位图,则位图传递无法正确复制像素数据。

C#代码片段:

[StructLayout(LayoutKind.Sequential), Serializable]
public struct BCBitmapInfo
{
    [MarshalAs(UnmanagedType.U4)] public int width;
    [MarshalAs(UnmanagedType.U4)] public int height;
    [MarshalAs(UnmanagedType.SysInt)] public IntPtr colorData;
}

const string BaseCodeDLL = "BaseCode.dll";
[DllImport(BaseCodeDLL)] private static extern IntPtr BCQueryBitmapByName(IntPtr context, [In, MarshalAs(UnmanagedType.LPStr)] String bitmapName);

private Bitmap GetBitmap(String bitmapName)
{
    IntPtr bitmapInfoUnmanaged = BCQueryBitmapByName(baseCodeDLLContext, bitmapName);
    if (bitmapInfoUnmanaged == (IntPtr)0) return null;

    BCBitmapInfo bitmapInfo = (BCBitmapInfo)Marshal.PtrToStructure(bitmapInfoUnmanaged, typeof(BCBitmapInfo));

    return new Bitmap(bitmapInfo.width, bitmapInfo.height, bitmapInfo.width * 4, System.Drawing.Imaging.PixelFormat.Format32bppRgb, bitmapInfo.colorData);
}

private void UpdateReplayImages()
{
    pictureBoxSprites.Image = (Image)GetBitmap("replaySprites");
    pictureBoxTiles.Image = (Image)GetBitmap("replayTiles");
}

C ++代码片段:

struct BCBitmapInfo
{
    UINT width;
    UINT height;
    BYTE *colorData;
};
class App
{
    ...
    BCBitmapInfo _queryBitmapInfo;
    Bitmap _queryBitmapDataA;
    Bitmap _queryBitmapDataB;
};

BCBitmapInfo* App::QueryBitmapByNameBad(const String &s)
{
    const Bitmap *resultPtr = &_queryBitmapDataA;

    if(s == "replayTiles") _queryBitmapDataA = MakeTilesBitmap();
    else if(s == "replaySprites") _queryBitmapDataA = MakeSpritesBitmap();

    _queryBitmapInfo.width = resultPtr->Width();
    _queryBitmapInfo.height = resultPtr->Height();
    _queryBitmapInfo.colorData = (BYTE*)resultPtr->Pixels();
    return &_queryBitmapInfo;
}

BCBitmapInfo* App::QueryBitmapByNameGood(const String &s)
{
    const Bitmap *resultPtr = NULL;

    if(s == "replayTiles")
    {
        resultPtr = &_queryBitmapDataA;
        _queryBitmapDataA = MakeTilesBitmap();
    }
    else if(s == "replaySprites")
    {
        resultPtr = &_queryBitmapDataB;
        _queryBitmapDataB = MakeSpritesBitmap();
    }

    _queryBitmapInfo.width = resultPtr->Width();
    _queryBitmapInfo.height = resultPtr->Height();
    _queryBitmapInfo.colorData = (BYTE*)resultPtr->Pixels();
    return &_queryBitmapInfo;
}

在调试模式下运行此代码并使用QueryBitmapByNameBad时,首先查询的位图将具有正确的尺寸,但像素数据将全部为浅绿色(第二个位图将呈现正常); QueryBitmapByNameGood工作正常,因为它为每个可能的查询使用不同的容器。我不明白为什么QueryBitmapByNameBad在单线程应用程序中行为不正确:不应该

new Bitmap(bitmapInfo.width, ..., bitmapInfo.colorData);

在它返回之前被强制复制bitmapInfo.colorData吗?是否有必要为每个可变长度查询创建一个新的“本地存储容器”?显然,在可以从多个线程调用的DLL的情况下,这很复杂,但对于单线程,上述方法似乎应该足够了。

1 个答案:

答案 0 :(得分:1)

该文档非常清楚Bitmap将继续使用该内存,直到它(Bitmap对象)被释放:

  

调用者负责分配和释放 scan0 参数指定的内存块。但是,在释放相关的Bitmap之前,不应释放内存。

来自Bitmap Constructor (Int32, Int32, Int32, PixelFormat, IntPtr)