У меня есть приложение на C #, которое должно получать данные переменной длины, передаваемые обратно из C ++ DLL.Обычно я решаю эту проблему следующим образом: для каждого типа объекта (например, строки или растрового изображения) у меня есть постоянная переменная C ++ соответствующего типа.Затем приложение C # вызывает что-то вроде «QueryBitmapByName (string bitmapName)», и «контракт» заключается в том, что C # должен обработать эту переменную, прежде чем он вызовет другую функцию в C ++ DLL.Это всегда работало в прошлом, но недавно я наткнулся на камень преткновения, который я не понимаю, возвращая несколько растровых изображений.В режиме Release поведение нормальное, но в режиме отладки при передаче растрового изображения не удается правильно скопировать данные пикселя, если растровые запросы запрашиваются в быстрой последовательности.
Фрагмент кода 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, которые могут вызываться из нескольких потоков, но для однопоточного подхода, описанного выше, кажется, что этого должно быть достаточно.