Непоследовательное поведение при передаче растровых изображений из C ++ DLL обратно в C # - PullRequest
0 голосов
/ 19 февраля 2012

У меня есть приложение на 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, которые могут вызываться из нескольких потоков, но для однопоточного подхода, описанного выше, кажется, что этого должно быть достаточно.

1 Ответ

1 голос
/ 19 февраля 2012

Документация ясно показывает, что Bitmap будет продолжать использовать эту память до тех пор, пока она (объект Bitmap) не будет удалена:

Вызывающий отвечает за выделение и освобождение блокапамяти, указанной параметром scan0 .Однако память не должна освобождаться до тех пор, пока не будет выпущено соответствующее растровое изображение.

из Конструктор растровых изображений (Int32, Int32, Int32, PixelFormat, IntPtr)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...