Komponen Windows Runtime dengan C++/CX
Catatan
Topik ini ada untuk membantu Anda mempertahankan aplikasi C++/CX Anda. Tetapi kami sarankan Anda menggunakan C++/WinRT untuk aplikasi baru. C++/WinRT adalah proyeksi bahasa C++17 modern yang sepenuhnya standar untuk API Windows Runtime (WinRT), yang diimplementasikan sebagai pustaka berbasis file header, dan dirancang untuk memberi Anda akses kelas satu ke Windows API modern. Untuk mempelajari cara membuat komponen Windows Runtime menggunakan C++/WinRT, lihat komponen Windows Runtime dengan C++/WinRT.
Topik ini menunjukkan cara menggunakan C++/CX untuk membuat komponen Windows Runtime—komponen yang dapat dipanggil dari aplikasi Universal Windows yang dibuat menggunakan bahasa Windows Runtime apa pun (C#, Visual Basic, C++, atau JavaScript).
Ada beberapa alasan untuk membangun komponen Windows Runtime di C++.
- Untuk mendapatkan keuntungan performa C++ dalam operasi yang kompleks atau intensif secara komputasi.
- Untuk menggunakan kembali kode yang sudah ditulis dan diuji.
Saat Anda membangun solusi yang berisi proyek JavaScript atau .NET, dan proyek komponen Windows Runtime, file proyek JavaScript dan DLL yang dikompilasi digabungkan ke dalam satu paket, yang dapat Anda debug secara lokal di simulator atau dari jarak jauh pada perangkat yang ditambatkan. Anda juga dapat mendistribusikan hanya proyek komponen sebagai Extension SDK. Untuk informasi selengkapnya, lihat Membuat Kit Pengembangan Perangkat Lunak.
Secara umum, saat Anda membuat kode komponen C++/CX, gunakan pustaka C++ reguler dan jenis bawaan, kecuali pada batas antarmuka biner abstrak (ABI) tempat Anda meneruskan data ke dan dari kode dalam paket .winmd lainnya. Di sana, gunakan jenis Windows Runtime dan sintaks khusus yang didukung C++/CX untuk membuat dan memanipulasi jenis tersebut. Selain itu, dalam kode C++/CX Anda, gunakan jenis seperti delegasi dan peristiwa untuk mengimplementasikan peristiwa yang dapat dinaikkan dari komponen Anda dan ditangani di JavaScript, Visual Basic, C++, atau C#. Untuk informasi selengkapnya tentang sintaks C++/CX, lihat Referensi Bahasa Visual C++ (C++/CX).
Aturan kapital dan penamaan
JavaScript
JavaScript peka huruf besar/kecil. Oleh karena itu, Anda harus mengikuti konvensi kapital ini:
- Saat Anda mereferensikan namespace dan kelas C++, gunakan casing yang sama yang digunakan di sisi C++.
- Saat Anda memanggil metode, gunakan casing unta bahkan jika nama metode dikapitalisasi di sisi C++. Misalnya, metode C++ GetDate() harus dipanggil dari JavaScript sebagai getDate().
- Nama kelas dan nama namespace layanan yang dapat diaktifkan tidak boleh berisi karakter UNICODE.
.NET
Bahasa .NET mengikuti aturan kapitalisasi normalnya.
Membuat instans objek
Hanya jenis Windows Runtime yang dapat diteruskan melalui batas ABI. Pengkompilasi akan memunculkan kesalahan jika komponen memiliki jenis seperti std::wstring sebagai jenis pengembalian atau parameter dalam metode publik. Jenis bawaan Ekstensi komponen Visual C++ (C++/CX) mencakup skalar biasa seperti int dan double, dan juga jenisdef yang setara dengan int32, float64, dan sebagainya. Untuk informasi selengkapnya, lihat Sistem Jenis (C++/CX).
// ref class definition in C++
public ref class SampleRefClass sealed
{
// Class members...
// #include <valarray>
public:
double LogCalc(double input)
{
// Use C++ standard library as usual.
return std::log(input);
}
};
//Instantiation in JavaScript (requires "Add reference > Project reference")
var nativeObject = new CppComponent.SampleRefClass();
//Call a method and display result in a XAML TextBlock
var num = nativeObject.LogCalc(21.5);
ResultText.Text = num.ToString();
Jenis bawaan C++/CX, jenis pustaka, dan jenis Windows Runtime
Kelas yang dapat diaktifkan (juga dikenal sebagai kelas ref) adalah kelas yang dapat dibuat dari bahasa lain seperti JavaScript, C# atau Visual Basic. Agar dapat dikonsumsi dari bahasa lain, komponen harus berisi setidaknya satu kelas yang dapat diaktifkan.
Komponen Windows Runtime dapat berisi beberapa kelas yang dapat diaktifkan publik serta kelas tambahan yang hanya diketahui secara internal ke komponen. Terapkan atribut WebHostHidden ke jenis C++/CX yang tidak dimaksudkan untuk terlihat oleh JavaScript.
Semua kelas publik harus berada di namespace layanan akar yang sama yang memiliki nama yang sama dengan file metadata komponen. Misalnya, kelas yang bernama A.B.C.MyClass hanya dapat dibuat jika didefinisikan dalam file metadata yang bernama A.winmd atau A.B.winmd atau A.B.C.winmd. Nama DLL tidak diperlukan untuk mencocokkan nama file .winmd.
Kode klien membuat instans komponen dengan menggunakan kata kunci baru (Baru di Visual Basic) sama seperti untuk kelas apa pun.
Kelas yang dapat diaktifkan harus dinyatakan sebagai kelas ref publik yang disegel. Kata kunci kelas ref memberi tahu pengkompilasi untuk membuat kelas sebagai jenis yang kompatibel dengan Windows Runtime, dan kata kunci yang disegel menentukan bahwa kelas tidak dapat diwariskan. Windows Runtime saat ini tidak mendukung model pewarisan umum; model pewarisan terbatas mendukung pembuatan kontrol XAML kustom. Untuk informasi selengkapnya, lihat Kelas dan struktur Ref (C++/CX).
Untuk C++/CX, semua primitif numerik ditentukan dalam namespace default. Namespace layanan Platform berisi kelas C++/CX yang khusus untuk sistem jenis Windows Runtime. Ini termasuk kelas Platform::String dan kelas Platform::Object . Jenis koleksi beton seperti Platform::Collections::Map class dan Platform::Collections::Vector class didefinisikan dalam namespace Platform::Collections . Antarmuka publik yang diterapkan jenis ini didefinisikan dalam Windows::Foundation::Collections Namespace (C++/CX). Jenis antarmuka inilah yang dikonsumsi oleh JavaScript, C# dan Visual Basic. Untuk informasi selengkapnya, lihat Sistem Jenis (C++/CX).
Metode yang mengembalikan nilai jenis bawaan
// #include <valarray>
public:
double LogCalc(double input)
{
// Use C++ standard library as usual.
return std::log(input);
}
//Call a method
var nativeObject = new CppComponent.SampleRefClass;
var num = nativeObject.logCalc(21.5);
document.getElementById('P2').innerHTML = num;
Metode yang mengembalikan struct nilai kustom
namespace CppComponent
{
// Custom struct
public value struct PlayerData
{
Platform::String^ Name;
int Number;
double ScoringAverage;
};
public ref class Player sealed
{
private:
PlayerData m_player;
public:
property PlayerData PlayerStats
{
PlayerData get(){ return m_player; }
void set(PlayerData data) {m_player = data;}
}
};
}
Untuk meneruskan struct nilai yang ditentukan pengguna di seluruh ABI, tentukan objek JavaScript yang memiliki anggota yang sama dengan struct nilai yang ditentukan dalam C++/CX. Anda kemudian dapat meneruskan objek tersebut sebagai argumen ke metode C++/CX sehingga objek secara implisit dikonversi ke jenis C++/CX.
// Get and set the value struct
function GetAndSetPlayerData() {
// Create an object to pass to C++
var myData =
{ name: "Bob Homer", number: 12, scoringAverage: .357 };
var nativeObject = new CppComponent.Player();
nativeObject.playerStats = myData;
// Retrieve C++ value struct into new JavaScript object
var myData2 = nativeObject.playerStats;
document.getElementById('P3').innerHTML = myData.name + " , " + myData.number + " , " + myData.scoringAverage.toPrecision(3);
}
Pendekatan lain adalah menentukan kelas yang mengimplementasikan IPropertySet (tidak ditampilkan).
Dalam bahasa .NET, Anda hanya membuat variabel jenis yang ditentukan dalam komponen C++/CX.
private void GetAndSetPlayerData()
{
// Create a ref class
var player = new CppComponent.Player();
// Create a variable of a value struct
// type that is defined in C++
CppComponent.PlayerData myPlayer;
myPlayer.Name = "Babe Ruth";
myPlayer.Number = 12;
myPlayer.ScoringAverage = .398;
// Set the property
player.PlayerStats = myPlayer;
// Get the property and store it in a new variable
CppComponent.PlayerData myPlayer2 = player.PlayerStats;
ResultText.Text += myPlayer.Name + " , " + myPlayer.Number.ToString() +
" , " + myPlayer.ScoringAverage.ToString();
}
Metode yang Kelebihan Beban
Kelas ref publik C++/CX dapat berisi metode yang kelebihan beban, tetapi JavaScript memiliki kemampuan terbatas untuk membedakan metode yang kelebihan beban. Misalnya, ini dapat membedakan antara tanda tangan ini:
public ref class NumberClass sealed
{
public:
int GetNumber(int i);
int GetNumber(int i, Platform::String^ str);
double GetNumber(int i, MyData^ d);
};
Tetapi tidak dapat membedakan antara ini:
int GetNumber(int i);
double GetNumber(double d);
Dalam kasus yang ambigu, Anda dapat memastikan bahwa JavaScript selalu memanggil kelebihan beban tertentu dengan menerapkan atribut Windows::Foundation::Metadata::D efaultOverload ke tanda tangan metode di file header.
JavaScript ini selalu memanggil kelebihan beban yang diatribusikan:
var nativeObject = new CppComponent.NumberClass();
var num = nativeObject.getNumber(9);
document.getElementById('P4').innerHTML = num;
.NET
Bahasa .NET mengenali kelebihan beban di kelas C++/CX ref sama seperti di kelas .NET apa pun.
DateTime
Dalam Windows Runtime, objek Windows::Foundation::D ateTime hanyalah bilangan bulat bertanda 64-bit yang mewakili jumlah interval 100 nanodetik baik sebelum atau sesudah 1 Januari 1601. Tidak ada metode pada objek Windows:Foundation::D ateTime. Sebaliknya, setiap bahasa memproyeksikan DateTime dengan cara yang asli dari bahasa tersebut: objek Tanggal di JavaScript dan jenis System.DateTime dan System.DateTimeOffset di .NET.
public ref class MyDateClass sealed
{
public:
property Windows::Foundation::DateTime TimeStamp;
void SetTime(Windows::Foundation::DateTime dt)
{
auto cal = ref new Windows::Globalization::Calendar();
cal->SetDateTime(dt);
TimeStamp = cal->GetDateTime(); // or TimeStamp = dt;
}
};
Saat Anda meneruskan nilai DateTime dari C++/CX ke JavaScript, JavaScript menerimanya sebagai objek Tanggal dan menampilkannya secara default sebagai string tanggal bentuk panjang.
function SetAndGetDate() {
var nativeObject = new CppComponent.MyDateClass();
var myDate = new Date(1956, 4, 21);
nativeObject.setTime(myDate);
var myDate2 = nativeObject.timeStamp;
//prints long form date string
document.getElementById('P5').innerHTML = myDate2;
}
Ketika bahasa .NET meneruskan System.DateTime ke komponen C++/CX, metode menerimanya sebagai Windows::Foundation::D ateTime. Ketika komponen meneruskan Windows::Foundation::D ateTime ke metode .NET, metode Framework menerimanya sebagai DateTimeOffset.
private void DateTimeExample()
{
// Pass a System.DateTime to a C++ method
// that takes a Windows::Foundation::DateTime
DateTime dt = DateTime.Now;
var nativeObject = new CppComponent.MyDateClass();
nativeObject.SetTime(dt);
// Retrieve a Windows::Foundation::DateTime as a
// System.DateTimeOffset
DateTimeOffset myDate = nativeObject.TimeStamp;
// Print the long-form date string
ResultText.Text += myDate.ToString();
}
Koleksi dan array
Koleksi selalu diteruskan di seluruh batas ABI sebagai handel ke jenis Windows Runtime seperti Windows::Foundation::Collections::IVector^ dan Windows::Foundation::Collections::IMap^. Misalnya, jika Anda mengembalikan handel ke Platform::Collections::Map, secara implisit mengonversi ke Windows::Foundation::Collections::IMap^. Antarmuka koleksi didefinisikan dalam namespace yang terpisah dari kelas C++/CX yang menyediakan implementasi konkret. Bahasa JavaScript dan .NET menggunakan antarmuka. Untuk informasi selengkapnya, lihat Koleksi (C++/CX)dan Array dan WriteOnlyArray (C++/CX).
Melewati IVector
// Windows::Foundation::Collections::IVector across the ABI.
//#include <algorithm>
//#include <collection.h>
Windows::Foundation::Collections::IVector<int>^ SortVector(Windows::Foundation::Collections::IVector<int>^ vec)
{
std::sort(begin(vec), end(vec));
return vec;
}
var nativeObject = new CppComponent.CollectionExample();
// Call the method to sort an integer array
var inVector = [14, 12, 45, 89, 23];
var outVector = nativeObject.sortVector(inVector);
var result = "Sorted vector to array:";
for (var i = 0; i < outVector.length; i++)
{
outVector[i];
result += outVector[i].toString() + ",";
}
document.getElementById('P6').innerHTML = result;
Bahasa .NET melihat IVector<T> sebagai IList<T>.
private void SortListItems()
{
IList<int> myList = new List<int>();
myList.Add(5);
myList.Add(9);
myList.Add(17);
myList.Add(2);
var nativeObject = new CppComponent.CollectionExample();
IList<int> mySortedList = nativeObject.SortVector(myList);
foreach (var item in mySortedList)
{
ResultText.Text += " " + item.ToString();
}
}
Meneruskan IMap
// #include <map>
//#include <collection.h>
Windows::Foundation::Collections::IMap<int, Platform::String^> ^GetMap(void)
{
Windows::Foundation::Collections::IMap<int, Platform::String^> ^ret =
ref new Platform::Collections::Map<int, Platform::String^>;
ret->Insert(1, "One ");
ret->Insert(2, "Two ");
ret->Insert(3, "Three ");
ret->Insert(4, "Four ");
ret->Insert(5, "Five ");
return ret;
}
// Call the method to get the map
var outputMap = nativeObject.getMap();
var mStr = "Map result:" + outputMap.lookup(1) + outputMap.lookup(2)
+ outputMap.lookup(3) + outputMap.lookup(4) + outputMap.lookup(5);
document.getElementById('P7').innerHTML = mStr;
Bahasa .NET melihat IMap dan IDictionary<K, V>.
private void GetDictionary()
{
var nativeObject = new CppComponent.CollectionExample();
IDictionary<int, string> d = nativeObject.GetMap();
ResultText.Text += d[2].ToString();
}
Properti
Kelas ref publik di ekstensi komponen C++/CX mengekspos anggota data publik sebagai properti, dengan menggunakan kata kunci properti . Konsepnya identik dengan properti .NET. Properti sepele menyerupai anggota data karena fungsinya implisit. Properti non-sepele memiliki dapatkan dan atur pengakses eksplisit dan variabel privat bernama yang merupakan "penyimpanan cadangan" untuk nilai tersebut. Dalam contoh ini, variabel anggota privat _propertyAValue adalah penyimpanan cadangan untuk PropertyA. Properti dapat mengaktifkan peristiwa saat nilainya berubah, dan aplikasi klien dapat mendaftar untuk menerima peristiwa tersebut.
//Properties
public delegate void PropertyChangedHandler(Platform::Object^ sender, int arg);
public ref class PropertyExample sealed
{
public:
PropertyExample(){}
// Event that is fired when PropertyA changes
event PropertyChangedHandler^ PropertyChangedEvent;
// Property that has custom setter/getter
property int PropertyA
{
int get() { return m_propertyAValue; }
void set(int propertyAValue)
{
if (propertyAValue != m_propertyAValue)
{
m_propertyAValue = propertyAValue;
// Fire event. (See event example below.)
PropertyChangedEvent(this, propertyAValue);
}
}
}
// Trivial get/set property that has a compiler-generated backing store.
property Platform::String^ PropertyB;
private:
// Backing store for propertyA.
int m_propertyAValue;
};
var nativeObject = new CppComponent.PropertyExample();
var propValue = nativeObject.propertyA;
document.getElementById('P8').innerHTML = propValue;
//Set the string property
nativeObject.propertyB = "What is the meaning of the universe?";
document.getElementById('P9').innerHTML += nativeObject.propertyB;
Bahasa .NET mengakses properti pada objek C++/CX asli seperti yang mereka lakukan pada objek .NET.
private void GetAProperty()
{
// Get the value of the integer property
// Instantiate the C++ object
var obj = new CppComponent.PropertyExample();
// Get an integer property
var propValue = obj.PropertyA;
ResultText.Text += propValue.ToString();
// Set a string property
obj.PropertyB = " What is the meaning of the universe?";
ResultText.Text += obj.PropertyB;
}
Delegasi dan peristiwa
Delegasi adalah jenis Windows Runtime yang mewakili objek fungsi. Anda dapat menggunakan delegasi sehubungan dengan peristiwa, panggilan balik, dan panggilan metode asinkron untuk menentukan tindakan yang akan dilakukan nanti. Seperti objek fungsi, delegasi memberikan keamanan jenis dengan mengaktifkan pengkompilasi untuk memverifikasi jenis pengembalian dan jenis parameter fungsi. Deklarasi delegasi menyerupan tanda tangan fungsi, implementasinya menyerupan definisi kelas, dan pemanggilan menyerupan pemanggilan fungsi.
Menambahkan pendengar peristiwa
Anda dapat menggunakan kata kunci peristiwa untuk mendeklarasikan anggota publik dari jenis delegasi tertentu. Kode klien berlangganan peristiwa dengan menggunakan mekanisme standar yang disediakan dalam bahasa tertentu.
public:
event SomeHandler^ someEvent;
Contoh ini menggunakan kode C++ yang sama seperti untuk bagian properti sebelumnya.
function Button_Click() {
var nativeObj = new CppComponent.PropertyExample();
// Define an event handler method
var singlecasthandler = function (ev) {
document.getElementById('P10').innerHTML = "The button was clicked and the value is " + ev;
};
// Subscribe to the event
nativeObj.onpropertychangedevent = singlecasthandler;
// Set the value of the property and fire the event
var propValue = 21;
nativeObj.propertyA = 2 * propValue;
}
Dalam bahasa .NET, berlangganan peristiwa dalam komponen C++ sama dengan berlangganan peristiwa di kelas .NET:
//Subscribe to event and call method that causes it to be fired.
private void TestMethod()
{
var objWithEvent = new CppComponent.PropertyExample();
objWithEvent.PropertyChangedEvent += objWithEvent_PropertyChangedEvent;
objWithEvent.PropertyA = 42;
}
//Event handler method
private void objWithEvent_PropertyChangedEvent(object __param0, int __param1)
{
ResultText.Text = "the event was fired and the result is " +
__param1.ToString();
}
Menambahkan beberapa pendengar peristiwa untuk satu peristiwa
JavaScript memiliki metode addEventListener yang memungkinkan beberapa handler berlangganan satu peristiwa.
public delegate void SomeHandler(Platform::String^ str);
public ref class LangSample sealed
{
public:
event SomeHandler^ someEvent;
property Platform::String^ PropertyA;
// Method that fires an event
void FireEvent(Platform::String^ str)
{
someEvent(Platform::String::Concat(str, PropertyA->ToString()));
}
//...
};
// Add two event handlers
var multicast1 = function (ev) {
document.getElementById('P11').innerHTML = "Handler 1: " + ev.target;
};
var multicast2 = function (ev) {
document.getElementById('P12').innerHTML = "Handler 2: " + ev.target;
};
var nativeObject = new CppComponent.LangSample();
//Subscribe to the same event
nativeObject.addEventListener("someevent", multicast1);
nativeObject.addEventListener("someevent", multicast2);
nativeObject.propertyA = "42";
// This method should fire an event
nativeObject.fireEvent("The answer is ");
Di C#, sejumlah penanganan aktivitas dapat berlangganan peristiwa dengan menggunakan operator += seperti yang ditunjukkan pada contoh sebelumnya.
Enum
Enum Windows Runtime di C++/CX dideklarasikan dengan menggunakan enum kelas publik; ini menyerubungi enum tercakup dalam C++standar.
public enum class Direction {North, South, East, West};
public ref class EnumExampleClass sealed
{
public:
property Direction CurrentDirection
{
Direction get(){return m_direction; }
}
private:
Direction m_direction;
};
Nilai enum diteruskan antara C++/CX dan JavaScript sebagai bilangan bulat. Anda dapat secara opsional mendeklarasikan objek JavaScript yang berisi nilai bernama yang sama dengan enum C++/CX dan menggunakannya sebagai berikut.
var Direction = { 0: "North", 1: "South", 2: "East", 3: "West" };
//. . .
var nativeObject = new CppComponent.EnumExampleClass();
var curDirection = nativeObject.currentDirection;
document.getElementById('P13').innerHTML =
Direction[curDirection];
C# dan Visual Basic memiliki dukungan bahasa untuk enum. Bahasa-bahasa ini melihat kelas enum publik C++ sama seperti mereka akan melihat enum .NET.
Metode asinkron
Untuk menggunakan metode asinkron yang diekspos oleh objek Windows Runtime lainnya, gunakan Kelas tugas (Concurrency Runtime). Untuk informasi selengkapnya, lihat dan Paralelisme Tugas (Runtime Konkurensi).
Untuk menerapkan metode asinkron di C++/CX, gunakan fungsi create_async yang ditentukan dalam ppltasks.h. Untuk informasi selengkapnya, lihat Membuat Operasi Asinkron di C++/CX untuk aplikasi UWP. Misalnya, lihat Panduan membuat komponen Windows Runtime C++/CX, dan memanggilnya dari JavaScript atau C#. Bahasa .NET menggunakan metode asinkron C++/CX sama seperti metode asinkron yang ditentukan dalam .NET.
Pengecualian
Anda dapat melempar jenis pengecualian apa pun yang ditentukan oleh Windows Runtime. Anda tidak dapat memperoleh tipe kustom dari tipe pengecualian Windows Runtime apa pun. Namun, Anda dapat melempar COMException dan menyediakan HRESULT kustom yang dapat diakses oleh kode yang menangkap pengecualian. Tidak ada cara untuk menentukan Pesan kustom dalam COMException.
Tips men-debug
Saat Anda men-debug solusi JavaScript yang memiliki DLL komponen, Anda dapat mengatur debugger untuk mengaktifkan melangkah melalui skrip, atau melangkah melalui kode asli dalam komponen, tetapi tidak keduanya pada saat yang sama. Untuk mengubah pengaturan, pilih simpul proyek JavaScript di Penjelajah Solusi lalu pilih Properti, Debugging, Jenis Debugger.
Pastikan untuk memilih kemampuan yang sesuai dalam perancang paket. Misalnya, jika Anda mencoba membuka file gambar di pustaka Gambar pengguna dengan menggunakan WINDOWS Runtime API, pastikan untuk memilih kotak centang Pustaka Gambar di panel Kemampuan perancang manifes.
Jika kode JavaScript Anda tampaknya tidak mengenali properti atau metode publik dalam komponen, pastikan bahwa di JavaScript Anda menggunakan casing unta. Misalnya, metode LogCalc C++/CX harus dirujuk sebagai logCalc di JavaScript.
Jika Anda menghapus proyek komponen C++/CX Windows Runtime dari solusi, Anda juga harus menghapus referensi proyek secara manual dari proyek JavaScript. Kegagalan untuk melakukannya mencegah operasi debug atau build berikutnya. Jika perlu, Anda kemudian dapat menambahkan referensi perakitan ke DLL.