我有一个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的情况下,这很复杂,但对于单线程,上述方法似乎应该足够了。
答案 0 :(得分:1)
该文档非常清楚Bitmap
将继续使用该内存,直到它(Bitmap
对象)被释放:
调用者负责分配和释放 scan0 参数指定的内存块。但是,在释放相关的Bitmap之前,不应释放内存。
来自Bitmap Constructor (Int32, Int32, Int32, PixelFormat, IntPtr)