Add ActiveQt-based COM interop helper

This commit is contained in:
Mike Gelfand 2015-12-16 20:01:03 +00:00
parent 35b08ce09e
commit e92449d91f
10 changed files with 350 additions and 19 deletions

View File

@ -223,7 +223,8 @@ if(ENABLE_QT)
set(QT_TARGETS)
if(USE_QT5)
set(QT5_REQUIRED_MODULES Core Gui Widgets Network DBus LinguistTools)
set(QT5_REQUIRED_MODULES Core Gui Widgets Network LinguistTools)
set(QT5_OPTIONAL_MODULES DBus AxContainer AxServer)
foreach(M ${QT5_REQUIRED_MODULES})
find_package(Qt5${M} QUIET)
if(Qt5${M}_FOUND)
@ -235,9 +236,18 @@ if(ENABLE_QT)
break()
endif()
endforeach()
if(QT_TARGETS)
foreach(M ${QT5_OPTIONAL_MODULES})
find_package(Qt5${M} QUIET)
if(Qt5${M}_FOUND)
list(APPEND QT_TARGETS Qt5::${M})
endif()
endforeach()
endif()
else()
set(QT4_REQUIRED_MODULES QtCore QtGui QtNetwork QtDBus)
find_package(Qt4 4.6.2 QUIET COMPONENTS ${QT4_REQUIRED_MODULES})
set(QT4_REQUIRED_MODULES QtCore QtGui QtNetwork)
set(QT4_OPTIONAL_MODULES QtDBus QAxContainer QAxServer)
find_package(Qt4 4.6.2 QUIET COMPONENTS ${QT4_REQUIRED_MODULES} OPTIONAL_COMPONENTS ${QT4_OPTIONAL_MODULES})
foreach(M ${QT4_REQUIRED_MODULES})
string(TOUPPER "${M}" M_UPPER)
if(QT_${M_UPPER}_FOUND)
@ -247,6 +257,14 @@ if(ENABLE_QT)
break()
endif()
endforeach()
if (QT_TARGETS)
foreach(M ${QT4_OPTIONAL_MODULES})
string(TOUPPER "${M}" M_UPPER)
if(QT_${M_UPPER}_FOUND)
list(APPEND QT_TARGETS Qt4::${M})
endif()
endforeach()
endif()
endif()
set(QT_FOUND ON)

View File

@ -10,9 +10,6 @@
#include <ctime>
#include <iostream>
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusReply>
#include <QIcon>
#include <QLibraryInfo>
#include <QMessageBox>
@ -20,6 +17,12 @@
#include <QRect>
#include <QSystemTrayIcon>
#ifdef QT_DBUS_LIB
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusReply>
#endif
#include <libtransmission/transmission.h>
#include <libtransmission/tr-getopt.h>
#include <libtransmission/utils.h>
@ -27,8 +30,8 @@
#include "AddData.h"
#include "Application.h"
#include "DBusInteropHelper.h"
#include "Formatter.h"
#include "InteropHelper.h"
#include "MainWindow.h"
#include "OptionsDialog.h"
#include "Prefs.h"
@ -157,7 +160,7 @@ Application::Application (int& argc, char ** argv):
// try to delegate the work to an existing copy of Transmission
// before starting ourselves...
DBusInteropHelper interopClient;
InteropHelper interopClient;
if (interopClient.isConnected ())
{
bool delegated = false;
@ -175,11 +178,7 @@ Application::Application (int& argc, char ** argv):
default: break;
}
if (metainfo.isEmpty ())
continue;
const QVariant result = interopClient.addMetainfo (metainfo);
if (result.isValid () && result.toBool ())
if (!metainfo.isEmpty () && interopClient.addMetainfo (metainfo))
delegated = true;
}
@ -294,7 +293,7 @@ Application::Application (int& argc, char ** argv):
for (const QString& filename: filenames)
addTorrent (filename);
DBusInteropHelper::registerObject (this);
InteropHelper::registerObject (this);
}
void
@ -542,6 +541,7 @@ Application::raise ()
bool
Application::notifyApp (const QString& title, const QString& body) const
{
#ifdef QT_DBUS_LIB
const QLatin1String dbusServiceName ("org.freedesktop.Notifications");
const QLatin1String dbusInterfaceName ("org.freedesktop.Notifications");
const QLatin1String dbusPath ("/org/freedesktop/Notifications");
@ -564,6 +564,7 @@ Application::notifyApp (const QString& title, const QString& body) const
if (replyMsg.isValid () && replyMsg.value () > 0)
return true;
}
#endif
myWindow->trayIcon ().showMessage (title, body);
return true;
@ -582,6 +583,8 @@ int
tr_main (int argc,
char * argv[])
{
InteropHelper::initialize ();
Application app (argc, argv);
return app.exec ();
}

View File

@ -22,11 +22,26 @@ else()
endmacro()
endif()
set(ENABLE_COM_INTEROP OFF)
if(MSVC AND ((Qt5AxContainer_FOUND AND Qt5AxServer_FOUND) OR (QT_QAXCONTAINER_FOUND AND QT_QAXSERVER_FOUND)))
set(ENABLE_COM_INTEROP ON)
endif()
set(ENABLE_DBUS_INTEROP OFF)
if(Qt5DBus_FOUND OR QT_QTDBUS_FOUND)
set(ENABLE_DBUS_INTEROP ON)
endif()
if(NOT ENABLE_COM_INTEROP AND NOT ENABLE_DBUS_INTEROP)
message(FATAL_ERROR "Neither D-Bus nor COM interop is possible")
endif()
set(${PROJECT_NAME}_SOURCES
AboutDialog.cc
AddData.cc
Application.cc
ColumnResizer.cc
ComInteropHelper.cc
DBusInteropHelper.cc
DetailsDialog.cc
FaviconCache.cc
@ -42,6 +57,7 @@ set(${PROJECT_NAME}_SOURCES
Formatter.cc
FreeSpaceLabel.cc
IconToolButton.cc
InteropHelper.cc
InteropObject.cc
LicenseDialog.cc
MainWindow.cc
@ -69,12 +85,20 @@ set(${PROJECT_NAME}_SOURCES
WatchDir.cc
)
if (NOT ENABLE_COM_INTEROP)
set_source_files_properties(ComInteropHelper.cc PROPERTIES HEADER_FILE_ONLY ON)
endif()
if (NOT ENABLE_DBUS_INTEROP)
set_source_files_properties(DBusInteropHelper.cc PROPERTIES HEADER_FILE_ONLY ON)
endif()
set(${PROJECT_NAME}_HEADERS
AboutDialog.h
AddData.h
Application.h
BaseDialog.h
ColumnResizer.h
ComInteropHelper.h
CustomVariantType.h
DBusInteropHelper.h
DetailsDialog.h
@ -91,6 +115,7 @@ set(${PROJECT_NAME}_HEADERS
Formatter.h
FreeSpaceLabel.h
IconToolButton.h
InteropHelper.h
InteropObject.h
LicenseDialog.h
MainWindow.h
@ -182,17 +207,28 @@ include_directories(
${EVENT2_INCLUDE_DIRS}
)
add_definitions(
"-DTRANSLATIONS_DIR=\"${CMAKE_INSTALL_FULL_DATADIR}/${TR_NAME}/translations\""
-DQT_NO_CAST_FROM_ASCII
)
tr_win32_app_info(${PROJECT_NAME}_WIN32_RC_FILE
"Transmission Qt Client"
"transmission-qt"
"transmission-qt.exe"
"qtr.ico")
if(ENABLE_COM_INTEROP)
find_program(MIDL_EXECUTABLE midl)
add_custom_command(
OUTPUT
${CMAKE_CURRENT_BINARY_DIR}/transmission-qt.tlb
COMMAND
${MIDL_EXECUTABLE} /tlb ${CMAKE_CURRENT_BINARY_DIR}/transmission-qt.tlb transmission-qt.idl
DEPENDS
transmission-qt.idl
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}
)
list(APPEND ${PROJECT_NAME}_WIN32_RC_FILE transmission-qt.tlb.rc transmission-qt.idl ${CMAKE_CURRENT_BINARY_DIR}/transmission-qt.tlb)
set_source_files_properties(transmission-qt.idl ${CMAKE_CURRENT_BINARY_DIR}/transmission-qt.tlb PROPERTIES HEADER_FILE_ONLY ON)
endif()
add_executable(${TR_NAME}-qt WIN32
${${PROJECT_NAME}_SOURCES}
${${PROJECT_NAME}_UI_SOURCES}
@ -209,6 +245,12 @@ target_link_libraries(${TR_NAME}-qt
${EVENT2_LIBRARIES}
)
target_compile_definitions(${TR_NAME}-qt PRIVATE
"TRANSLATIONS_DIR=\"${CMAKE_INSTALL_FULL_DATADIR}/${TR_NAME}/translations\""
QT_NO_CAST_FROM_ASCII
$<$<BOOL:${ENABLE_COM_INTEROP}>:ENABLE_COM_INTEROP>
$<$<BOOL:${ENABLE_DBUS_INTEROP}>:ENABLE_DBUS_INTEROP>)
if(MSVC)
tr_append_target_property(${TR_NAME}-qt LINK_FLAGS "/ENTRY:mainCRTStartup")
endif()

68
qt/ComInteropHelper.cc Normal file
View File

@ -0,0 +1,68 @@
/*
* This file Copyright (C) 2015 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
*
* $Id$
*/
#include <windows.h>
#include <objbase.h>
#include <QAxFactory>
#include <QAxObject>
#include <QString>
#include <QVariant>
#include "ComInteropHelper.h"
#include "InteropObject.h"
QAXFACTORY_BEGIN("{1e405fc2-1a3a-468b-8bd6-bfbb58770390}", "{792d1aac-53cc-4dc9-bc29-e5295fdb93a9}")
QAXCLASS(InteropObject)
QAXFACTORY_END()
// These are ActiveQt internals; declaring here as I don't like their WinMain much...
extern HANDLE qAxInstance;
extern bool qAxOutProcServer;
extern wchar_t qAxModuleFilename[MAX_PATH];
extern QString qAxInit();
ComInteropHelper::ComInteropHelper ():
m_client (new QAxObject (QLatin1String ("Transmission.QtClient")))
{
}
ComInteropHelper::~ComInteropHelper ()
{
}
bool
ComInteropHelper::isConnected () const
{
return !m_client->isNull ();
}
QVariant
ComInteropHelper::addMetainfo (const QString& metainfo)
{
return m_client->dynamicCall ("AddMetainfo(QString)", metainfo);
}
void
ComInteropHelper::initialize ()
{
qAxOutProcServer = true;
::GetModuleFileNameW (0, qAxModuleFilename, MAX_PATH);
qAxInstance = ::GetModuleHandleW (NULL);
::CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
qAxInit ();
}
void
ComInteropHelper::registerObject (QObject * parent)
{
QAxFactory::startServer();
QAxFactory::registerActiveObject(new InteropObject (parent));
}

37
qt/ComInteropHelper.h Normal file
View File

@ -0,0 +1,37 @@
/*
* This file Copyright (C) 2015 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
*
* $Id$
*/
#ifndef QTR_COM_INTEROP_HELPER_H
#define QTR_COM_INTEROP_HELPER_H
#include <memory>
class QAxObject;
class QObject;
class QString;
class QVariant;
class ComInteropHelper
{
public:
ComInteropHelper ();
~ComInteropHelper ();
bool isConnected () const;
QVariant addMetainfo (const QString& metainfo);
static void initialize ();
static void registerObject (QObject * parent);
private:
std::unique_ptr<QAxObject> m_client;
};
#endif // QTR_COM_INTEROP_HELPER_H

70
qt/InteropHelper.cc Normal file
View File

@ -0,0 +1,70 @@
/*
* This file Copyright (C) 2015 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
*
* $Id$
*/
#include <QVariant>
#include "InteropHelper.h"
bool
InteropHelper::isConnected () const
{
#ifdef ENABLE_DBUS_INTEROP
if (myDbusClient.isConnected ())
return true;
#endif
#ifdef ENABLE_COM_INTEROP
if (myComClient.isConnected ())
return true;
#endif
return false;
}
bool
InteropHelper::addMetainfo (const QString& metainfo)
{
#ifdef ENABLE_DBUS_INTEROP
{
const QVariant response = myDbusClient.addMetainfo (metainfo);
if (response.isValid () && response.toBool ())
return true;
}
#endif
#ifdef ENABLE_COM_INTEROP
{
const QVariant response = myComClient.addMetainfo (metainfo);
if (response.isValid () && response.toBool ())
return true;
}
#endif
return false;
}
void
InteropHelper::initialize ()
{
#ifdef ENABLE_COM_INTEROP
ComInteropHelper::initialize ();
#endif
}
void
InteropHelper::registerObject (QObject * parent)
{
#ifdef ENABLE_DBUS_INTEROP
DBusInteropHelper::registerObject (parent);
#endif
#ifdef ENABLE_COM_INTEROP
ComInteropHelper::registerObject (parent);
#endif
}

43
qt/InteropHelper.h Normal file
View File

@ -0,0 +1,43 @@
/*
* This file Copyright (C) 2015 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
*
* $Id$
*/
#ifndef QTR_INTEROP_HELPER_H
#define QTR_INTEROP_HELPER_H
#ifdef ENABLE_COM_INTEROP
#include "ComInteropHelper.h"
#endif
#ifdef ENABLE_DBUS_INTEROP
#include "DBusInteropHelper.h"
#endif
class QAxObject;
class QString;
class QVariant;
class InteropHelper
{
public:
bool isConnected () const;
bool addMetainfo (const QString& metainfo);
static void initialize ();
static void registerObject (QObject * parent);
private:
#ifdef ENABLE_DBUS_INTEROP
DBusInteropHelper myDbusClient;
#endif
#ifdef ENABLE_COM_INTEROP
ComInteropHelper myComClient;
#endif
};
#endif // QTR_INTEROP_HELPER_H

View File

@ -15,7 +15,18 @@
class InteropObject: public QObject
{
Q_OBJECT
#ifdef ENABLE_DBUS_INTEROP
Q_CLASSINFO ("D-Bus Interface", "com.transmissionbt.Transmission")
#endif
#ifdef ENABLE_COM_INTEROP
Q_CLASSINFO ("ClassID", "{0e2c952c-0597-491f-ba26-249d7e6fab49}")
Q_CLASSINFO ("InterfaceID", "{9402f54f-4906-4f20-ad73-afcfeb5b228d}")
Q_CLASSINFO ("RegisterObject", "yes")
Q_CLASSINFO ("CoClassAlias", "QtClient")
Q_CLASSINFO ("Description", "Transmission Qt Client Class")
#endif
public:
InteropObject (QObject * parent = nullptr);

33
qt/transmission-qt.idl Normal file
View File

@ -0,0 +1,33 @@
import "ocidl.idl";
[
uuid(1E405FC2-1A3A-468B-8BD6-BFBB58770390),
version(1.0),
helpstring("Transmission Qt Client Type Library 1.0")
]
library TransmissionLib
{
[
uuid(9402F54F-4906-4F20-AD73-AFCFEB5B228D),
helpstring("QtClient Interface")
]
dispinterface IQtClient
{
properties:
methods:
[id(1)] VARIANT_BOOL PresentWindow();
[id(2)] VARIANT_BOOL AddMetainfo([in] BSTR p_metainfo);
};
[
aggregatable,
appobject,
helpstring("Transmission Qt Client Class"),
uuid(0E2C952C-0597-491F-BA26-249D7E6FAB49)
]
coclass QtClient
{
[default] dispinterface IQtClient;
};
};

View File

@ -0,0 +1,6 @@
#include "winresrc.h"
#pragma code_page(1252)
LANGUAGE 0, 0
1 TYPELIB "transmission-qt.tlb"