In this article, you will learn to create a Win32 application in C++ that consumes a C++/WinRT component runtime.
Introduction
Note: In the same Visual Studio solution, we will create a C++/WinRT Runtime Component project in which we will implement runtime classes and a Win32 C++ Classic app project that will use them. For the Win32 classic app, I will use a Project Template for Visual Studio 2019 based on the homonymous C++ framework for Win32: Win32Framework, in its version with Ribbon that often, myself and my team, we use as a starting point for Win32 C++ apps as it simplifies and speeds up the work. (the Project Template and the Win32Framework are both my creation and distributed exclusively within my C++ team, it is not excluded, however, that in the future I may release a version of the Win32Framework on this website or on GitHub).
Of course, Win32Framework is not an obligatory requirement and you are obviously free to use the type of C++ project that best suits you, as long as it is based on the C++ standard 17.
The first step will then be to create a new C++ project in Visual Studio 2019.
Note: In this tutorial, you used Visual Studio 2019 vers. 16.8.x.
We choose a meaningful name for the new project, I chose UWPWin32Demo
, and created its folder with the same name.
Once the project has been created, we are welcomed by the welcome and license page of the framework, the Readme file and the tabs with the CFrameWindow.h and CFrameWindow files already open.cpp.
Note: The project based on the Win32Framework
Ribbon framework is already ready to be compiled with the standard C++ 17 language. If your project is not, go to Properties -> General -> C++ Language Standard and be sure to choose version 17 [ISO C++17 Standard (/std:c++17)].
We build the application to verify that everything is OK, or we run a Debug session of the Win32 app (see images above).
If your C++ Win32 starter app, compiled with the standard C++ version 17 is also OK, let's move on to the second step: the custom Runtime Component that will be consumed by our application.
Note: In order for a Runtime Component to be "consumed" by other applications, the class must be a runtime class. If we wanted to use the runtime component in a local application, that is, exclusively in the same project, an ordinary C++ class would suffice.
We add a new project and choose the template: Windows Runtime Component C++/WinRT. The WinRT component in this example will handle a queue delete system for which we give a meaningful name to the UWP project: TicketMachine
.
We choose the minimum and maximum target of the operating system and confirm.
As already mentioned (see part 1), the Visual Studio wizard, in addition to everything you need to compile and instantiate the component correctly at runtime, has created for us a runtime class named Class
and the IDL Class.idl file: More precisely, we find three files:
- Class.idl
- Class.h
- Class.cpp
The .idl file defines the C++/WinRT (Runtime Classes) classes. By opening the .idl (Interface Definition Language) file, which will be fed to the MICROSOFT Interface Definition Language (MIDL) tool during the compilation of the project, you can see the new IDL language syntax in its version 3.0.
For those who do not know COM (Component Object Model), remember that .idl files are used to describe COM types and interfaces. Those who know COM and therefore "traditional" IDL will find the syntax somewhat different because, in the updated version, the language has been simplified and modified to support the types of Windows Runtime.
In the solution in Visual Studio, we select the UWP project TicketMachine
; first we rename the file Class.idl. We choose a meaningful name.
Note: C++/WinRT creates a corresponding .winmd file for each .idl file, so to avoid having multiple .winmd files, I will declare all interfaces and classes in a single .idl file. This technique is perfectly legal and allows, in complex projects, to greatly shorten build times.
In our example, interfaces and classes will be collected (see note above) in a single .idl file; I will call the latter Common.idl. Once the .idl file is renamed, the two files, header and implementation, (Class.h and Class.cpp respectively) are automatically renamed.
Once the three files have been renamed, we must correct the includes and the name of the class that by default refer to Class.
We continue by compiling the TicketMachine
project for the first time, thus creating the first version (although currently useless) of the TicketMachine.winmd and TicketMachine files.dll.
We will see later that at the appropriate time, the DLL will be loaded at runtime (or rather, the operating system will do it for us) and, through it, we will have access to the metadata contained in the .winmd file by our Win32 application; in fact, since it is not possible (at least currently) to import directly into a native Win32 application, the types contained in a .winmd file, this is (at the moment) the way to access metadata. If you thought you could get away with it by simply importing a .winmd file into a native Win32 project by adding a reference in Visual Studio, well, you would change your mind if you tried as Visual Studio would block your way with the warning below:
A Brief Analysis for the Sample C++/WinRT Component
Our sample project will allow us to see encapsulation, inheritance and polymorphism at work from the C++/WinRT point of view; we will design a WinRT component that manages queues at the counters of a hypothetical office and a Win32 Classic C++ client application that will "consume" the same.
Also in the component, we will also implement some events that can then be managed, as well as in the Win32 client, in applications (whether they are UWP, .NET, etc.) possibly implemented in the future.
First, we identify the entities involved: → Services → Branches → Operators → Totems for tickets → Tickects via smartphone app → Queue operations waiting → Log completed operations.
Some initial specifications:
- → An operator, for each working day, can be assigned to any counter.
- → Each operator at his counter can perform all services.
- → For each ticket in the queue, you must indicate the estimated waiting time.
- → A waiting start event must be raised when the ticket is issued.
- → A close operation event must be raised when the operation is completed.
From the above, it can be deduced that the following interfaces and classes should be implemented:
- Interfaces:
IServices
Property IDesks
IOperators
ITickets
- Classes:
Service
Desk
Operator
Ticket
OperationsQueue
OperationsLog
We represent with a UML graph the relationships we intend to develop.
We define interfaces and classes in the Common.idl file:
namespace TicketMachine
{
interface IDate
{
Int32 Year;
Int32 Month;
Int32 Day;
};
interface ITime
{
Int32 Hh;
Int32 Mm;
Int32 Ss;
};
unsealed runtimeclass Date: IDate,ITime
{
Date();
String DateToString{get; };
String TimeToString{ get; };
};
interface IServices
{
String ServiceCode;
String ServiceName;
};
runtimeclass Service : IServices, ICommon
{
Service();
Windows.Foundation.IAsyncOperation<IVector<Service> > GetCollection{ get; };
}
interface IImageFiles
{
Windows.Storage.StorageFile ImageFile;
};
interface ITickets
{
String TicketNumber;
String TicketBarcode;
Int32 MaxQueueItems;
Date CreationDate;
Windows.Storage.StorageFile TicketQRCode;
};
unsealed runtimeclass Ticket: ITickets, IImageFiles, ICommon,IServices,IDesks,IOperators
{
Ticket();
void Create();
String TicketToString{ get; };
}
interface IDesks
{
String DeskNumber;
String DeskCode;
String DeskName;
};
runtimeclass Desk: IDesks, ICommon
{
Desk();
Windows.Foundation.IAsyncOperation<IVector<Desk> > GetCollection{ get; };
}
interface IOperators
{
String FirstName;
String LastName;
Date BirthDate;
};
runtimeclass Operator : IOperators, ICommon
{
Operator();
String BadgeCode;
Windows.Foundation.IAsyncOperation<IVector<Operator> > GetCollection{ get; };
}
[default_interface]
unsealed runtimeclass OperationsQueue : IServices, IDesks, ITickets,ICommon
{
OperationsQueue();
Windows.Foundation.IAsyncOperation<IVector<OperationsQueue> > GetCollection{ get; };
void OperationItemAdded(Boolean isAdded);
void OperationCompleted(Boolean isCompleted);
event Windows.Foundation.EventHandler
<TicketMachine.StartQueueEventArgs> OnQueueItemAdded;
event Windows.Foundation.EventHandler
<TicketMachine.CloseOperationEventArgs> OnOperationCompleted;
};
[default_interface]
unsealed runtimeclass OperationsLog : IServices, ITickets, ICommon
{
OperationsLog();
Windows.Foundation.IAsyncOperation<IVector<OperationsLog> > GetCollection{ get; };
};
interface ICommon
{
void Add();
void Edit();
void Delete();
void Save();
};
[default_interface]
unsealed runtimeclass Common: ICommon
{
Common();
}
runtimeclass StartQueueEventArgs
{
Boolean ItemAdded{get; };
};
runtimeclass CloseOperationEventArgs
{
Boolean OperationClosed{get; };
};
}
We build the TicketMachine
project to allow Visual Studio to create all stubs files (h and cpp).
Now we open the folder of the Project TicketMachine
and create a new folder with the name of your choice, I will call it Generated. Within this, we copy all the files (h and cpp) that Visual Studio has created in the subfolder of the project .. \TicketMachine\Generated Files\sources except Common.h and Common.cpp.
So in Solution Explorer, in the Generated Files filter, we right-click -> Add -> Existing Item... and add all the files of the newly created folder (Generated in my case).
Let's comment in all the files of the Generated folder (h and cpp) the line:
static_assert(false, "Do not compile generated C++/WinRT source files directly");
Compile the TicketMachine
project and make sure TicketMachine.winmd and TicketMachine.dll were produced by Visual Studio.
Soon, we will see how to consume our WinRT component in a native Win32 application, but first let's take care of implementing, in the respective classes, properties, methods and events.
We will implement the Date
, Service
, Operator
, Desk
, and Ticket
classes first. The Date
class aims to implement two new data types (Date
and Time
); Service
, Operator
and Ticket
will provide a static
list, respectively, of services, operators and branches; obviously in a real application, this data would come from a database or a webservice. The Ticket
class will be used to (precisely) create the tickets of the reservations at the counters.
Date.h
#pragma once
#include "Date.g.h"
namespace winrt::TicketMachine::implementation
{
struct Date : DateT<Date>
{
Date() = default;
hstring DateToString();
hstring TimeToString();
int32_t Year();
void Year(int32_t value);
int32_t Month();
void Month(int32_t value);
int32_t Day();
void Day(int32_t value);
int32_t Hh();
void Hh(int32_t value);
int32_t Mm();
void Mm(int32_t value);
int32_t Ss();
void Ss(int32_t value);
private:
int32_t m_year{ 0 };
int32_t m_month{ 0 };
int32_t m_day{ 0 };
int32_t m_hh{ 0 };
int32_t m_mm{ 0 };
int32_t m_ss{ 0 };
hstring Format(int32_t t);
};
}
namespace winrt::TicketMachine::factory_implementation
{
struct Date : DateT<Date, implementation::Date>
{
};
}
#include "pch.h"
#include "Date.h"
#include "Date.g.cpp"
namespace winrt::TicketMachine::implementation
{
hstring Date::DateToString()
{
hstring tmpDate = winrt::to_hstring(m_month) + L"/" +
to_hstring(m_day)+L"/"+to_hstring(m_year);
return tmpDate;
}
hstring Date::TimeToString()
{
hstring tmpTime = L"";
tmpTime = Format(m_hh) + L":" + Format(m_mm) + L":" + Format(m_ss);
return tmpTime;
}
hstring implementation::Date::Format(int32_t t)
{
hstring tmpTime = to_hstring(t);
switch (tmpTime.size())
{
case 1:tmpTime = L"0" + tmpTime; break;
}
return tmpTime;
}
int32_t Date::Year()
{
return m_year;
}
void Date::Year(int32_t value)
{
m_year = value;
}
int32_t Date::Month()
{
return m_month;
}
void Date::Month(int32_t value)
{
m_month = value;
}
int32_t Date::Day()
{
return m_day;
}
void Date::Day(int32_t value)
{
m_day = value;
}
int32_t Date::Hh()
{
return m_hh;
}
void Date::Hh(int32_t value)
{
m_hh = value;
}
int32_t Date::Mm()
{
return m_mm;
}
void Date::Mm(int32_t value)
{
m_mm = value;
}
int32_t Date::Ss()
{
return m_ss;
}
void Date::Ss(int32_t value)
{
m_ss = value;
}
}
Date.cpp
#include "pch.h"
#include "Date.h"
#include "Date.g.cpp"
namespace winrt::TicketMachine::implementation
{
hstring Date::DateToString()
{
hstring tmpDate = winrt::to_hstring(m_month) + L"/" +
to_hstring(m_day)+L"/"+to_hstring(m_year);
return tmpDate;
}
hstring Date::TimeToString()
{
hstring tmpTime = L"";
tmpTime = Format(m_hh) + L":" + Format(m_mm) + L":" + Format(m_ss);
return tmpTime;
}
hstring implementation::Date::Format(int32_t t)
{
hstring tmpTime = to_hstring(t);
switch (tmpTime.size())
{
case 1:tmpTime = L"0" + tmpTime; break;
}
return tmpTime;
}
int32_t Date::Year()
{
return m_year;
}
void Date::Year(int32_t value)
{
m_year = value;
}
int32_t Date::Month()
{
return m_month;
}
void Date::Month(int32_t value)
{
m_month = value;
}
int32_t Date::Day()
{
return m_day;
}
void Date::Day(int32_t value)
{
m_day = value;
}
int32_t Date::Hh()
{
return m_hh;
}
void Date::Hh(int32_t value)
{
m_hh = value;
}
int32_t Date::Mm()
{
return m_mm;
}
void Date::Mm(int32_t value)
{
m_mm = value;
}
int32_t Date::Ss()
{
return m_ss;
}
void Date::Ss(int32_t value)
{
m_ss = value;
}
}
Service.h
#pragma once
#include "Service.g.h"
namespace winrt::TicketMachine::implementation
{
struct Service : ServiceT<Service>
{
Service() = default;
Windows::Foundation::IAsyncOperation
<Windows::Foundation::Collections::IVector<TicketMachine::Service>> GetCollection();
hstring ServiceCode();
void ServiceCode(hstring const& value);
hstring ServiceName();
void ServiceName(hstring const& value);
void Add();
void Edit();
void Delete();
void Save();
private:
hstring m_serviceCode{ L"" };
hstring m_serviceName{ L"" };
Windows::Foundation::Collections::IVector<TicketMachine::Service> m_services;
};
}
namespace winrt::TicketMachine::factory_implementation
{
struct Service : ServiceT<Service, implementation::Service>
{
};
}
Service.cpp
#include "pch.h"
#include "Service.h"
#include "Service.g.cpp"
#include <winrt/Windows.Foundation.Collections.h>
namespace winrt::TicketMachine::implementation
{
Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVector
<TicketMachine::Service>> Service::GetCollection()
{
winrt::TicketMachine::Service srv[4];
m_services = winrt::single_threaded_vector<winrt::TicketMachine::Service>();
int i = 0;
while (i <= 4)
{
srv[i] = winrt::make<winrt::TicketMachine::implementation::Service>();
switch (i)
{
case 0: srv[i].ServiceCode(L"01");
srv[i].ServiceName(L"Currency Exchanges");
break;
case 1: srv[i].ServiceCode(L"02");
srv[i].ServiceName(L"Front Desk Customer Service");
break;
case 2: srv[i].ServiceCode(L"03");
srv[i].ServiceName(L"Computer&Smartphone Assistance");
break;
case 3: srv[i].ServiceCode(L"04");
srv[i].ServiceName(L"Lost&Found");
break;
case 4: srv[i].ServiceCode(L"05");
srv[i].ServiceName(L"Parking");
break;
}
m_services.Append(srv[i]);
++i;
}
co_return m_services;
}
hstring Service::ServiceCode()
{
return m_serviceCode;
}
void Service::ServiceCode(hstring const& value)
{
m_serviceCode = value;
}
hstring Service::ServiceName()
{
return m_serviceName;
}
void Service::ServiceName(hstring const& value)
{
m_serviceName = value;
}
void Service::Add()
{
}
void Service::Edit()
{
}
void Service::Delete()
{
}
void Service::Save()
{
}
}
Operator.h
#pragma once
#include "Operator.g.h"
namespace winrt::TicketMachine::implementation
{
struct Operator : OperatorT<Operator>
{
Operator() = default;
hstring BadgeCode();
void BadgeCode(hstring const& value);
Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVector
<TicketMachine::Operator>> GetCollection();
hstring FirstName();
void FirstName(hstring const& value);
hstring LastName();
void LastName(hstring const& value);
TicketMachine::Date BirthDate();
void BirthDate(TicketMachine::Date const& value);
void Add();
void Edit();
void Delete();
void Save();
private:
hstring m_badgeCode{ L"" };
hstring m_firstName{ L"" };
hstring m_lastName{ L"" };
TicketMachine::Date m_birthDate;
Windows::Foundation::Collections::IVector<TicketMachine::Operator> m_operators;
};
}
namespace winrt::TicketMachine::factory_implementation
{
struct Operator : OperatorT<Operator, implementation::Operator>
{
};
}
Operator.cpp
#include "pch.h"
#include "Operator.h"
#include "Operator.g.cpp"
#include <winrt/Windows.Foundation.Collections.h>
namespace winrt::TicketMachine::implementation
{
hstring Operator::BadgeCode()
{
return m_badgeCode;
}
void Operator::BadgeCode(hstring const& value)
{
m_badgeCode = value;
}
Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVector
<TicketMachine::Operator>> Operator::GetCollection()
{
winrt::TicketMachine::Operator opr[4];
m_operators = winrt::single_threaded_vector<winrt::TicketMachine::Operator>();
int i = 0;
while (i <= 4)
{
opr[i] = winrt::make<winrt::TicketMachine::implementation::Operator>();
switch (i)
{
case 0: opr[i].BadgeCode(L"8C8882D9-5BDE-4FDA-86C7-DB9926DB75AE");
opr[i].FirstName(L"Mario");
opr[i].LastName(L"Rossi");
m_birthDate.Year(2001); m_birthDate.Month(12); m_birthDate.Day(10);
opr[i].BirthDate(m_birthDate);
break;
case 1: opr[i].BadgeCode(L"441661CA-9A44-4B6A-AA4B-B99B13F4C214");
opr[i].FirstName(L"Franco");
opr[i].LastName(L"Verdi");
m_birthDate.Year(1976); m_birthDate.Month(4); m_birthDate.Day(22);
opr[i].BirthDate(m_birthDate);
break;
case 2: opr[i].BadgeCode(L"1F10AFF3-4365-445B-B256-AFFA2CCC8BF7");
opr[i].FirstName(L"Gianna");
opr[i].LastName(L"Gialli");
m_birthDate.Year(1985); m_birthDate.Month(5); m_birthDate.Day(31);
opr[i].BirthDate(m_birthDate);
break;
case 3: opr[i].BadgeCode(L"FE1F901F-6BBF-45F0-8885-DE049153FFD0");
opr[i].FirstName(L"Maria");
opr[i].LastName(L"Bianchi");
m_birthDate.Year(1998); m_birthDate.Month(12); m_birthDate.Day(19);
opr[i].BirthDate(m_birthDate);
break;
case 4: opr[i].BadgeCode(L"C3D6F5F7-7981-433B-8A7E-61DD6AF8735E");
opr[i].FirstName(L"Rosa");
opr[i].LastName(L"Neri");
m_birthDate.Year(1990); m_birthDate.Month(2); m_birthDate.Day(22);
opr[i].BirthDate(m_birthDate);
break;
}
m_operators.Append(opr[i]);
++i;
}
co_return m_operators;
}
hstring Operator::FirstName()
{
return m_firstName;
}
void Operator::FirstName(hstring const& value)
{
m_firstName = value;
}
hstring Operator::LastName()
{
return m_lastName;
}
void Operator::LastName(hstring const& value)
{
m_lastName = value;
}
TicketMachine::Date Operator::BirthDate()
{
return m_birthDate;
}
void Operator::BirthDate(TicketMachine::Date const& value)
{
m_birthDate = value;
}
void Operator::Add()
{
}
void Operator::Edit()
{
}
void Operator::Delete()
{
}
void Operator::Save()
{
}
}
Desk.h
#pragma once
#include "Desk.g.h"
namespace winrt::TicketMachine::implementation
{
struct Desk : DeskT<Desk>
{
Desk() = default;
Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVector
<TicketMachine::Desk>> GetCollection();
hstring DeskNumber();
void DeskNumber(hstring const& value);
hstring DeskCode();
void DeskCode(hstring const& value);
hstring DeskName();
void DeskName(hstring const& value);
void Add();
void Edit();
void Delete();
void Save();
private:
hstring m_deskNumber{ L"" };
hstring m_deskCode{ L"" };
hstring m_deskName {L"" };
Windows::Foundation::Collections::IVector<TicketMachine::Desk> m_desks;
};
}
namespace winrt::TicketMachine::factory_implementation
{
struct Desk : DeskT<Desk, implementation::Desk>
{
};
}
Desk.cpp
#include "pch.h"
#include "Desk.h"
#include "Desk.g.cpp"
namespace winrt::TicketMachine::implementation
{
Windows::Foundation::IAsyncOperation$lt;Windows::Foundation::Collections::IVector
<TicketMachine::Desk>> Desk::GetCollection()
{
winrt::TicketMachine::Desk dsk[4];
m_desks = winrt::single_threaded_vector<winrt::TicketMachine::Desk>();
int i = 0;
while (i <= 4)
{
dsk[i] = winrt::make<winrt::TicketMachine::implementation::Desk>();
switch (i)
{
case 0: dsk[i].DeskNumber(L"1");
dsk[i].DeskCode(L"A");
dsk[i].DeskName(L"A01");
break;
case 1: dsk[i].DeskNumber(L"2");
dsk[i].DeskCode(L"B");
dsk[i].DeskName(L"B02");
break;
case 2: dsk[i].DeskNumber(L"3");
dsk[i].DeskCode(L"C");
dsk[i].DeskName(L"C03");
break;
case 3: dsk[i].DeskNumber(L"4");
dsk[i].DeskCode(L"D");
dsk[i].DeskName(L"D04");
break;
case 4: dsk[i].DeskNumber(L"5");
dsk[i].DeskCode(L"E");
dsk[i].DeskName(L"E05");
break;
}
m_desks.Append(dsk[i]);
++i;
}
co_return m_desks;
}
hstring Desk::DeskNumber()
{
return m_deskNumber;
}
void Desk::DeskNumber(hstring const& value)
{
m_deskNumber = value;
}
hstring Desk::DeskCode()
{
return m_deskCode;
}
void Desk::DeskCode(hstring const& value)
{
m_deskCode = value;
}
hstring Desk::DeskName()
{
return m_deskName;
}
void Desk::DeskName(hstring const& value)
{
m_deskName = value;
}
void Desk::Add()
{
}
void Desk::Edit()
{
}
void Desk::Delete()
{
}
void Desk::Save()
{
}
}
Ticket.h
#pragma once
#include "Ticket.g.h"
namespace winrt::TicketMachine::implementation
{
struct Ticket : TicketT<Ticket>
{
Ticket() = default;
void Create();
hstring TicketNumber();
void TicketNumber(hstring const& value);
hstring TicketBarcode();
void TicketBarcode(hstring const& value);
int32_t MaxQueueItems();
void MaxQueueItems(int32_t value);
TicketMachine::Date CreationDate();
void CreationDate(TicketMachine::Date const& value);
Windows::Storage::StorageFile TicketQRCode();
void TicketQRCode(Windows::Storage::StorageFile const& value);
Windows::Storage::StorageFile ImageFile();
void ImageFile(Windows::Storage::StorageFile const& value);
hstring TicketToString();
void Add();
void Edit();
void Delete();
void Save();
hstring ServiceCode();
void ServiceCode(hstring const& value);
hstring ServiceName();
void ServiceName(hstring const& value);
hstring DeskNumber();
void DeskNumber(hstring const& value);
hstring DeskCode();
void DeskCode(hstring const& value);
hstring DeskName();
void DeskName(hstring const& value);
hstring FirstName();
void FirstName(hstring const& value);
hstring LastName();
void LastName(hstring const& value);
TicketMachine::Date BirthDate();
void BirthDate(TicketMachine::Date const& value);
private:
hstring m_ticketNumber{ L"" };
hstring m_ticketBarcode{ L"" };
int32_t m_maxQueueElements{ 100 };
winrt::TicketMachine::Date m_creationDate;
winrt::TicketMachine::Service m_service;
winrt::TicketMachine::Desk m_desk;
winrt::TicketMachine::Operator m_operator;
int m_index = 0;
hstring m_ticketString = L"";
hstring m_serviceCode{ L"" };
hstring m_serviceName{ L"" };
hstring m_deskNumber{ L"" };
hstring m_deskCode{ L"" };
hstring m_deskName{ L"" };
hstring m_badgeCode{ L"" };
hstring m_firstName{ L"" };
hstring m_lastName{ L"" };
TicketMachine::Date m_birthDate;
TicketMachine::Date GetDate();
};
}
namespace winrt::TicketMachine::factory_implementation
{
struct Ticket : TicketT<Ticket, implementation::Ticket>
{
};
}
Ticket.cpp
#include "pch.h"
#include "Ticket.h"
#include "Ticket.g.cpp"
#include "Helpers.h"
#include <ctime>
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
namespace winrt::TicketMachine::implementation
{
void Ticket::Create()
{
winrt::TicketMachine::Service srv;
Collection<winrt::TicketMachine::Service> _srv;
auto _services = _srv.GetItems(srv,true);
winrt::TicketMachine::Desk dsk;
Collection<winrt::TicketMachine::Desk> _dsk;
auto _desks = _dsk.GetItems(dsk, true);
winrt::TicketMachine::Operator opr;
Collection<winrt::TicketMachine::Operator> _opr;
auto _operators = _opr.GetItems(opr, true);
if (m_index > m_maxQueueElements)
m_index = 1;
else
m_index += 1;
TicketNumber(winrt::to_hstring(m_index));
int rnd = rand() % 5;
hstring tmpTicket = _services[rnd].ServiceCode() + L", " +
_services[rnd].ServiceName() + L", " +
_desks[rnd].DeskNumber() + L", " +
_desks[rnd].DeskCode() + L", " +
_desks[rnd].DeskName() + L", " +
_operators[rnd].BadgeCode() + L", " +
_operators[rnd].FirstName() + L", " +
_operators[rnd].LastName() + L", " +
_operators[rnd].BirthDate().DateToString() + L", ";
m_creationDate = GetDate();
m_serviceCode = _services[rnd].ServiceCode();
m_serviceName = _services[rnd].ServiceName();
m_deskNumber = _desks[rnd].DeskNumber();
m_deskCode = _desks[rnd].DeskCode();
m_deskName = _desks[rnd].DeskName();
m_badgeCode = _operators[rnd].BadgeCode();
m_firstName = _operators[rnd].FirstName();
m_lastName = _operators[rnd].LastName();
m_birthDate = _operators[rnd].BirthDate();
m_ticketString = tmpTicket;
}
TicketMachine::Date implementation::Ticket::GetDate()
{
std::time_t t = std::time(0);
struct tm date;
_localtime64_s(&date, &t);
int year = date.tm_year + 1900;
int month = date.tm_mon + 1;
int day = date.tm_mday;
int hour = date.tm_hour;
int minutes = date.tm_min;
int seconds = date.tm_sec;
TicketMachine::Date dt;
dt.Year(year);
dt.Month(month);
dt.Day(day);
dt.Hh(hour);
dt.Mm(minutes);
dt.Ss(seconds);
return dt;
}
hstring Ticket::TicketToString()
{
return m_ticketString;
}
hstring Ticket::TicketNumber()
{
return m_ticketNumber;
}
void Ticket::TicketNumber(hstring const& value)
{
m_ticketNumber = value;
}
hstring Ticket::TicketBarcode()
{
return m_ticketBarcode;
}
void Ticket::TicketBarcode(hstring const& value)
{
m_ticketBarcode = value;
}
int32_t Ticket::MaxQueueItems()
{
return m_maxQueueElements;
}
void Ticket::MaxQueueItems(int32_t value)
{
m_maxQueueElements = value;
}
TicketMachine::Date Ticket::CreationDate()
{
return m_creationDate;
}
void Ticket::CreationDate(TicketMachine::Date const& value)
{
m_creationDate = value;
}
Windows::Storage::StorageFile Ticket::TicketQRCode()
{
return nullptr;
}
void Ticket::TicketQRCode(Windows::Storage::StorageFile const& )
{
}
Windows::Storage::StorageFile Ticket::ImageFile()
{
return nullptr;
}
void Ticket::ImageFile(Windows::Storage::StorageFile const& )
{
}
void Ticket::Add()
{
}
void Ticket::Edit()
{
}
void Ticket::Delete()
{
}
void Ticket::Save()
{
}
hstring Ticket::ServiceCode()
{
return m_serviceCode;
}
void Ticket::ServiceCode(hstring const& value)
{
m_serviceCode = value;
}
hstring Ticket::ServiceName()
{
return m_serviceName;
}
void Ticket::ServiceName(hstring const& value)
{
m_serviceName = value;
}
hstring Ticket::DeskNumber()
{
return m_deskNumber;
}
void Ticket::DeskNumber(hstring const& value)
{
m_deskNumber = value;
}
hstring Ticket::DeskCode()
{
return m_deskCode;
}
void Ticket::DeskCode(hstring const& value)
{
m_deskCode = value;
}
hstring Ticket::DeskName()
{
return m_deskName;
}
void Ticket::DeskName(hstring const& value)
{
m_deskName = value;
}
hstring Ticket::FirstName()
{
return m_firstName;
}
void Ticket::FirstName(hstring const& value)
{
m_firstName = value;
}
hstring Ticket::LastName()
{
return m_lastName;
}
void Ticket::LastName(hstring const& value)
{
m_lastName = value;
}
TicketMachine::Date Ticket::BirthDate()
{
return m_birthDate;
}
void Ticket::BirthDate(TicketMachine::Date const& value)
{
m_birthDate = value;
}
}
The Create
method in the Ticket.cpp file must be called, by the applications that consume the component, whenever a new ticket needs to be created.
This, after being created, will queue to any other pending tickets, for the purpose we will implement the OperationsQueue
class.
Each ticket, once the operation at the counter is completed, will be inserted in another area (in a real application in a database, etc.) that we will represent here with the OperationsLog
class.
If we analyze the Create
method, we can see that the Collection
class is instantiated several times.
The purpose of this template class is to create a list of:
and associate them with each other (if we want) in a pseudo-random way. Therefore, every time we perform the Create
method, we will have a different combination of services, counters and operators.
This mechanism, at runtime, will simulate the function of a Totem for the issuance of tickets. The Collection
template class is located in the Helpers.h include
file:
Helpers.h
#pragma once
#include "pch.h"
#include <vector>
#include <algorithm>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <string>
#include <functional>
#include <numeric>
#include <random>
#include <chrono>
#include <ppltasks.h >
template <typename T>
inline void CreateIntegerRange(T& _vector, int min, int max)
{
int index = min;
do
{
_vector.insert(_vector.end(), index);
++index;
} while (index <= max);
}
template <typename T>
class RandomOrder
{
public:
RandomOrder() = delete;
virtual ~RandomOrder()
{
m_vector.clear();
}
RandomOrder(std::vector<T> D)
{
unsigned seed = (unsigned)std::chrono::system_clock::now().time_since_epoch().count();
std::shuffle(D.begin(), D.end(), std::default_random_engine(seed));
m_vector = D;
}
std::vector<T> get()
{
return m_vector;
}
private:
std::vector<T> m_vector;
};
template <typename I>
class Collection
{
public:
Collection() = default;
winrt::Windows::Foundation::Collections::IVectorView<I>
GetView(winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Foundation::
Collections::IVector<I>> items)
{
return Concurrency::create_task([items] {
return items.get().GetView();
}).get();
}
std::vector<I> GetItems(I const& E, bool shuffle)
{
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::
Foundation::Collections::IVector<I>> items = E.GetCollection();
winrt::Windows::Foundation::Collections::IVectorView<I> viewItems = GetView(items);
std::vector<I> _items;
int i = 0;
int elements = viewItems.Size();
do
{
_items.push_back(viewItems.GetAt(i));
++i;
} while (i <= (elements - 1));
if (shuffle)
{
RandomOrder<I> rdrItems(_items);
return rdrItems.get();
}
else
return _items;
}
};
Events in a C++/WinRT Component
As we well know, in general, an event in a class is the way to notify the application that uses that class that "something" has just happened (e.g.: click on a button, end of downloading a file, etc.). In C++/WinRT, an event is declared as a delegate type and must be logged in order to be managed; finally when no longer used, it can be revoked.
Depending on the case, an event can be declared as a delegate type among the following:
winrt::d bound
winrt::event
We will use the type winrt::d ebound
when the event does not have to communicate "with the outside"; instead we will use winrt::event
when the event must be accessible through ABI (application binary interface), that is, when both the WinRT component, and the application that consumes it, access the event.
Equally notorious is that an event can have arguments.
In the case of the winrt::d
elegated type, it is not strictly necessary that the argument be a Windows Runtime type, but it can be of type Windows Runtime or custom type. Conversely, for the winrt::event
type, the argument can only be of type Windows Runtime or primitive type (int
, float
, etc.). Now, since the events of our TicketMachine
component must "pass" ABI, we declare in the Common.idl file in the Operations queue class two events of type: winr::event
, both with argument of type bool
.
The first event will be invoked when a new ticket is issued and the second when an operation has been completed.
Common.idl
.............................
.............................
.............................
void OperationItemAdded(Boolean isAdded);
void OperationCompleted(Boolean isCompleted);
event Windows.Foundation.EventHandler<TicketMachine.StartQueueEventArgs> OnQueueItemAdded;
event Windows.Foundation.EventHandler<TicketMachine.CloseOperationEventArgs> OnOperationCompleted;
Following the declaration and implementation of the classes:
OperationsQueue
OperationsLog
StartQueueEventArgs
CloseOperationEventArgs
Property
OperationsQueue.h
#pragma once
#include "OperationsQueue.g.h"
namespace winrt::TicketMachine::implementation
{
struct OperationsQueue : OperationsQueueT<OperationsQueue>
{
OperationsQueue() = default;
Windows::Foundation::IAsyncOperation<Windows::Foundation::
Collections::IVector<TicketMachine::OperationsQueue>> GetCollection();
void OperationItemAdded(bool isAdded);
void OperationCompleted(bool isCompleted);
winrt::event_token OnQueueItemAdded(Windows::Foundation::EventHandler
<TicketMachine::StartQueueEventArgs> const& handler);
winrt::event_token OnOperationCompleted(Windows::Foundation::EventHandler
<TicketMachine::CloseOperationEventArgs> const& handler);
void OnQueueItemAdded(winrt::event_token const& token) noexcept;
void OnOperationCompleted(winrt::event_token const& token) noexcept;
hstring ServiceCode();
void ServiceCode(hstring const& value);
hstring ServiceName();
void ServiceName(hstring const& value);
hstring DeskNumber();
void DeskNumber(hstring const& value);
hstring DeskCode();
void DeskCode(hstring const& value);
hstring DeskName();
void DeskName(hstring const& value);
hstring TicketNumber();
void TicketNumber(hstring const& value);
hstring TicketBarcode();
void TicketBarcode(hstring const& value);
int32_t MaxQueueItems();
void MaxQueueItems(int32_t value);
TicketMachine::Date CreationDate();
void CreationDate(TicketMachine::Date const& value);
Windows::Storage::StorageFile TicketQRCode();
void TicketQRCode(Windows::Storage::StorageFile const& value);
void Add();
void Edit();
void Delete();
void Save();
private:
hstring m_serviceCode{ L"" },
m_serviceName{ L"" },
m_deskCode{ L"" },
m_deskName{ L"" },
m_deskNumber{ L"" },
m_ticketNumber{ L"" };
int32_t m_maxQueueItems{ 0 };
TicketMachine::Date m_CreationDate;
winrt::event<Windows::Foundation::EventHandler<StartQueueEventArgs>> m_OnStartQueueEvent;
bool m_startQueue = false;
winrt::event<Windows::Foundation::EventHandler>CloseOperationEventArgs<<
m_OnCompletedEvent;
bool m_completed = false;
};
}
namespace winrt::TicketMachine::factory_implementation
{
struct OperationsQueue : OperationsQueueT<OperationsQueue, implementation::OperationsQueue>
{
};
}
OperationsQueue.cpp
#include "pch.h"
#include "OperationsQueue.h"
#include "OperationsQueue.g.cpp"
#include "StartQueueEventArgs.h"
#include "CloseOperationEventArgs.h"
#include "Helpers.h"
namespace winrt::TicketMachine::implementation
{
Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::
IVector<TicketMachine::OperationsQueue>> OperationsQueue::GetCollection()
{
return nullptr;
}
void OperationsQueue::OperationItemAdded(bool isAdded)
{
m_startQueue = isAdded;
auto args = winrt::make_self<winrt::TicketMachine::implementation::
StartQueueEventArgs>(m_startQueue);
m_OnStartQueueEvent(*this, *args);
}
void OperationsQueue::OperationCompleted(bool isCompleted)
{
m_completed = isCompleted;
auto args = winrt::make_self<winrt::TicketMachine::implementation::
CloseOperationEventArgs>(m_completed);
m_OnCompletedEvent(*this, *args);
}
winrt::event_token OperationsQueue::OnQueueItemAdded(Windows::Foundation::
EventHandler<ticketmachine::startqueueeventargs> const& handler)
{
return m_OnStartQueueEvent.add(handler);
}
void OperationsQueue::OnQueueItemAdded(winrt::event_token const& token) noexcept
{
m_OnStartQueueEvent.remove(token);
}
winrt::event_token OperationsQueue::OnOperationCompleted
(Windows::Foundation::EventHandler<ticketmachine::closeoperationeventargs> const& handler)
{
return m_OnCompletedEvent.add(handler);
}
void OperationsQueue::OnOperationCompleted(winrt::event_token const& token) noexcept
{
m_OnCompletedEvent.remove(token);
}
hstring OperationsQueue::ServiceCode()
{
return m_serviceCode;
}
void OperationsQueue::ServiceCode(hstring const& value)
{
m_serviceCode = value;
}
hstring OperationsQueue::ServiceName()
{
return m_serviceName;
}
void OperationsQueue::ServiceName(hstring const& value)
{
m_serviceName = value;
}
hstring OperationsQueue::DeskNumber()
{
return m_deskNumber;
}
void OperationsQueue::DeskNumber(hstring const& value)
{
m_deskNumber = value;
}
hstring OperationsQueue::DeskCode()
{
return m_deskCode;
}
void OperationsQueue::DeskCode(hstring const& value)
{
m_deskCode = value;
}
hstring OperationsQueue::DeskName()
{
return m_deskName;
}
void OperationsQueue::DeskName(hstring const& value)
{
m_deskName = value;
}
hstring OperationsQueue::TicketNumber()
{
return m_ticketNumber;
}
void OperationsQueue::TicketNumber(hstring const& value)
{
m_ticketNumber = value;
}
hstring OperationsQueue::TicketBarcode()
{
return L"";
}
void OperationsQueue::TicketBarcode(hstring const& )
{
}
int32_t OperationsQueue::MaxQueueItems()
{
return m_maxQueueItems;
}
void OperationsQueue::MaxQueueItems(int32_t value)
{
m_maxQueueItems = value;
}
TicketMachine::Date OperationsQueue::CreationDate()
{
return m_CreationDate;
}
void OperationsQueue::CreationDate(TicketMachine::Date const& value)
{
m_CreationDate = value;
}
Windows::Storage::StorageFile OperationsQueue::TicketQRCode()
{
return nullptr;
}
void OperationsQueue::TicketQRCode(Windows::Storage::StorageFile const& )
{
}
void OperationsQueue::Add()
{
}
void OperationsQueue::Edit()
{
}
void OperationsQueue::Delete()
{
}
void OperationsQueue::Save()
{
}
}</ticketmachine::closeoperationeventargs></ticketmachine::startqueueeventargs>
OperationsLog.h
#pragma once
#include "OperationsLog.g.h"
namespace winrt::TicketMachine::implementation
{
struct OperationsLog : OperationsLogT<operationslog>
{
OperationsLog() = default;
Windows::Foundation::IAsyncOperation<Windows::Foundation::
Collections::IVector<TicketMachine::OperationsLog>> GetCollection();
hstring ServiceCode();
void ServiceCode(hstring const& value);
hstring ServiceName();
void ServiceName(hstring const& value);
hstring TicketNumber();
void TicketNumber(hstring const& value);
hstring TicketBarcode();
void TicketBarcode(hstring const& value);
int32_t MaxQueueItems();
void MaxQueueItems(int32_t value);
TicketMachine::Date CreationDate();
void CreationDate(TicketMachine::Date const& value);
Windows::Storage::StorageFile TicketQRCode();
void TicketQRCode(Windows::Storage::StorageFile const& value);
void Add();
void Edit();
void Delete();
void Save();
private:
hstring m_serviceCode{ L"" },
m_serviceName{ L"" },
m_deskCode{ L"" },
m_deskName{ L"" },
m_deskNumber{ L"" },
m_ticketNumber{ L"" };
int32_t m_maxQueueItems{ 0 };
TicketMachine::Date m_CreationDate;
};
}
namespace winrt::TicketMachine::factory_implementation
{
struct OperationsLog : OperationsLogT<OperationsLog, implementation::OperationsLog>
{
};
}
</operationslog>
OperationsLog.cpp
#include "pch.h"
#include "OperationsLog.h"
#include "OperationsLog.g.cpp"
#include "Helpers.h"
namespace winrt::TicketMachine::implementation
{
Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::
IVector<TicketMachine::OperationsLog>> OperationsLog::GetCollection()
{
return nullptr;
}
hstring OperationsLog::ServiceCode()
{
return m_serviceCode;
}
void OperationsLog::ServiceCode(hstring const& value)
{
m_serviceCode = value;
}
hstring OperationsLog::ServiceName()
{
return m_serviceName;
}
void OperationsLog::ServiceName(hstring const& value)
{
m_serviceName = value;
}
hstring OperationsLog::TicketNumber()
{
return m_ticketNumber;
}
void OperationsLog::TicketNumber(hstring const& value)
{
m_ticketNumber = value;
}
hstring OperationsLog::TicketBarcode()
{
return L"";
}
void OperationsLog::TicketBarcode(hstring const& )
{
}
int32_t OperationsLog::MaxQueueItems()
{
return m_maxQueueItems;
}
void OperationsLog::MaxQueueItems(int32_t value)
{
m_maxQueueItems = value;
}
TicketMachine::Date OperationsLog::CreationDate()
{
return m_CreationDate;
}
void OperationsLog::CreationDate(TicketMachine::Date const& value)
{
m_CreationDate = value;
}
Windows::Storage::StorageFile OperationsLog::TicketQRCode()
{
return nullptr;
}
void OperationsLog::TicketQRCode(Windows::Storage::StorageFile const& )
{
}
void OperationsLog::Add()
{
}
void OperationsLog::Edit()
{
}
void OperationsLog::Delete()
{
}
void OperationsLog::Save()
{
}
}
StartQueueEventArgs.h
#pragma once
#include "StartQueueEventArgs.g.h"
namespace winrt::TicketMachine::implementation
{
struct StartQueueEventArgs : StartQueueEventArgsT<StartQueueEventArgs>
{
StartQueueEventArgs() = default;
bool ItemAdded();
StartQueueEventArgs(bool isAdded);
private:
bool m_added = false;
};
}
StartQueueEventArgs.cpp
#include "pch.h"
#include "StartQueueEventArgs.h"
#include "StartQueueEventArgs.g.cpp"
namespace winrt::TicketMachine::implementation
{
bool StartQueueEventArgs::ItemAdded()
{
return m_added;
}
StartQueueEventArgs::StartQueueEventArgs(bool isAdded) :m_added(isAdded)
{
m_added = isAdded;
}
}
CloseOperationEventArgs.h
#pragma once
#include "CloseOperationEventArgs.g.h"
namespace winrt::TicketMachine::implementation
{
struct CloseOperationEventArgs : CloseOperationEventArgsT<CloseOperationEventArgs>
{
CloseOperationEventArgs() = default;
bool OperationClosed();
CloseOperationEventArgs(bool isClosed);
private:
bool m_closed = false;
};
}
CloseOperationEventArgs.cpp
#include "pch.h"
#include "CloseOperationEventArgs.h"
#include "CloseOperationEventArgs.g.cpp"
namespace winrt::TicketMachine::implementation
{
bool CloseOperationEventArgs::OperationClosed()
{
return m_closed;
}
implementation::CloseOperationEventArgs::CloseOperationEventArgs(bool isClosed)
{
m_closed = isClosed;
}
}
The component is ready, we compile the project and produce an updated version of TicketMachine.dll and TicketMachine.winmd.
In the next, as well as the last part of this tutorial, we will see how to consume the C++/WinRT component in a Win32 C++ application.
Go to Part 4. Implementing the demo C++ Win32 Application
History
- 14th January, 2022: Initial version