Коллеги, подскажите!
Столкнулся с такой проблемой, когда несколько потоков грузят картинки из кусков памяти (не из файлов).
Создается 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);
}
Сделал аналогичный тест для загрузки IPicture из файла через OleLoadPicturePath.
При таком же количестве тредов ~100 наблюдается отказ в виде возврата E_FAIL.
Рецепт прекращения этого безобразия: поместить все время жизни объекта IPicture в критическую секцию.
Подготовка IStream может быть перед входом в критическую секцию — это не влияет.