I would like to create a dll library that will be loading in runtime. The library'll be using internally COM objects with MTA flag. The library will be created in main thread.
So I have question: Where there is best place where can I call 'CoInitializeEx' and 'CoUninitialize' functions. In the my dll(init/deinit functions) or client should call directly these functions?
I prefer first option. I would like avoid public dependig on COMs. Client shouldn't know I'm using COMs, but also I'd like avoid crashes when client unload my lib(then I call 'CoUninitialize' for my lib) and other libs(depend on COM) will be in undefined state.
EDIT:
ProviderApi.h
#ifndef API_H
#define API_H
class Provider;
define API __declspec(dllexport)
using ErrCode = int;
extern "C" {
API ErrCode init(Provider** provider);
API ErrCode deinit();
}
#endif
ProviderApi.cpp
#include <Objbase.h>
Provider* prov;
API ErrCode init(Provider** provider)
{
const auto res = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
// create provider
// then I can use any com object
*provider = prov;
}
API ErrCode deinit()
{
CoUninitialize();
}
client code
#include <Windows.h>
class Provider;
typedef ErrCode (*f_init)(Provider**);
typedef ErrCode (*f_deinit)();
int main(int /*argc*/, char** /*argv*/)
{
auto lib = LoadLibrary("my_lib.dll");
auto init = (f_init)GetProcAddress(lib, "init");
auto deinit = (f_deinit)GetProcAddress(lib, "deinit");
Provider* prov;
init(&prov);
// do something
deinit();
return 0;
}
UPDATE 28.02.2020:
OK, I finally found the solution of my problem. To hide the COM dependency, just create a separate thread and execute COM methods in it. Bellow there is sample of usage.
ComThread.h
#ifndef COM_THREAD_H
#define COM_THREAD_H
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
class ComThread
{
public:
ComThread();
ComThread(const ComThread&) = delete;
ComThread(ComThread&&) = delete;
~ComThread() noexcept;
ComThread& operator=(const ComThread&) = delete;
ComThread& operator=(ComThread&&) = delete;
void doTask(std::function<void(void)> comTask);
private:
std::thread m_thread;
std::mutex m_mutex;
std::mutex m_taskMutex;
std::condition_variable m_cv;
bool m_stop;
std::function<void(void)> m_currentTask;
void worker();
};
#endif // COM_THREAD_H
ComThread.cpp
#include "ComThread.h"
#include <cassert>
#include <objbase.h>
ComThread::ComThread()
: m_stop(false),
m_thread(std::thread(&ComThread::worker, this))
{
}
ComThread::~ComThread() noexcept
{
m_stop = true;
m_cv.notify_one();
if (m_thread.joinable())
m_thread.join();
}
void ComThread::doTask(std::function<void()> comTask)
{
std::lock_guard lock(m_taskMutex);
m_currentTask = comTask;
m_cv.notify_one();
std::unique_lock taskLock(m_mutex);
m_cv.wait(taskLock, [this] () { return !m_currentTask; });
}
void ComThread::worker()
{
const auto res = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
assert(res == S_OK);
while (true)
{
{
std::unique_lock lock(m_mutex);
m_cv.wait(lock, [this] () { return m_stop || m_currentTask; });
}
if (m_stop)
break;
assert(m_currentTask);
m_currentTask();
m_currentTask = nullptr;
m_cv.notify_one();
}
CoUninitialize();
}
main.cpp
#include "DeckLinkAPI_h.h
#include "ComThread.h"
int main(int /*argc*/, char** /*argv*/)
{
ComThread comThread;
IDeckLinkIterator* deckLinkIterator = nullptr;
HRESULT hr = S_OK;
comThread.doTash([&hr, &deckLinkIterator]() {
hr = CoCreateInstance(
CLSID_CDeckLinkIterator,
nullptr,
CLSCTX_ALL,
IID_IDeckLinkIterator,
reinterpret_cast<void**>(&deckLinkIterator));
});
if (hr != S_OK)
return -1;
// do other things
}