65.9K
CodeProject 正在变化。 阅读更多。
Home

C++/WinRT 运行时组件在 Win32 应用程序中的应用 - 第 4 部分

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2022 年 1 月 14 日

CPOL

3分钟阅读

viewsIcon

6725

downloadIcon

245

实现一个“使用”C++/WinRT 运行时组件的 Win32 C++ 应用程序。

(Win32 应用程序中运行的 WinRT 售票机)

我们打开 Visual Studio 中之前创建的 C++ Win32 项目(参见 第 3 部分)。我们的示例应用程序将模拟一个排队取号系统的操作,门票的发出不会因为在 Totem 上选择真实用户而发生,而是由 Win32 客户端应用程序管理,只需点击功能区控件中的新建按钮即可。

将 C++/WinRT 组件导入 Win32 桌面应用程序

我们在 第 1 部分 中看到,C++/WinRT 基于 C++ 标准 17,因此与每个 C++ 17 应用程序完全兼容。你可能已经想到,只需在 C++ Win32 项目中添加一个或多个 *include* 即可。

好吧,这确实是可行的,但这不是我们想要的。我们想要的是让我们的 Win32 C++ 应用程序使用 C++/WinRT 组件,而不是将其合并到其中;也就是说,我们的应用程序必须在运行时加载组件并调用其类、属性和事件。

为此,你需要以某种方式将 WinRT 组件类型导入到你的 Win32 项目中。

将 C++/WinRT 类型导入 C++/Win32

我们在 第 3 部分 中看到,Visual Studio 并没有(至少目前)直接将 .winmd 文件导入到 Win32 C++ 项目中。但是,有一个解决方法:如果我们定义 Win32 项目对 WinRT 组件的依赖关系,那么 cppwinrt.exe 工具会为我们生成所有需要的代码,以便我们可以在 Win32 应用程序中定义 WinRT 组件的类型。但是,cppwinrt.exe 工具是 C++/Winrt 编译工具链的一部分,因此默认情况下在 C++/Win32 项目中不存在,因此,为了获得该工具,我们将不得不通过 NuGet 将两个组件添加到 Win32 项目中

  • Microsoft.Windows.CppWinRT
  • VC WinRT Forwarders

首先,我们必须定义对 WinRT 组件的依赖关系:在 C++ 项目 Win32 中,我们执行 添加 -> 新建项... 并添加一个属性表文件。

我们以如下方式修改该文件的内容:在 解决方案资源管理器 中,我们转到 属性管理器,选择 Win32 项目并右键单击,然后单击菜单项 添加现有属性表...

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ImportGroup Label="PropertySheets" />
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup />
  <ItemDefinitionGroup />
  <ItemGroup />
  <ItemGroup>
    <Reference Include="$(SolutionDir)\$(Configuration)\TicketMachine\TicketMachine.winmd">
      <IsWinMDFile>true</IsWinMDFile>
    </Reference>
    <ReferenceCopyLocalPaths Include=
     "$(SolutionDir)\$(Configuration)\TicketMachine\TicketMachine.dll">
      <IsWinMDFile>false</IsWinMDFile>
    </ReferenceCopyLocalPaths>
  </ItemGroup>
</Project>

我们选择我们创建的文件。

让我们向 Win32 项目添加一个清单文件:并按如下方式修改它

    <?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>

  <file name="TicketMachine.dll">
    <activatableClass
        name="TicketMachine.Service"
        threadingModel="both"
        xmlns="urn:schemas-microsoft-com:winrt.v1" />
        <activatableClass
        name="TicketMachine.Desk"
        threadingModel="both"
        xmlns="urn:schemas-microsoft-com:winrt.v1" />
    <activatableClass
        name="TicketMachine.Operator"
        threadingModel="both"
        xmlns="urn:schemas-microsoft-com:winrt.v1" />
    <activatableClass
        name="TicketMachine.Ticket"
        threadingModel="both"
        xmlns="urn:schemas-microsoft-com:winrt.v1" />
    <activatableClass
        name="TicketMachine.OperationsQueue"
        threadingModel="both"
        xmlns="urn:schemas-microsoft-com:winrt.v1" />
    <activatableClass
        name="TicketMachine.OperationsLog"
        threadingModel="both"
        xmlns="urn:schemas-microsoft-com:winrt.v1" />
  </file>

</assembly>

正如我们在清单文件中看到的那样,我们必须设置组件文件(.dll)的名称、我们希望在运行时加载的类的名称(在组件的 .idl 文件中定义)以及线程模型。

打开 NuGet 并添加这两个 WinRT 组件

保存 Visual Studio 解决方案,从 项目 菜单中,转到 项目生成顺序...,确保 Win32 应用程序在 WinRT 组件之后编译,然后重新编译该解决方案。

因此,我们将看到 Win32 项目现在依赖于 WinRT 组件,此外,cppwinrt tool.exe 已经为我们生成了允许我们实现 TicketMachine 组件的类型的代码。 我们在 Win32 项目的 GeneratedFiles 文件夹中找到了 cppwinrt tool.exe 生成的文件。

现在,通过在我们的项目中添加 include 文件 TicketMachine.h,你就可以使用该组件了。

#pragma once
#include "CWindow.h"
#include "MessageHandlerT.h"
............
............
#include "winrt/TicketMachine.h"

我们在框架的 CFrameWindow 类中定义了以下成员(参见第 3 部分)

    class CFrameWindow :
        public CWindow
    {

....................
....................
....................

private:
static winrt::TicketMachine::Ticket m_ticket;
static winrt::TicketMachine::OperationsQueue m_operationsQueue;
winrt::TicketMachine::OperationsLog m_operationsLog;
       
//C++/WinRT Component event handlers
winrt::event_token m_TicketAddedEventTokenArgs;
winrt::event_token m_OperationCompleteEventTokenArgs;

//Items in Queue collection
static std::vector <winrt::ticketmachine::operationsqueue> m_opQueue;

//Items Log collection
std::vector<winrt::ticketmachine::operationslog> m_opLog;

static void InitializeServices();
static void InitializeDesks();
static void InitializeOperators();

static bool AddOperationsQueue(static winrt::TicketMachine::OperationsQueue const& q, 
                               static winrt::TicketMachine::Ticket const& t);

public:
std::vector<winrt::ticketmachine::operationsqueue> GetOperationsQueue();
std::vector<winrt::ticketmachine::operationslog> GetOperationsLog();

static void CreateNewTicket();

    ....................
    ....................
    ....................

    };

在构造函数中,我们实现了事件的处理程序

....................
....................
....................
....................

///OnStartQueue Event handler
m_TicketAddedEventTokenArgs = m_operationsQueue.OnQueueItemAdded([]
(const auto&, winrt::TicketMachine::StartQueueEventArgs args)
{
	bool isAdded = args.ItemAdded();

	if (isAdded)
	{		
		struct _tagOperationQueue
		{
			LPWSTR ticketNumber;
			LPWSTR serviceCode;
			LPWSTR serviceName;

			LPWSTR deskNumber;
			LPWSTR deskCode;
			LPWSTR deskName;
			
			LPWSTR ticketCreationDate;
			LPWSTR ticketBarcode;
			int32_t ticketMaxNumber;			
		};
		_tagOperationQueue OQ;
		OQ.serviceCode = (LPWSTR)m_operationsQueue.ServiceCode().c_str();
		OQ.serviceName = (LPWSTR)m_operationsQueue.ServiceName().c_str();

		OQ.deskNumber = (LPWSTR)m_operationsQueue.DeskNumber().c_str();
		OQ.deskCode = (LPWSTR)m_operationsQueue.DeskCode().c_str();
		OQ.deskName = (LPWSTR)m_operationsQueue.DeskName().c_str();


		OQ.ticketNumber = (LPWSTR)m_operationsQueue.TicketNumber().c_str();
		OQ.ticketBarcode = (LPWSTR)m_operationsQueue.TicketBarcode().c_str();
		OQ.ticketMaxNumber = m_operationsQueue.MaxQueueItems();
		OQ.ticketCreationDate = (LPWSTR)(m_operationsQueue.CreationDate().DateToString() + L" " +
			                             m_operationsQueue.CreationDate().TimeToString()).c_str();
		
		SendMessage((HWND)m_clientWnd->GetChildHandle(), WM_NEW_TICKET, 
                    (WPARAM)(pCFrameWnd->hwndFrame), reinterpret_cast<LPARAM>(&OQ));
    }
    });

    ///OnOperationCompleted Event handlers
    m_OperationCompleteEventTokenArgs = m_operationsQueue.OnOperationCompleted([]
    (const auto&, winrt::TicketMachine::CloseOperationEventArgs args)
    {
    bool isCompleted = args.OperationClosed();

    if (isCompleted)
    {
    SendMessage((HWND)m_clientWnd->GetChildHandle(), WM_OPERATION_CLOSED, 
               (WPARAM)(pCFrameWnd->hwndFrame), (LPARAM)(NULL));

    }

    });

    ....................
    ....................

    InitializeServices();

    InitializeDesks();

    InitializeOperators();
}

成员函数的实现

....................
....................
....................
....................

void Win32Framework::CFrameWindow::InitializeServices()
	{
		struct _tagServicesType
		{
			LPWSTR serviceCode;
			LPWSTR serviceName;
		};

		_tagServicesType SRV{ 0 };

		winrt::TicketMachine::Service srv;
		Collection<winrt::ticketmachine::service> _srv;

		auto _services = _srv.GetItems(srv, false);

		size_t index = 0;

		do {
			
			SRV.serviceCode = (LPWSTR)_services[index].ServiceCode().c_str();
			SRV.serviceName = (LPWSTR)_services[index].ServiceName().c_str();
			++index;

			SendMessage((HWND)m_clientWnd->GetChildHandle(), WM_INITIALIZE_SERVICES, 
                        (WPARAM)(pCFrameWnd->hwndFrame), reinterpret_cast<lparam>(&SRV));

		} while (index <= (_services.size() - 1));
	}

	void Win32Framework::CFrameWindow::InitializeDesks()
	{
		struct _tagDeskType
		{
			LPWSTR deskNumber;
			LPWSTR deskCode;			
			LPWSTR deskName;
		};

		_tagDeskType DSK{ 0 };

		winrt::TicketMachine::Desk dsk;
		Collection<;winrt::TicketMachine::Desk> _dsk;

		auto _desks = _dsk.GetItems(dsk, false);

		size_t index = 0;

		do {
			
			DSK.deskNumber = (LPWSTR)_desks[index].DeskNumber().c_str();
			DSK.deskCode = (LPWSTR)_desks[index].DeskCode().c_str();
			DSK.deskName = (LPWSTR)_desks[index].DeskName().c_str();
			++index;

			SendMessage((HWND)m_clientWnd->GetChildHandle(), WM_INITIALIZE_DESKS, 
                       (WPARAM)(pCFrameWnd->hwndFrame), reinterpret_cast<lparam>(&DSK));

		} while (index <= (_desks.size() - 1));
	}

	void Win32Framework::CFrameWindow::InitializeOperators()
	{
		struct _tagOperatorType
		{			
			LPWSTR firstName;
			LPWSTR lastName;
			LPWSTR badgeCode;
		};

		_tagOperatorType OPR{ 0 };

		winrt::TicketMachine::Operator opr;
		Collection<twinrt::TicketMachine::Operator> _opr;

		auto _operators = _opr.GetItems(opr, false);

		size_t index = 0;

		do {
				
			OPR.firstName = (LPWSTR)_operators[index].FirstName().c_str();
			OPR.lastName = (LPWSTR)_operators[index].LastName().c_str();
			OPR.badgeCode = (LPWSTR)_operators[index].BadgeCode().c_str();
			
			++index;

			SendMessage((HWND)m_clientWnd->GetChildHandle(), WM_INITIALIZE_OPERATORS, 
                       (WPARAM)(pCFrameWnd->hwndFrame), reinterpret_cast<LPARAM>(&OPR));

		} while (index <= (_operators.size() - 1));
	}

void Win32Framework::CFrameWindow::CreateNewTicket()
	{	
		m_ticket.Create();

		bool insElement = AddOperationsQueue(m_operationsQueue,m_ticket);

		if (insElement)
		{
			//Raise the insert new ticket in queue event
			m_operationsQueue.OperationItemAdded(true);
		}
}
	
....................
....................
....................
....................

其余的工作更多地是致力于创建演示应用程序的 GUI,无论如何我不会在这里处理它,以免赘述,因为它超出了上下文。当工作完成时,我们将得到页面顶部显示的结果。

历史

  • 2022 年 1 月 14 日:初始版本
© . All rights reserved.