C ++ одиночный класс с DLL - PullRequest
       30

C ++ одиночный класс с DLL

16 голосов
/ 04 августа 2011

Я создал статическую библиотеку с классом:

class CLog
{
   private:
   CLog();
   ...
   ...
   public:
   static CLog& GetInstance()                                
   {
           static CLog Instance;
           return Instance;
   }
   void Write(char *cpPr);
};
#define Log CLog::GetInstance()

Эта библиотека связана с DLL и основной программой. DLL загружается с помощью LoadLibrary. В этом случае очевидно, что вызов Log.Write в основном exe и в dll создает два отдельных экземпляра CLog. Любые идеи, как обойти эту проблему и по-прежнему обеспечить динамическую загрузку DLL?

Ответы [ 3 ]

10 голосов
/ 04 августа 2011

Проблема в том, что каждый проект, который связывает статическую библиотеку, будь то основная программа или DLL, получит отдельную копию статической переменной. Это нарушает типичный метод создания синглтона.

Самый простой способ обойти это - создать другую DLL, содержащую синглтон, а не статическую библиотеку. Поскольку только один выход компоновщика будет содержать статическую переменную, проблема решена.

В моем собственном случае я создал одноэлементный менеджер, который идентифицировал каждый синглтон по уникальному GUID и гарантировал, что только одна копия существовала во всем приложении. Менеджер-синглтон существовал как его собственная DLL.

1 голос
/ 04 августа 2011

Метод, который я использовал, состоял в том, чтобы экспортировать функцию с именем GetLogger из EXE, которая предоставляет указатель на синглтон.Реализация GetInstance () зависит от определения препроцессора _USRDLL.Когда _USRDLL установлен (для компиляции DLL), GetInstance () вызывает GetModuleHandle (), чтобы получить дескриптор EXE и загружает функцию GetLogger.Вот код, основанный на вашем примере:

Статическая библиотека имеет Log.h:

class Log
{
  private:
  Log();

  public:
  ~Log();

  static Log& GetInstance()                                
  {                 
  #ifdef _USRDLL
    typedef Log* (*GetLoggerFn)();
    HMODULE mod = GetModuleHandle( NULL );
    GetLoggerFn getLogger = (GetLoggerFn)::GetProcAddress( mod, "GetLogger" );
    Log* Instance = getLogger();
    return *Instance;
  #else
    static Log Instance;
    return Instance;
  #endif
  }
  void Write(const std::string& str );
};
#define LOG Log::GetInstance()

Статическая библиотека имеет Log.cpp:

#include "Log.h"

void Log::Write(const std::string& str )
{
    std::cout << this << "  " << str << std::endl;
}

Log::Log()
{
}

Log::~Log()
{
    std::cout << "Log destroyed" << std::endl;
}

DLL просто имеетЛог оператора в DllMain:

#include "../static/Log.h"
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    LOG.Write("Hello from dll");
    return TRUE;
}

И EXE выглядит так:

#include "stdafx.h"
#include "../static/Log.h"
#include <Windows.h>
extern "C"
{
    __declspec( dllexport ) Log* GetLogger()
    {
        return &LOG;
    }
}


int _tmain(int argc, _TCHAR* argv[])
{
    LOG.Write("Hello from exe");
    HMODULE mod = ::LoadLibraryA( "../Debug/tdll.dll");
    ::FreeLibrary( mod );
    LOG.Write("unloaded library");
    return 0;
}
0 голосов
/ 04 августа 2011

Если вместо этого вы используете статический член класса (в отличие от использования статически размещенной локальной переменной), я считаю, что это создаст только один экземпляр (я не могу проверить это в данный момент). Что-то вроде:

class CLog
{

private:
    CLog();

    static CLog instance;

public:

    static CLog & GetInstance( )
    {
        return instance;
    }

    void Write(char *cpPr);

};

CLog CLog::instance; // I believe this is also necessary

Редактировать

Хорошо, Марк указал, что это тоже не работает. DLL и EXE по-прежнему получают разные экземпляры CLog. Надо еще кое-что подумать.

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