2013-11-13 2 views
6

Я работаю над приложением, которое должно поддерживать архитектуру плагина. Это первый раз, когда я это сделал, поэтому я не совсем уверен, как мне нужно это делать.Создание интерфейса плагина

How to create some class from dll(constructor in dll)?(с++) Предлагает мне просто создать класс, состоящий из полностью виртуальных функций, и позволить DLL реализовать это в пользовательском классе и вернуть этот настраиваемый объект с помощью метода GetPluginObject() или тому подобного. Тем не менее, C++ DLL plugin interface говорит, что не будет достаточно, и что собственно (совместимый по нескольким составителей) подход потребует следующее:

  • только основные типы данных могут использоваться
  • Что-то вроде СОМ QueryInterface должны быть выставлены таким образом, плагин DLL, может правильно определить, какой интерфейс (ы) он реализует
  • Некоторые формы подсчета ссылок требуется
  • Все методы предпочтительно должны быть помечены как STDCALL
  • Любые Структуры должны быть предоставлены фиксированной выравнивания

Что мне нужно для плагина, достаточно просто: мне нужен только один массив структур, возвращаемых из одной функции.

struct InternalCommand 
{ 
    int commandValue; 
    std::wstring commandName; 
    std::wstring commandHandlerFunctionName; //I'm planning on using GetProcAddress with the provided function name to get the individual command handler 
} 

std::vector<InternalCommand> GetEmergeInternalCommands(); 

Учитывая ограничения и требования в приведенном выше списке, используя другой интерфейс этого проекта в качестве шаблона, кажется, мне нужно определить это следующим образом:

#define MAX_LINE_LENGTH 4096 

#ifdef __GNUC__ 
#define ALIGNOF(type) __alignof__(type) 
#else 
#define ALIGNOF(type) __alignof(type) 
#endif 

#ifdef __GNUC__ 
#define ALIGNED(size) __attribute__((aligned (size))) 
#else 
#define ALIGNED(size) __declspec(align(size)) 
#endif 

#include <windows.h> 

// {b78285af-c62f-4cff-9e15-f790a4a219ee} 
const IID IID_IEmergeInternalCommand = {0xB78285AF, 0xC62F, 0x4CFF, {0x9E, 0x15, 0xF7, 0x90, 0xA4, 0xA2, 0x19, 0xEE}}; 

#ifdef __cplusplus 
extern "C" 
{ 
#endif 

struct ALIGNED((ALIGNOF(int) + ALIGNOF(wchar_t) + ALIGNOF(wchar_t))) EmergeInternalCommandInformation 
{ 
    int commandValue; 
    wchar_t commandName[MAX_LINE_LENGTH]; 
    wchar_t commandHandlerFunctionName[MAX_LINE_LENGTH]; 
}; 

#undef INTERFACE 
#define INTERFACE IEmergeInternalCommandProvider 
DECLARE_INTERFACE_(IEmergeInternalCommandProvider, IUnknown) 
{ 
    STDMETHOD(QueryInterface)(THIS_ REFIID, LPVOID*) PURE; 
    STDMETHOD_(ULONG, AddRef)(THIS) PURE; 
    STDMETHOD_(ULONG, Release)(THIS) PURE; 

    STDMETHOD_(int, GetEmergeInternalCommandCount)(THIS) PURE; 
    STDMETHOD_(EmergeInternalCommandInformation, GetEmergeInternalCommandInformation)(THIS_ int) PURE; 
}; 
#undef INTERFACE 
typedef IEmergeInternalCommandProvider* LPEMERGEINTERNALCOMMANDPROVIDER; 

#ifdef __cplusplus 
} 
#endif 

А потом, на стороне хоста, я бы использовал GetProcAddress в DLL плагина для вызова DLL QueryInterface, затем с помощью указателя QueryInterface вернется к работе с плагином.

Это похоже на много излишних и многих уродливых, хотя. Например, я не думаю, что я могу правильно передать std :: vector в или из, поэтому я застрял, используя возврат одного элемента для GetEmergeInternalCommandInformation() и функцию полного счета GetEmergeInternalCommandCount(), чтобы я мог пропустить команды плагина один одним. Есть ли другой способ, я могу безопасно получить массив структуры как возвращаемое значение, не нарушая правила?

Кроме того, я не совсем уверен, что я определил правильно-структуру, как с точки зрения наличия в wchar_t массивов (я ограничен выделим wchar_t с?) И с точки зрения стоимости выравнивания.

Я также не совсем уверен в том, как предполагается реализовать эту DLL-плагин. Я думаю, что нужно только #include заголовок интерфейса, а затем создать класс, наследующий от интерфейса, не так ли?

#include "EmergeInternalCommandInterface.h" 
class EmergeInternalCommands : public IEmergeInternalCommandProvider 
//class definition goes here 

Я также не уверен, что мне нужно зарегистрировать этот интерфейс с помощью COM или если я могу просто использовать его. Интерфейс, который я использовал в качестве шаблона, является полнофункциональным COM-интерфейсом и зарегистрирован как таковой, но я не знаю, нужно ли мне что-то, что продвинулось для базовой системы плагинов.

И последнее, но не в последнюю очередь - я делаю этот путь более сложным, чем это должно быть?

+0

Еще один подход, который вы можете рассмотреть, - это не реализовать плагины с C++ вообще, а вместо этого использовать встроенный язык сценариев, такой как lua. Идея заключается в том, что вы связываете и выставляете базовые строительные блоки или примитивы в vm, чтобы плагин мог получить доступ Они сами могут быть либо чистым сценарием lua, либо модулем расширения lua, написанным на C или C++. Обычно требования не требуют, чтобы ваше приложение и плагин были написаны на одном языке. – greatwolf

+0

Пока я играя с идеей использования Lua, я собираюсь чтобы реализовать плагиновую систему на C++, поскольку мое приложение и его будущие плагины активно используют Windows API. Я не уверен, что Lua сможет провести здесь свое дело. – computerfreaker

+1

Взгляните на ffi LuaJIT - он позволяет вам легко связывать функции winapi прямо из lua.Обычно вам нужно предоставить предварительно обработанный заголовок, содержащий интересующие объявления функции win32. – greatwolf

ответ

4

Взгляните на мой проект cppcomponents на https://github.com/jbandela/cppcomponents.Я создал эту библиотеку специально для сценариев, таких как ваши, поскольку я нашел доступные в настоящее время решения.

Это библиотека C++ 11, предназначенная только для заголовков, которая работает в Windows и Linux.

Это требует довольно совместимый C++ компилятор 11 как MSVC 2013, gcc 4.7.2 или Clang 3.2

  • Он будет автоматически обрабатывать реализацию QueryInterface, AddRef и Release.
  • Когда вы используете его, подсчет ссылок выполняется автоматически
  • Это позволяет возвращать станд :: строку, вектор, кортеж, а также другие стандартные типы
  • Он может обрабатывать исключения
  • Вы можете использовать несколько компиляторы, например, вы можете написать программу в Visual C++ и писать плагины в GCC

Вот самый простой способ, чтобы написать то, что вы хотите

Сначала определяют интерфейс и plugi п в CommandProvider.h

#include <cppcomponents/cppcomponents.hpp> 
#include <tuple> 
#include <vector> 

typedef std::tuple<int, std::wstring, std::wstring> Command; 

struct ICommandProvider:cppcomponents::define_interface<cppcomponents::uuid<0xf4b4056d, 0x37a8, 0x4f32, 0x9eea, 0x03a31ed55dfa>> 
{ 
    std::vector<Command>GetEmergeInternalCommands(); 

    CPPCOMPONENTS_CONSTRUCT(ICommandProvider, GetEmergeInternalCommands) 
}; 

inline std::string CommandProviderId(){ return "CommandProvider"; } 
typedef cppcomponents::runtime_class<CommandProviderId, cppcomponents::object_interfaces<ICommandProvider>> CommandProvider_t; 
typedef cppcomponents::use_runtime_class<CommandProvider_t> CommandProvider; 

Тогда в ImplementCommandProvider.cpp, которые будут собраны в CommandProviderDll.dll

#include "CommandProvider.h" 

struct ImplementCommandProvider :cppcomponents::implement_runtime_class<ImplementCommandProvider, CommandProvider_t> 
{ 
ImplementCommandProvider(){} 


std::vector<Command>GetEmergeInternalCommands(){ 
    std::vector<Command> vec; 
    vec.push_back(std::make_tuple(1, L"Test", L"TestFunction")); 
    vec.push_back(std::make_tuple(2, L"Test2", L"TestFunction2")); 
    vec.push_back(std::make_tuple(3, L"Test3", L"TestFunction3")); 

    return vec; 
} 


}; 

CPPCOMPONENTS_REGISTER(ImplementCommandProvider) 
CPPCOMPONENTS_DEFINE_FACTORY() 

Вот как вы будете использовать его

#include "CommandProvider.h" 
#include <iostream> 


int main(){ 

    std::string dllName; 

    std::cout << "Enter dll name without the .dll extension\n"; 
    std::cin >> dllName; 

    auto p = CommandProvider::dynamic_creator(dllName, "CommandProvider")(); 

    for (auto& c : p.GetEmergeInternalCommands()){ 
     std::wcout << L"Value " << std::get<0>(c) << L" Name " << std::get<1>(c) << L" Function " << std::get<2>(c) << L"\n"; 

    } 


} 

Вот как ты построил бы его из командной строки Я предполагаю, что вы находитесь в каталоге с 3 файлами и что компилятор MSVC находится на вашем пути

Это как построить основную программу

cl MainProgram.cpp /I c:\Users\jrb\Source\Repos\cppcomponents /EHsc 

Это, как построить Dll

cl ImplementCommandProvider.cpp /I c:\Users\jrb\Source\Repos\cppcomponents /EHsc /link /dll /OUT:CommandProviderDll.dll 

Затем, когда вы запустите программу, введите в CommandProviderDll для DllName

Если вы хотите определить настраиваемую структуру, это возможно, и я могу вам помочь.

В настоящее время библиотека не располагает документацией (работает над ней :(), но я могу помочь вам с любыми вопросами о библиотеке. Библиотека выпущена под лицензией Boost, поэтому вы можете использовать ее для коммерческих приложений, если вы хочу

+0

Это именно то, что мне нужно, спасибо большое! Получил мой проект, который был создан с вашей библиотекой, и он работает хорошо. Просто предложение для вас, оберните VS-эксклюзивные '# pragma' в '#ifdef _MSC_VER', чтобы GCC и другие компиляторы, отличные от VS, не предупреждали об этом. – computerfreaker

+0

И если бы у вас был пример использования настраиваемой структуры, я бы определенно ее оценил. Я предвижу необходимость второго, несколько более сложного интерфейса для этого проекта в будущем, и я вовсе не уверен, что для этого будет достаточно 'std :: tuple'. – computerfreaker

+0

@computerfreaker Спасибо за ваши отзывы. Вот ссылка о том, как создавать пользовательские структуры. В качестве примера я использовал вашу начальную команду. https://github.com/jbandela/cppcomponents/wiki/Customtyty –

Смежные вопросы