Всем привет!
Оказалось что со сборкой OLE для Windows возникают сложности. Я и подумать не мог что на это уйдёт такое большое количество времени и сил. Надеждой что сиё усилие не бесполезно и способно хоть кому то помочь или избавить от лишних мучений и является эта статья.
Эта инструкция крайне далека от совершенства и является просто набором последовательных действий приводящих к рабочему результату.
Начнём с плохой новости. Нам понадобится виртуалка с Windows и установленной внутри VisualStudio. Всё это нужно для того что бы из .tlb файла сгенерировать заголовочный h файл с GUID ‘ами классов и интерфейсов. Скорее всего это можно сделать с помощью WIDL или какого то другого инструмента без лишних усилий, но я пока не знаю как, кто знает напишите в комментариях. Одно могу скачать точно, с помощью Qt‘шной dumpcpp у меня ничего не получилось. В общем вот как это делаю я: Создаём проект, пишем в cpp файле строку:
#import "E:\kirill\Documents\Visual Studio 2013\Projects\API\Edm.tlb" raw_interfaces_only, raw_native_types, no_namespace, named_guids, auto_search
После чего нажимаем Скомпилировать и получаем в конечной папке два файла edm.tlh и edm.tli, забираем их.
Затем их нужно подредактировать, рекомендую gedit по тому что знаю как в нём заменять 10+ вхождения.
gedit edm.tlh
Нажимает поиск и замена, включаем регулярные выражения и меняем всё что не компилируется в MinGW:
- Ищем: «^enum \w+;» меняем на «»
- Ищем: «struct __declspec\(uuid\(\»(\w+)-(‘w+)-(\w+)-(\w{2})(\w{2})-(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})\»))\r\n\/\*([\s\w]+)\*\/ (\w+);» меняем на «struct DECLSPEC_UUID(«\1-\2-\3-\4\5-\6\7\8\9\10\11»)\r\n/*\12*/ \13;\r\n__CRT_UUID_DECL(\13, 0x\1, 0x\2, 0x\3, 0x\4,0x\5, 0x\6,0x\7,0x\8,0x\9,0x\10,0x\11)«
- Ищем: «struct __declspec\(uuid\(\»(\w+)-(‘w+)-(\w+)-(\w{2})(\w{2})-(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})\»))\r\n(\w+);» меняем на «struct DECLSPEC_UUID(«\1-\2-\3-\4\5-\6\7\8\9\10\11»)\r\n\12;\r\n__CRT_UUID_DECL(\12, 0x\1, 0x\2, 0x\3, 0x\4,0x\5, 0x\6,0x\7,0x\8,0x\9,0x\10,0x\11)«
Примечание: Замена в виде 0x\10 именно так работает только в gedit на сколько я понял во всех других как то по другому, возможно 0x$10 или 0x\{10}
Ещё возможно придётся выкосить property ну и ещё что то что я забыл, но пункты 2 и 3 обязательно нужно сделать что бы работал __uuidof.
Создаём проект C++ библиотеки:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
QT -= gui QT += widgets axserver TARGET = PDMToolAddIn TEMPLATE = lib CONFIG += warn_off dll RC_FILE = pdmtooladdin.rc DEF_FILE = pdmtooladdin.def SOURCES += main.cpp \ mytooladdin.cpp HEADERS += \ mytooladdin.h \ edm.h DISTFILES += edm.tli unix { target.path = /usr/lib INSTALLS += target } |
Дальше файлы взятые из примеров и иконка зачем то, наверное файл rc не нужен:
1 2 |
1 TYPELIB "pdmtooladdin.rc" 1 ICON DISCARDABLE "pdmtooladdin.ico" |
1 2 3 4 5 6 |
EXPORTS DllCanUnloadNow PRIVATE DllGetClassObject PRIVATE DllRegisterServer PRIVATE DllUnregisterServer PRIVATE DumpIDL PRIVATE |
Файл main.cpp:
1 2 3 4 5 6 7 8 |
#include "mytooladdin.h" #include <QAxFactory> QAXFACTORY_BEGIN( "{e8680220-c895-4ab2-bddb-cf16baa27f3f}", // type library ID "{3c1b9157-5f01-45ed-8262-a7413e67ce95}") // application ID QAXCLASS(ToolAddIn) QAXFACTORY_END() |
И ещё два файла:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#ifndef MYTOOLADDIN_H #define MYTOOLADDIN_H #include <QAxBindable> class IToolAddIn; class ToolAddIn : public QObject, public QAxBindable { Q_OBJECT Q_CLASSINFO("ClassID", "{e8680220-c895-4ab2-bddb-cf16baa27f3f}") Q_CLASSINFO("InterfaceID", "{3c1b9157-5f01-45ed-8262-a7413e67ce95}") public: explicit ToolAddIn(QObject* parent = nullptr); QAxAggregated *createAggregate() override; }; #endif // MYTOOLADDIN_H |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
#include <QAxAggregated> #include <QUuid> #include <objsafe.h> #include "edm.h" #include "mytooladdin.h" // ------------------------------------------------------------------ __CRT_UUID_DECL(IToolAddIn, 0x3c1b9157, 0x5f01, 0x45ed, 0x82, 0x62, 0xa7, 0x41, 0x3e, 0x67, 0xce, 0x95) class IToolAddIn : public QAxAggregated, public IEdmAddIn5 { public: long queryInterface(const QUuid &iid, void **iface) override { *iface = 0; if (iid == IID_IUnknown || iid == __uuidof(this) || iid == __uuidof(IEdmAddIn5)) *iface = dynamic_cast<IEdmAddIn5*>(this); else return E_NOINTERFACE; AddRef(); return S_OK; } QAXAGG_IUNKNOWN STDMETHOD(GetAddInInfo)(EdmAddInInfo * poInfo, IEdmVault5 * poVault, IEdmCmdMgr5 * poCmdMgr) { if (poInfo == NULL || poCmdMgr == NULL ) return E_POINTER; //Return some information to the Properties dialog box poInfo->mbsAddInName= SysAllocString( L"My first add-in" ); poInfo->mbsCompany = SysAllocString( L"The name of my company" ); poInfo->mbsDescription= SysAllocString( L"This is a very nice add-in." ); poInfo->mlAddInVersion = 1; //SolidWorks Enterprise PDM 5.2 is required by this add-in poInfo->mlRequiredVersionMajor = 5; poInfo->mlRequiredVersionMinor= 2; //Add hooks and menu commands to SolidWorks Enterprise PDM //Below is a menu command that appears in the Tools //and context-sensitive menus of a vault in Windows Explorer poCmdMgr->AddCmd( 1, bstr_t(L"My first menu command"), EdmMenu_Nothing, bstr_t(L""), bstr_t(L""), 0, 0 ); return S_OK; } STDMETHOD(OnCmd)(EdmCmd * poCmd, SAFEARRAY * * ppoData) { if (poCmd == NULL ||ppoData == NULL) return E_POINTER; MessageBox((HWND)poCmd->mlParentWnd, L"Hello World!", L"SolidWorks Enterprise PDM", MB_OK ); return S_OK; } }; // ------------------------------------------------------------------ ToolAddIn::ToolAddIn(QObject *parent) : QObject(parent) {} QAxAggregated *ToolAddIn::createAggregate() { return new IToolAddIn; } |
Дальше устанавливаем MXE и всё что нам нужно:
1 2 3 4 5 6 7 |
git clone https://github.com/mxe/mxe.git cd mxe echo " JOBS := 6 MXE_TARGETS := i686-w64-mingw32.static x86_64-w64-mingw32.static " > settings.mk make -j6 widl qtactiveqt |
Теперь фиксим непонятную ошибку в ActiveQt связанную с регистрацией OLE сервера и режимом поточности. Как вносить изменения в MXE можно посмотреть тут.
Заходим в папку с нашим проектом, создаём папку и пробуем в ней компилировать:
1 2 3 4 |
mkdir build cd build x86_64-w64-mingw32.static-qmake-qt5 .. make -j6 |
Видим ошибку: «..qt5/bin/idc: Команда не найдена» Это нормально. Копируем строку над ошибкой и выполняем через wine используя свои пути и имена. Генерируем PDMToolAddIn.idl: wine /mnt/second_drive/build/mxe_new/usr/x86_64-w64-mingw32.static/qt5/bin/idc release/PDMToolAddIn.dll /idl release//PDMToolAddIn.idl -version 1.0
Теперь открываем вновь сгенерированный файл и комментируем следующие строки:
1 2 |
//importlib("stdole32.tlb"); //importlib("stdole2.tlb"); |
Зоздаём файл PDMToolAddIn.tlb: x86_64-w64-mingw32.static-widl -t -o release//PDMToolAddIn.tlb release//PDMToolAddIn.idl
И лепим этот файл к нашей библиотеке: wine /mnt/second_drive/build/mxe_new/usr/x86_64-w64-mingw32.static/qt5/bin/idc release/PDMToolAddIn.dll /tlb release//PDMToolAddIn.tlb
Готово, наш AddIn модуль готов к использованию. Если будут вопросы, буду рад ответить на них в комментариях.