



![]() ![]() |
software-on-demand-ita.comGiuseppe Pischedda Software Engineer software-on-demand-ita.comGiuseppe Pischedda Software Engineer |
Part 4
Implementazione di un'applicazione C++ Win32 che "consuma" un Runtime Component C++/WinRT.
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.
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:
<?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>
<?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>
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.
Giuseppe Pischedda 2021