проблемы с OleLoadPicture и threads
От: qaz77  
Дата: 09.11.21 14:03
Оценка: 5 (1)
Коллеги, подскажите!

Столкнулся с такой проблемой, когда несколько потоков грузят картинки из кусков памяти (не из файлов).
Создается IStream функцией CreateStreamOnHGlobal. При каком-то количестве одновременно существующих объектов IStream (созданных с разными HGLOBAL)
функция OleLoadPicture начинает возвращать STG_E_REVERTED (0x80030102).

Что это может быть?
Если поместить функцию test_load_image_from_mem_ в критическую секцию, то проблема уходит...

При количестве threads 100 у меня через раз срабатывает assert у OleLoadPicture.
При количестве 1000 — гарантировано.
Картинка задана в коде как массив байт. Это валидная BMP 16x16 4bpp.
Вот минимальный пример:

#include <windows.h>
#include <ole2.h>
#include <olectl.h>

#include <cassert>

void test_load_image_from_mem_(int thread_num, const void* image_data, size_t data_size)
{
    HGLOBAL hmem = ::GlobalAlloc(GMEM_MOVEABLE, data_size);
    assert(hmem);
    void* pmem = ::GlobalLock(hmem);
    memcpy(pmem, image_data, data_size);
    ::GlobalUnlock(hmem);

    IStream* ps = nullptr;
    HRESULT hr = ::CreateStreamOnHGlobal(hmem, TRUE, &ps);
    assert(SUCCEEDED(hr));
    assert(ps);

    IPicture* pic_ptr = nullptr;
    hr = ::OleLoadPicture(ps, static_cast<LONG>(data_size), FALSE, IID_IPicture, (void**)&pic_ptr);
    assert(SUCCEEDED(hr));
    assert(pic_ptr);
    ps->Release();

    pic_ptr->Release();
}

const char TestData[246] = {
  0x42u, 0x4Du, 0xF6u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x76u, 0x00u, 0x00u, 0x00u, 0x28u, 0x00u, 
  0x00u, 0x00u, 0x10u, 0x00u, 0x00u, 0x00u, 0x10u, 0x00u, 0x00u, 0x00u, 0x01u, 0x00u, 0x04u, 0x00u, 0x00u, 0x00u, 
  0x00u, 0x00u, 0x80u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
  0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x80u, 0x00u, 0x00u, 0x80u, 
  0x00u, 0x00u, 0x00u, 0x80u, 0x80u, 0x00u, 0x80u, 0x00u, 0x00u, 0x00u, 0x80u, 0x00u, 0x80u, 0x00u, 0x80u, 0x80u, 
  0x00u, 0x00u, 0xC0u, 0xC0u, 0xC0u, 0x00u, 0x80u, 0x80u, 0x80u, 0x00u, 0x00u, 0x00u, 0xFFu, 0x00u, 0x00u, 0xFFu, 
  0x00u, 0x00u, 0x00u, 0xFFu, 0xFFu, 0x00u, 0xFFu, 0x00u, 0x00u, 0x00u, 0xFFu, 0x00u, 0xFFu, 0x00u, 0xFFu, 0xFFu, 
  0x00u, 0x00u, 0xFFu, 0xFFu, 0xFFu, 0x00u, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 

  0xDDu, 0xDDu, 0xDDu, 0xDDu, 0x00u, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xD0u, 0xFFu, 0x0Du, 0xDDu, 0xDDu, 
  0xDDu, 0xDDu, 0xDDu, 0xD0u, 0xFFu, 0x0Du, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xD0u, 0x00u, 0xDDu, 0xDDu, 0xDDu, 
  0xDDu, 0xDDu, 0xDDu, 0x0Du, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xD0u, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 
  0xDDu, 0xDDu, 0x0Du, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xD0u, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 
  0xDDu, 0x0Du, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xD0u, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0x00u, 
  0x0Du, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xD0u, 0xFFu, 0x0Du, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xD0u, 0xFFu, 
  0x0Du, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0x00u, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 
  0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, 0xDDu, };


DWORD WINAPI thread_proc_(LPVOID lpParameter)
{
    const int thread_num = (int)lpParameter;

    ::OleInitialize(nullptr);

    test_load_image_from_mem_(thread_num, TestData, sizeof(TestData));

    ::OleUninitialize();
    return 0;
}

HANDLE start_thread_(int num)
{
    return ::CreateThread(nullptr, 0, thread_proc_, (LPVOID)num, 0, nullptr);
}

void start_threads_(int count)
{
    HANDLE* harr = new HANDLE[count];

    for (int num = 0; num < count; ++num)
        harr[num] = start_thread_(num);

    ::WaitForMultipleObjects(count, harr, TRUE, INFINITE);

    for (int num = 0; num < count; ++num)
        ::CloseHandle(harr[num]);
    delete[] harr;
}

int CALLBACK WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine,
                     int nCmdShow)
{
    ::MessageBox(NULL, L"Ready to start", L"TestOleLoadPicture", MB_OK);
    start_threads_(100);
    ::MessageBox(NULL, L"Everything OK!", L"TestOleLoadPicture", MB_OK);
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.