Revision control

++ // Copyright (C) 2024 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #include <QString>
++ #include <QUuid>
++ #include <QtCore/qassert.h>
++ #include <QtCore/qdebug.h>
++ #include "testserverlib.h"
++ #include "testserver.h"
++
++ extern "C" IMAGE_DOS_HEADER __ImageBase; // Trickery to get path to current dll
++ static long s_serverLock = 0;
++
++ static const std::array<wchar_t, MAX_PATH> GetModuleFilename()
++ {
++ std::array<wchar_t, MAX_PATH> filename;
++ const DWORD length = GetModuleFileName(reinterpret_cast<HMODULE>(&__ImageBase), filename.data(),
++ static_cast<DWORD>(filename.size()));
++
++ if (0 == length || filename.size() == length)
++ return {};
++
++ return filename;
++ }
++
++ // Create a dispatcher that implements IDispatch for the input owner
++ static ComPtr<IUnknown> CreateDispatcher(const QUuid &guid, IUnknown *owner)
++ {
++ ComPtr<ITypeLib> typeLib;
++ if (LoadTypeLib(GetModuleFilename().data(), &typeLib) != S_OK)
++ {
++ qCritical("Failed to load type library");
++ return {};
++ }
++
++ ComPtr<ITypeInfo> typeInfo;
++ if (typeLib->GetTypeInfoOfGuid(guid, &typeInfo)
++ != S_OK) {
++ qCritical("Failed to get type info");
++ return {};
++ }
++
++ ComPtr<IUnknown> dispatcher;
++ if (CreateStdDispatch(owner, owner, typeInfo.Get(), &dispatcher) != S_OK) {
++ qCritical("Failed to create standard dispatch");
++ return {};
++ }
++
++ return dispatcher;
++ }
++
++ TestServer::TestServer()
++ {
++ m_unkDisp = CreateDispatcher(__uuidof(IComServer), this);
++ ++s_serverLock;
++ }
++
++ HRESULT __stdcall TestServer::QueryInterface(IID const &id, void **result)
++ {
++ Q_ASSERT(result);
++ if (id == __uuidof(IDispatch)) {
++ ComPtr<IDispatch> disp;
++ m_unkDisp.As(&disp);
++ *result = disp.Detach();
++ return S_OK;
++ }
++ return QComObject::QueryInterface(id, result);
++ }
++
++ HRESULT TestServer::SetObserver(IUnknown* observer)
++ {
++ const ComPtr<IUnknown> rcv{ observer };
++ return rcv.As(&m_receiver);
++ }
++
++ HRESULT TestServer::VariantIn(VARIANT v)
++ {
++ return m_receiver->VariantIn(v);
++ }
++
++ HRESULT TestServer::GetTestEnumValue(TestEnum *value)
++ {
++ *value = TestEnumSecond;
++ return S_OK;
++ }
++
++ HRESULT TestServer::EnumIn(TestEnum value)
++ {
++ return m_receiver->EnumIn(value);
++ }
++
++ struct Factory : IClassFactory
++ {
++ ULONG __stdcall AddRef() override { return 2; }
++ ULONG __stdcall Release() override { return 1; }
++ HRESULT __stdcall QueryInterface(IID const &id, void **result) override
++ {
++ Q_ASSERT(result);
++
++ if (id == __uuidof(IClassFactory) || id == __uuidof(IUnknown)) {
++ *result = static_cast<IClassFactory *>(this);
++ return S_OK;
++ }
++
++ *result = nullptr;
++ return E_NOINTERFACE;
++ }
++
++ HRESULT __stdcall CreateInstance(IUnknown *outer, IID const &iid, void **result) override
++ {
++ Q_ASSERT(result);
++ Q_ASSERT(!outer);
++
++ const ComPtr<TestServer> server{ new (std::nothrow) TestServer };
++ return server->QueryInterface(iid, result);
++ }
++
++ HRESULT __stdcall LockServer(BOOL lock) override
++ {
++ if (lock)
++ ++s_serverLock;
++ else
++ --s_serverLock;
++
++ return S_OK;
++ }
++ };
++
++ HRESULT __stdcall DllGetClassObject(CLSID const &clsid, IID const &iid, void **result)
++ {
++ if (__uuidof(TestServer) == clsid) {
++ static Factory farm;
++
++ return farm.QueryInterface(iid, result);
++ }
++
++ return CLASS_E_CLASSNOTAVAILABLE;
++ }
++
++ HRESULT __stdcall DllCanUnloadNow()
++ {
++ return s_serverLock ? S_FALSE : S_OK;
++ }