Windows Komponen 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), diimplementasikan sebagai pustaka berbasis file header, dan dirancang untuk memberi Anda akses kelas satu ke API Windows modern. Untuk mempelajari cara membuat komponen runtime Windows menggunakan C++/WinRT, lihat komponen Runtime Windows dengan C++/WinRT.

Topik ini menunjukkan cara menggunakan C++/CX untuk membuat komponen runtime Windows—komponen yang dapat dipanggil dari aplikasi Universal Windows yang dibuat menggunakan bahasa runtime Windows (C#, Visual Basic, C++, atau Javascript).

Ada beberapa alasan untuk membangun komponen runtime Windows di C ++.

  • Untuk mendapatkan keuntungan kinerja C ++ dalam operasi yang kompleks atau komputasi intensif.
  • Untuk menggunakan kembali kode yang sudah ditulis dan diuji.

Saat Anda membangun solusi yang berisi proyek JavaScript atau .NET, dan proyek komponen runtime Windows, file proyek JavaScript dan DLL yang dikompilasi digabungkan menjadi 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 SDK Ekstensi. Untuk informasi selengkapnya, lihat Membuat Kit Pengembangan Perangkat Lunak.

Secara umum, ketika Anda mengkodekan komponen C ++/CX Anda, gunakan pustaka C ++ biasa dan tipe bawaan, kecuali pada batas antarmuka biner abstrak (ABI) tempat Anda meneruskan data ke dan dari kode dalam paket .winmd lain. Di sana, gunakan Windows jenis 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 acara untuk mengimplementasikan acara 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 C++ Visual (C++/CX).

Aturan casing dan penamaan

JavaScript

JavaScript peka huruf besar/kecil. Oleh karena itu, Anda harus mengikuti konvensi casing ini:

  • Saat Anda mereferensikan ruang nama 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 yang dapat diaktifkan dan nama ruang nama tidak dapat berisi karakter UNICODE.

.NET

Bahasa .NET mengikuti aturan casing normal mereka.

Instantiating objek

Hanya Windows jenis Runtime yang dapat diteruskan melintasi batas ABI. Compiler akan meningkatkan kesalahan jika komponen memiliki jenis seperti std::wstring sebagai tipe kembali atau parameter dalam metode publik. Ekstensi komponen Visual C ++ (C ++/CX) tipe bawaan termasuk skalar biasa seperti int dan double, dan juga setara dengan typedef int32, float64, dan sebagainya. Untuk informasi selengkapnya, lihat Tipe Sistem (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();

Tipe bawaan C++/CX, tipe pustaka, dan tipe 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 runtime Windows dapat berisi beberapa kelas yang dapat diaktifkan publik serta kelas tambahan yang hanya diketahui secara internal pada komponen. Terapkan atribut WebHostHidden ke tipe C++/CX yang tidak dimaksudkan untuk terlihat oleh JavaScript.

Semua kelas publik harus berada di ruang nama root yang sama yang memiliki nama yang sama dengan file metadata komponen. Misalnya, kelas yang diberi nama A.B.C.MyClass dapat dibuat hanya jika didefinisikan dalam file metadata yang diberi nama A.winmd atau A.B.winmd atau A.B.C.winmd. Nama DLL tidak diperlukan untuk mencocokkan nama file .winmd.

Kode klien membuat instance komponen dengan menggunakan kata kunci baru (Baru dalam Visual Basic) sama seperti untuk kelas mana pun.

Kelas yang dapat diaktifkan harus dinyatakan sebagai kelas ref publik yang disegel. Kata kunci kelas ref memberi tahu kompiler untuk membuat kelas sebagai jenis yang kompatibel dengan runtime Windows, dan kata kunci yang disegel menentukan bahwa kelas tidak dapat diwariskan. Runtime Windows saat ini tidak mendukung model pewarisan umum; model pewarisan terbatas yang mendukung pembuatan kontrol XAML kustom. Untuk informasi selengkapnya, lihat Kelas dan struktur Ref (C++/CX).

Untuk C ++/CX, semua primitif numerik didefinisikan dalam namespace default. Ruang nama Platform berisi kelas C ++/CX yang khusus untuk sistem tipe Runtime Windows. Ini termasuk kelas Platform::String dan Platform::Object class. Jenis koleksi konkret seperti Platform::Collections::Kelas peta dan Platform::Collections::Vector class didefinisikan dalam platform::Collections namespace. 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 Tipe Sistem (C++/CX).

Metode yang mengembalikan nilai tipe 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 penataan nilai yang ditentukan pengguna di seluruh ABI, tentukan objek JavaScript yang memiliki anggota yang sama dengan struktur nilai yang didefinisikan dalam C ++/CX. Anda kemudian dapat meneruskan objek itu sebagai argumen ke metode C ++/CX sehingga objek secara implisit dikonversi ke tipe 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 mendefinisikan kelas yang mengimplementasikan IPropertySet (tidak ditampilkan).

Dalam bahasa .NET, Anda cukup membuat variabel jenis yang didefinisikan 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 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, 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);
};

Tapi itu tidak bisa 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 dalam file header.

JavaScript ini selalu memanggil kelebihan beban yang dikaitkan:

var nativeObject = new CppComponent.NumberClass();
var num = nativeObject.getNumber(9);
document.getElementById('P4').innerHTML = num;

.NET

Bahasa .NET mengenali kelebihan beban di kelas ref C ++/CX sama seperti di kelas .NET mana pun.

DateTime

Dalam runtime Windows, objek Windows::Foundation::D ateTime hanyalah bilangan bulat bertanda tangan 64-bit yang mewakili jumlah interval 100 nanodetik baik sebelum atau setelah 1 Januari 1601. Tidak ada metode pada objek Windows: Foundation: :D ateTime. Sebagai gantinya, setiap bahasa memproyeksikan DateTime dengan cara yang asli dari bahasa tersebut: objek Tanggal di JavaScript dan tipe 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 melewati System.DateTime ke komponen C++/CX, metode menerimanya sebagai Windows::Foundation::D ateTime. Saat komponen melewati 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 melewati batas ABI sebagai pegangan ke jenis Runtime Windows seperti Windows::Foundation::Collections::IVector^ dan Windows::Foundation::Collections::IMap^. Misalnya, jika Anda mengembalikan pegangan ke Platform::Collections::Map, gagang tersebut secara implisit dikonversi menjadi Windows::Foundation::Collections::IMap^. Antarmuka koleksi didefinisikan dalam ruang nama yang terpisah dari kelas C ++/CX yang menyediakan implementasi konkret. Bahasa JavaScript dan .NET mengkonsumsi 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 IVectorT<> sebagai IListT<>.

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

Melewati 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 IDictionaryK<, V>.

private void GetDictionary()
{
    var nativeObject = new CppComponent.CollectionExample();
    IDictionary<int, string> d = nativeObject.GetMap();
    ResultText.Text += d[2].ToString();
}

Properti

Kelas ref publik dalam ekstensi komponen C ++/CX mengekspos anggota data publik sebagai properti, dengan menggunakan kata kunci properti. Konsep ini identik dengan properti .NET. Properti sepele menyerupai anggota data karena fungsinya implisit. Properti non-sepele memiliki aksesori get and set eksplisit dan variabel pribadi bernama yang merupakan "toko latar" untuk nilainya. Dalam contoh ini, variabel anggota pribadi _propertyAValue adalah toko pendukung untuk PropertyA. Properti dapat menyalakan 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;

Properti akses bahasa .NET pada objek C ++/CX asli seperti yang terjadi 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 acara

Delegasi adalah jenis runtime Windows 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 menyediakan keamanan tipe dengan mengaktifkan kompiler untuk memverifikasi jenis pengembalian dan jenis parameter fungsi. Deklarasi delegasi menyerupai tanda tangan fungsi, implementasi menyerupai definisi kelas, dan pemanggilan menyerupai pemanggilan fungsi.

Menambahkan pendengar acara

Anda dapat menggunakan kata kunci acara untuk mendeklarasikan anggota publik dari jenis delegasi tertentu. Kode klien berlangganan acara 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 acara dalam komponen C ++ sama dengan berlangganan acara 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 acara untuk satu acara

JavaScript memiliki metode addEventListener yang memungkinkan beberapa penangan untuk berlangganan satu acara.

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 penangan peristiwa dapat berlangganan acara dengan menggunakan operator += seperti yang ditunjukkan pada contoh sebelumnya.

Enum

Enum Runtime Windows dalam C ++/CX dideklarasikan dengan menggunakan enum kelas publik; menyerupai enum cakupan 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 dilewatkan 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];

Baik C # dan Visual Basic memiliki dukungan bahasa untuk enums. 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 runtime Windows lainnya, gunakan Task Class (Concurrency Runtime). Untuk informasi selengkapnya, lihat dan Paralelisme Tugas (Concurrency Runtime).

Untuk menerapkan metode asinkron dalam C ++/CX, gunakan fungsi create_async yang didefinisikan dalam ppltasks.h. Untuk informasi selengkapnya, lihat Membuat Operasi Asinkron di C++/CX untuk aplikasi UWP. Sebagai contoh, lihat Panduan membuat komponen C++/CX Windows Runtime, dan menyebutnya dari JavaScript atau C#. Bahasa .NET mengkonsumsi metode asinkron C ++/CX sama seperti metode asinkron yang didefinisikan dalam .NET.

Pengecualian

Anda dapat melemparkan jenis pengecualian apa pun yang ditentukan oleh Windows Runtime. Anda tidak dapat memperoleh jenis kustom dari jenis pengecualian runtime Windows mana pun. Namun, Anda dapat melempar COMException dan menyediakan HRESULT khusus 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 KOMPONEN DLL, Anda dapat mengatur debugger untuk mengaktifkan skrip, atau melangkah melalui kode asli di komponen, tetapi tidak keduanya pada saat yang bersamaan. Untuk mengubah pengaturan, pilih simpul proyek JavaScript di Penjelajah Solusi lalu pilih Properti, Debugging, Tipe Debugger.

Pastikan untuk memilih kemampuan yang sesuai dalam desainer paket. Misalnya, jika Anda mencoba membuka file gambar di pustaka Gambar pengguna dengan menggunakan API Runtime Windows, pastikan untuk memilih kotak centang Pustaka Gambar di panel Kemampuan desainer manifes.

Jika kode JavaScript Anda tampaknya tidak mengenali properti atau metode publik di komponen, pastikan bahwa di JavaScript Anda menggunakan casing unta. Misalnya, metode LogCalc C ++/CX harus direferensikan 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 debug berikutnya atau membangun operasi. Jika perlu, Anda kemudian dapat menambahkan referensi perakitan ke DLL.