C++/WinRT Runtime Component in Win32 Applications

Part 4



Implementazione di un'applicazione C++ Win32 che "consuma" un Runtime Component C++/WinRT.

Application sample Application sample Application sample Application sample




Apriamo in Visual Studio il progetto C++ Win32 creato in precedenza (vedi parte 3).
La nostra applicazione di esempio simulerà il funzionamento di un sistema tagliacode, l'emissione dei tickets non avverrà a seguito della scelta un un utente reale al Totem, ma sarà gestita dall'applicazione client Win32, sarà sufficiente fare click sul button New nel controllo ribbon.

Ticket flowchart



Importare un componente C++/WinRT in un'applicazione Win32 Desktop

Abbiamo visto, nella parte 1, che C++/WinRT è basato sul C++ standard 17, quindi pienamente compatibile con ogni applicazione C++ 17.
Probabilmente avete già in mente che sarà sufficiente aggiungere uno o più includes nel progetto C++ Win32 e fine.
Ebbene effettivamente questo è possibile, ma non è ciò che vogliamo.

Ciò che vogliamo è che la nostra applicazione C++ Win32 consumi il componente C++/WinRT e non che lo inglobi in essa; ovvero la nostra app dovrà, a runtime, caricare il componente e invocarne classi, proprietà ed eventi.
Allo scopo sarà necessario importare, in qualche modo, i tipi del componente WinRT nel progetto Win32.


Importazione dei tipi C++/WinRT in un progetto C++/Win32

Abbiamo visto, nella parte 3, che Visual Studio non consente (almeno attualmente) l'importazione diretta di un file .winmd in un progetto C++ Win32.
Tuttavia c'è un workaround: se definiamo la dipendenza del progetto Win32, dal componente WinRT, lo strumento cppwinrt.exe, produce per noi tutto il codice necessario affinchè noi possiamo definire i tipi del componente WinRT nell'app Win32.

Tuttavia lo strumento cppwinrt.exe fa parte della toolchain di compilazione di C++/Winrt, quindi sicuramente non presente di default in un progetto C++/Win32, perciò, per avere lo strumento disponibile, dovremo aggiungere, via NuGet, al progetto Win32, due componenti:



Prima di tutto dobbiamo definire la dipendenza dal componente WinRT:

Nel progetto C++ Win32
Facciamo Add -> New Item.. ed aggiungiamo un file Property Sheet

Property Sheet



Modifichiamo il contenuto del file nel modo seguente:

<?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>


In Solution Explorer andiamo in Property Manager, selezioniamo il progetto Win32 con click destro e facciamo click sulla voce di menu Add Existing Property Sheet...
Property Sheet


Selezioniamo il file che abbiamo creato.
Property Sheet



Aggiungiamo un file manifest al progetto Win32:

e modifichiamolo nel modo seguente:


<?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>





Apriamo NuGet ed aggiungiamo i due componenti WinRT:
CppWinRT


Salviamo la soluzione Visual Studio, dal menu Project andiamo in Project Build Order..., assicuriamoci che l'applicazione Win32 venga compilata dopo il componente WinRT e ricompiliamo la soluzione.

Come risultato avremo che il progetto Win32 dipende ora dal componente WinRT, inoltre lo strumento cppwinrt.exe ha prodotto per noi il codice che ci consente di implementare i tipi del componente TicketMachine.

I file prodotti dallo strumento cppwinrt.exe li troviamo nella cartella GeneratedFiles del progetto Win32.


Ora è possibile, aggiungendo il file di inclusione TicketMachine.h nel nostro progetto, consumare il componente.

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



Definiamo nella classe CFrameWindow del framework, (vedi parte 3), i seguenti membri:



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();

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

    };


Nel costruttore implementiamo gli handlers per gli eventi:

Win32Framework::CFrameWindow::CFrameWindow(HINSTANCE hInst)
{

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

///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();


}


Implementazione delle funzioni membro:



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

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<winrt::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);

		}
}
	
....................
....................
....................
....................



Il resto del lavoro è più che altro dedicato alla creazione della GUI dell'applicazione demo, che comunque non tratterò qui per non dilungarmi e perchè esula dal contesto.
A lavoro finito avremo il risultato mostrato ad inizio pagina.


download C++/WinRT Component and C++/Win32 Application ver. 1.0.0  zip 

zip package hash codes

HASH codes:

  • MD5 : ADD14F3FA8CDA27A30F72D490F4592C3
  • SHA1 : 79C6B7CB28ADF41D5A9B3EC5C3EF43D88EAA8CC8
  • SHA256 : 2DB1817032DC6DE9CA28D67D0B7A545E69A256C93A9C5DEE310F030F5C28699D







Giuseppe Pischedda 2021


Se il post ti è utile puoi fare una donazione all'autore, l'importo è a tua libera scelta.

Grazie