windows C++-通过 C++/WinRT 创作 COM 组件(四)

如何测试示例应用程序

生成应用程序,然后至少以管理员身份运行一次,以便使注册(和其他设置)代码运行。 执行此操作的一种方法是以管理员身份运行 Visual Studio,然后从 Visual Studio 运行应用。 在任务栏中右键单击 Visual Studio 以显示跳转列表,在跳转列表右键单击 Visual Studio,然后单击“以管理员身份运行”。 同意提示,然后打开项目。 运行应用程序时,会显示一条消息,指出是否在以管理员身份运行应用程序。 如果不是,则注册和其他设置不会运行。 该注册和其他设置必须至少运行一次,才能使应用程序正常工作。

无论是否在以管理员身份运行应用程序,按“T”使 toast 显示。 然后可以直接从弹出的 toast 通知或是从操作中心单击“回调 ToastAndCallback”按钮,这样会启动应用程序,实例化组件类并执行 INotificationActivationCallback::Activate 方法。

进程内 COM 服务器

上面的 ToastAndCallback 示例应用可充当本地(或进程外)COM 服务器。 这由 LocalServer32 Windows 注册表项进行指示,该项用于注册其组件类的 CLSID。 本地 COM 服务器在可执行二进制文件 (.exe) 中承载其组件类。

或者(并且可能更有可能),可以选择在动态链接库 (.dll) 中承载组件类。 采用 DLL 形式的 COM 服务器称为进程内 COM 服务器,由使用 InprocServer32 Windows 注册表项注册 CLSID 进行指示。

创建动态链接库 (DLL) 项目

可以通过在 Microsoft Visual Studio 中创建新项目,来开始创建进程内 COM 服务器的任务。 创建“Visual C++”>“Windows 桌面”>“动态链接库(DLL)”项目。

若要向新项目添加 C++/WinRT 支持,请执行修改 Windows 桌面应用程序项目以添加 C++/WinRT 支持中所述的步骤。

实现组件类、类工厂和进程内服务器导出

打开 dllmain.cpp,并向其添加如下所示的代码清单。

如果已具有一个实现 C++WinRT Windows 运行时类的 DLL,则表示已具有如下所示的 DllCanUnloadNow 函数。 如果要将组件类添加到该 DLL,则可以添加 DllGetClassObject 函数。

如果没有要保持与之兼容的现有 Windows 运行时 C++ 模板库 (WRL) 代码,则可以从显示的代码中删除 WRL 部分。

// dllmain.cpp

struct MyCoclass : winrt::implements<MyCoclass, IPersist>
{
    HRESULT STDMETHODCALLTYPE GetClassID(CLSID* id) noexcept override
    {
        *id = IID_IPersist; // Doesn't matter what we return, for this example.
        return S_OK;
    }
};

struct __declspec(uuid("85d6672d-0606-4389-a50a-356ce7bded09"))
    MyCoclassFactory : winrt::implements<MyCoclassFactory, IClassFactory>
{
    HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject) noexcept override
    {
        try
        {
            return winrt::make<MyCoclass>()->QueryInterface(riid, ppvObject);
        }
        catch (...)
        {
            return winrt::to_hresult();
        }
    }

    HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock) noexcept override
    {
        // ...
        return S_OK;
    }

    // ...
};

HRESULT __stdcall DllCanUnloadNow()
{
#ifdef _WRL_MODULE_H_
    if (!::Microsoft::WRL::Module<::Microsoft::WRL::InProc>::GetModule().Terminate())
    {
        return S_FALSE;
    }
#endif

    if (winrt::get_module_lock())
    {
        return S_FALSE;
    }

    winrt::clear_factory_cache();
    return S_OK;
}

HRESULT __stdcall DllGetClassObject(GUID const& clsid, GUID const& iid, void** result)
{
    try
    {
        *result = nullptr;

        if (clsid == __uuidof(MyCoclassFactory))
        {
            return winrt::make<MyCoclassFactory>()->QueryInterface(iid, result);
        }

#ifdef _WRL_MODULE_H_
        return ::Microsoft::WRL::Module<::Microsoft::WRL::InProc>::GetModule().GetClassObject(clsid, iid, result);
#else
        return winrt::hresult_class_not_available().to_abi();
#endif
    }
    catch (...)
    {
        return winrt::to_hresult();
    }
}
对弱引用的支持

如果类型实现 IInspectable或任何派生自 IInspectable 的接口,则 C++/WinRT,具体而言,winrt::implements 基结构模板,会实现 IWeakReferenceSource。

这是因为 IWeakReferenceSource 和 IWeakReference 旨在用于 Windows 运行时类型。 因此,只需通过向实现添加 winrt::Windows::Foundation::IInspectable(或派生自 IInspectable 的接口),即可为组件类启用弱引用支持。

实现从另一个接口派生的 COM 接口

接口派生是经典 COM 的一项功能(Windows 运行时中刻意不提供此功能)。 下例展示了接口派生的显示效果。 

IFileSystemBindData2 : public IFileSystemBindData { /* ... */  };

如果要编写需要实现的类,例如 IFileSystemBindData 和 IFileSystemBindData2,则表达的第一步是声明仅实现派生接口,如下所示。 

// pch.h
#pragma once
#include <Shobjidl.h>
...

// main.cpp
...
struct MyFileSystemBindData :
    implements<MyFileSystemBindData,
    IFileSystemBindData2>
{
    // IFileSystemBindData
    IFACEMETHOD(SetFindData)(const WIN32_FIND_DATAW* pfd) override { /* ... */ return S_OK; };
    IFACEMETHOD(GetFindData)(WIN32_FIND_DATAW* pfd) override { /* ... */ return S_OK; };

    // IFileSystemBindData2
    IFACEMETHOD(SetFileID)(LARGE_INTEGER liFileID) override { /* ... */ return S_OK; };
    IFACEMETHOD(GetFileID)(LARGE_INTEGER* pliFileID) override { /* ... */ return S_OK; };
    IFACEMETHOD(SetJunctionCLSID)(REFCLSID clsid) override { /* ... */ return S_OK; };
    IFACEMETHOD(GetJunctionCLSID)(CLSID* pclsid) override { /* ... */ return S_OK; };
};
...
int main()
...

下一步是在针对 MyFileSystemBindData 示例对 IID_IFileSystemBindData(基接口)直接或间接调用 QueryInterface 时,确保 QueryInterface 成功 。 为此,需要为 winrt::is_guid_of 函数模板提供专用化。

winrt::is_guid_of 是可变参数,因此可以为其提供接口列表。 下面介绍如何提供专用化,以使对 IFileSystemBindData2 的检查还包括对 IFileSystemBindData 的测试 。

// pch.h
...
namespace winrt
{
    template<>
    inline bool is_guid_of<IFileSystemBindData2>(guid const& id) noexcept
    {
        return is_guid_of<IFileSystemBindData2, IFileSystemBindData>(id);
    }
}

// main.cpp
...
int main()
{
    ...
    auto mfsbd{ winrt::make<MyFileSystemBindData>() };
    auto a{ mfsbd.as<IFileSystemBindData2>() }; // Would succeed even without the **is_guid_of** specialization.
    auto b{ mfsbd.as<IFileSystemBindData>() }; // Needs the **is_guid_of** specialization in order to succeed.
}

winrt::is_guid_of 的专用化在项目的所有文件中必须相同,并且在 winrt::implements 或 winrt::delegate 模板使用接口时可见 。 通常会将该内容放在一个通用头文件中。 

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值