3

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
}

1 Answers1

1

Microsoft advises against calling these from the Dll load/unload functions:

Because there is no way to control the order in which in-process servers are loaded or unloaded, do not call CoInitialize, CoInitializeEx, or CoUninitialize from the DllMain function.

Reference

https://docs.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-coinitialize

Jerry Coffin
  • 44,795