建立 C++/CX Windows 執行階段元件,並從 JavaScript 或 C# 呼叫該元件的逐步解說
注意
本主題可協助您維護 C++/CX 應用程式。 但我們建議您將 C++/WinRT 用於新的應用程式。 C++/WinRT 是完全標準現代的 Windows 執行階段 (WinRT) API 的 C++17 語言投影,僅實作為標頭檔案式程式庫,以及設計用來提供您現代化 Windows API 的第一級存取。 若要了解如何使用 C++/WinRT 建立 Windows 執行階段元件,請參閱使用 C++/WinRT 的 Windows 執行階段元件。
本逐步解說示範如何建立可從 JavaScript、C# 或 Visual Basic 呼叫的基本 Windows 執行階段元件 DLL。 開始本逐步解說之前,請確定您了解一些概念,例如:抽象二進位介面 (ABI)、ref 類別,以及讓 ref 類別更容易使用的 Visual C++ 元件擴充功能。 如需詳細資訊,請參閱使用 C++/CX 的 Windows 執行階段元件和 Visual C++ 語言參考 (C++/CX)。
建立 C++ 元件 DLL
在此範例中,我們會先建立元件專案,但您也可以先建立 JavaScript 專案。 順序並不重要。
請注意,元件的主類別包含屬性和方法定義的範例,以及事件宣告。 提供這些只是為了向您展示它是如何完成的。 它們不是必需的,在本範例中,我們將用我們自己的程式碼取代所有產生的程式碼。
建立 C++ 元件專案
在 Visual Studio 功能表列上,選擇檔案、新增、專案。
在新專案對話方塊的左窗格中,展開 Visual C++,然後選取通用 Windows 應用程式節點。
在中央窗格中,選取 Windows 執行階段元件,然後將專案命名為WinRT_CPP。
選擇 [確定] 按鈕。
若要將可啟動的類別新增至元件
可啟動類別是用戶端程式碼可以使用 new 運算式 (在 Visual Basic 中為 New 或在 C++ 中為 ref new) 建立的類別。 在您的元件中,您將其宣告為公用參考類別已密封。 事實上,Class1.h 和 .cpp 檔案已經有一個 ref 類別。 您可以變更名稱,但在本例中我們將使用預設名稱 - Class1。 如果需要,您可以在元件中定義其他 ref 類別或一般類別。 有關 ref 類別的更多資訊,請參見 類型系統 (C++/CX)。
將這些 #include 指令新增至 Class1.h 中:
#include <collection.h>
#include <ppl.h>
#include <amp.h>
#include <amp_math.h>
collection.h 是 C++ 特定類別 (例如 Platform::Collections::Vector 類別和 Platform::Collections::Map 類別) 的標頭檔,其會實作由 Windows 執行階段所定義與語言無關的介面。 amp 標頭用於在 GPU 上執行運算。 它們沒有 Windows 執行階段對等項目,這沒關係,因為它們是私人的。 一般來說,出於效能原因,您應該在元件內部使用 ISO C++ 程式碼和標準函式庫; 只是 Windows 執行階段介面必須以 Windows 執行階段類型表示。
在命名空間範圍內新增委派
委派是一種建構,可定義方法的參數和傳回類型。 事件是特定委派類型的執行個體,訂閱該事件的任何事件處理程序方法都必須具有委派中指定的簽章。 以下程式碼定義了一個接受 int 並傳回無效的委派類型。 接下來,程式碼宣告此類型的公共事件; 這使得用戶端程式碼能夠提供在事件觸發時呼叫的方法。
在 Class1.h 的命名空間範圍中,於 Class1 宣告之前新增以下委派宣告。
public delegate void PrimeFoundHandler(int result);
如果將程式碼貼到 Visual Studio 時程式碼未正確排列,只需按 Ctrl+K+D 即可修復整個檔案的縮排。
新增公用成員
該類別會公開三個公用方法和一個公用事件。 第一個方法是同步的,因為其執行速度非常快。 由於其他兩個方法可能需要一些時間,因此它們是非同步的,所以不會阻塞 UI 執行緒。 這些方法會傳回 IAsyncOperationWithProgress 和 IAsyncActionWithProgress。 前者定義了一個傳回結果的非同步方法,後者定義了一個傳回無效的非同步方法。 這些介面還使用戶端程式碼能夠接收有關作業進度的更新。
public:
// Synchronous method.
Windows::Foundation::Collections::IVector<double>^ ComputeResult(double input);
// Asynchronous methods
Windows::Foundation::IAsyncOperationWithProgress<Windows::Foundation::Collections::IVector<int>^, double>^
GetPrimesOrdered(int first, int last);
Windows::Foundation::IAsyncActionWithProgress<double>^ GetPrimesUnordered(int first, int last);
// Event whose type is a delegate "class"
event PrimeFoundHandler^ primeFoundEvent;
新增私人成員
這個類別包含三個私人成員:兩個用於數值計算的輔助方法,和一個用於將事件呼叫從工作執行緒封送回 UI 執行緒的 CoreDispatcher 物件。
private:
bool is_prime(int n);
Windows::UI::Core::CoreDispatcher^ m_dispatcher;
新增標頭和命名空間指示詞
- 在 Class1.cpp 中,新增以下 #include 指示詞:
#include <ppltasks.h>
#include <concurrent_vector.h>
- 現在,新增這些 using 陳述式來提取所需的命名空間:
using namespace concurrency;
using namespace Platform::Collections;
using namespace Windows::Foundation::Collections;
using namespace Windows::Foundation;
using namespace Windows::UI::Core;
新增 ComputeResult 的實作
在 Class1.cpp 中,新增以下方法實作。 該方法會在呼叫執行緒上同步執行,但速度非常快,因為它使用 C++ AMP 在 GPU 上平行計算。 如需詳細資訊,請參閱 C++ AMP 概觀。 結果將附加到 Platform::Collections::Vector<T> 具體類型,該類型會在傳回時隱含轉換為 Windows::Foundation::Collections::IVector<T>。
//Public API
IVector<double>^ Class1::ComputeResult(double input)
{
// Implement your function in ISO C++ or
// call into your C++ lib or DLL here. This example uses AMP.
float numbers[] = { 1.0, 10.0, 60.0, 100.0, 600.0, 10000.0 };
array_view<float, 1> logs(6, numbers);
// See http://msdn.microsoft.com/library/hh305254.aspx
parallel_for_each(
logs.extent,
[=] (index<1> idx) restrict(amp)
{
logs[idx] = concurrency::fast_math::log10(logs[idx]);
}
);
// Return a Windows Runtime-compatible type across the ABI
auto res = ref new Vector<double>();
int len = safe_cast<int>(logs.extent.size());
for(int i = 0; i < len; i++)
{
res->Append(logs[i]);
}
// res is implicitly cast to IVector<double>
return res;
}
新增 GetPrimesOrdered 及其輔助方法的實作
在 Class1.cpp 中,新增 GetPrimesOrdered 和 is_prime 輔助方法的實作。 GetPrimesOrdered 會使用 concurrent_vector 類別和 parallel_for 函式循環來分割工作,並使用執行程序之電腦的最大資源來產生結果。 計算、儲存和排序結果後,它們將會新增到 Platform::Collections::Vector<T> 中,並作為 Windows::Foundation::Collections::IVector<T> 傳回用戶端程式碼。
請注意進度報告程式的程式碼,這可讓用戶端連接進度列或其他 UI,以向使用者顯示作業將花費多長時間。 進度報告是有成本的。 必須在元件端觸發事件,並在 UI 執行緒上處理事件,並且必須在每次迭代時儲存進度值。 最小化成本的一種方法是限制觸發進度事件的頻率。 如果成本仍然過高,或者您無法估計作業的長度,請考慮使用進度環,這會顯示作業正在進行,但不會顯示完成之前的剩餘時間。
// Determines whether the input value is prime.
bool Class1::is_prime(int n)
{
if (n < 2)
return false;
for (int i = 2; i < n; ++i)
{
if ((n % i) == 0)
return false;
}
return true;
}
// This method computes all primes, orders them, then returns the ordered results.
IAsyncOperationWithProgress<IVector<int>^, double>^ Class1::GetPrimesOrdered(int first, int last)
{
return create_async([this, first, last]
(progress_reporter<double> reporter) -> IVector<int>^ {
// Ensure that the input values are in range.
if (first < 0 || last < 0) {
throw ref new InvalidArgumentException();
}
// Perform the computation in parallel.
concurrent_vector<int> primes;
long operation = 0;
long range = last - first + 1;
double lastPercent = 0.0;
parallel_for(first, last + 1, [this, &primes, &operation,
range, &lastPercent, reporter](int n) {
// Increment and store the number of times the parallel
// loop has been called on all threads combined. There
// is a performance cost to maintaining a count, and
// passing the delegate back to the UI thread, but it's
// necessary if we want to display a determinate progress
// bar that goes from 0 to 100%. We can avoid the cost by
// setting the ProgressBar IsDeterminate property to false
// or by using a ProgressRing.
if(InterlockedIncrement(&operation) % 100 == 0)
{
reporter.report(100.0 * operation / range);
}
// If the value is prime, add it to the local vector.
if (is_prime(n)) {
primes.push_back(n);
}
});
// Sort the results.
std::sort(begin(primes), end(primes), std::less<int>());
reporter.report(100.0);
// Copy the results to a Vector object, which is
// implicitly converted to the IVector return type. IVector
// makes collections of data available to other
// Windows Runtime components.
return ref new Vector<int>(primes.begin(), primes.end());
});
}
新增 GetPrimesUnordered 的實作
建立 C++ 元件的最後一步是在 Class1.cpp 中新增 GetPrimesUnordered 的實作。 此方法會在找到每個結果時將其傳回,而無需等到找到所有結果。 每個結果都會在事件處理程序中傳回,並即時顯示在 UI 上。 再次注意,使用進度報告程式。 此方法也使用 is_prime 輔助方法。
// This method returns no value. Instead, it fires an event each time a
// prime is found, and passes the prime through the event.
// It also passes progress info.
IAsyncActionWithProgress<double>^ Class1::GetPrimesUnordered(int first, int last)
{
auto window = Windows::UI::Core::CoreWindow::GetForCurrentThread();
m_dispatcher = window->Dispatcher;
return create_async([this, first, last](progress_reporter<double> reporter) {
// Ensure that the input values are in range.
if (first < 0 || last < 0) {
throw ref new InvalidArgumentException();
}
// In this particular example, we don't actually use this to store
// results since we pass results one at a time directly back to
// UI as they are found. However, we have to provide this variable
// as a parameter to parallel_for.
concurrent_vector<int> primes;
long operation = 0;
long range = last - first + 1;
double lastPercent = 0.0;
// Perform the computation in parallel.
parallel_for(first, last + 1,
[this, &primes, &operation, range, &lastPercent, reporter](int n)
{
// Store the number of times the parallel loop has been called
// on all threads combined. See comment in previous method.
if(InterlockedIncrement(&operation) % 100 == 0)
{
reporter.report(100.0 * operation / range);
}
// If the value is prime, pass it immediately to the UI thread.
if (is_prime(n))
{
// Since this code is probably running on a worker
// thread, and we are passing the data back to the
// UI thread, we have to use a CoreDispatcher object.
m_dispatcher->RunAsync( CoreDispatcherPriority::Normal,
ref new DispatchedHandler([this, n, operation, range]()
{
this->primeFoundEvent(n);
}, Platform::CallbackContext::Any));
}
});
reporter.report(100.0);
});
}
建立 JavaScript 用戶端應用程式 (Visual Studio 2017)
如果您想建立 C# 用戶端,則可以跳過本節。
注意
Visual Studio 2019 不支援使用 JavaScript 的通用 Windows 平台 (UWP) 專案。 請參閱 Visual Studio 2019 中的 JavaScript 和 TypeScript 若要跟著本節操作,建議您使用 Visual Studio 2017。 請參閱 Visual Studio 2017 中的 JavaScript。
建立 JavaScript 專案
在方案總管 (在 Visual Studio 2017 中; 請參閱上述的備註) 中,開啟解決方案節點的捷徑功能表,然後選擇新增、新專案。
展開 JavaScript (它可能嵌套在其他語言下),並選擇空白應用程式 (通用 Windows)。
選擇確定按鈕,接受預設名稱「App1」。
開啟 App1 專案節點的捷徑功能表,然後選擇設定為啟動專案。
新增對 WinRT_CPP 的專案參考:
開啟參考節點的捷徑功能表,然後選擇新增參考。
在「參考管理器」對話方塊的左窗格中,選擇專案,然後選擇解決方案。
在中央窗格中,選擇 WinRT_CPP,然後選擇確定按鈕
新增叫用 JavaScript 事件處理程序的 HTML
將此 HTML 貼到 default.html 頁面的<主體>節點中:
<div id="LogButtonDiv">
<button id="logButton">Logarithms using AMP</button>
</div>
<div id="LogResultDiv">
<p id="logResult"></p>
</div>
<div id="OrderedPrimeButtonDiv">
<button id="orderedPrimeButton">Primes using parallel_for with sort</button>
</div>
<div id="OrderedPrimeProgress">
<progress id="OrderedPrimesProgressBar" value="0" max="100"></progress>
</div>
<div id="OrderedPrimeResultDiv">
<p id="orderedPrimes">
Primes found (ordered):
</p>
</div>
<div id="UnorderedPrimeButtonDiv">
<button id="ButtonUnordered">Primes returned as they are produced.</button>
</div>
<div id="UnorderedPrimeDiv">
<progress id="UnorderedPrimesProgressBar" value="0" max="100"></progress>
</div>
<div id="UnorderedPrime">
<p id="unorderedPrimes">
Primes found (unordered):
</p>
</div>
<div id="ClearDiv">
<button id="Button_Clear">Clear</button>
</div>
新增樣式
在 default.css 中,移除主體樣式,然後新增以下樣式:
#LogButtonDiv {
border: orange solid 1px;
-ms-grid-row: 1; /* default is 1 */
-ms-grid-column: 1; /* default is 1 */
}
#LogResultDiv {
background: black;
border: red solid 1px;
-ms-grid-row: 1;
-ms-grid-column: 2;
}
#UnorderedPrimeButtonDiv, #OrderedPrimeButtonDiv {
border: orange solid 1px;
-ms-grid-row: 2;
-ms-grid-column:1;
}
#UnorderedPrimeProgress, #OrderedPrimeProgress {
border: red solid 1px;
-ms-grid-column-span: 2;
height: 40px;
}
#UnorderedPrimeResult, #OrderedPrimeResult {
border: red solid 1px;
font-size:smaller;
-ms-grid-row: 2;
-ms-grid-column: 3;
-ms-overflow-style:scrollbar;
}
新增呼叫元件 DLL 的 JavaScript 事件處理程序
在 default.js 檔案結尾新增以下函式。 當選擇主頁上的按鈕時,將呼叫這些函式。 請注意 JavaScript 如何啟動 C++ 類別,然後呼叫其方法並使用傳回值來填入 HTML 標籤。
var nativeObject = new WinRT_CPP.Class1();
function LogButton_Click() {
var val = nativeObject.computeResult(0);
var result = "";
for (i = 0; i < val.length; i++) {
result += val[i] + "<br/>";
}
document.getElementById('logResult').innerHTML = result;
}
function ButtonOrdered_Click() {
document.getElementById('orderedPrimes').innerHTML = "Primes found (ordered): ";
nativeObject.getPrimesOrdered(2, 10000).then(
function (v) {
for (var i = 0; i < v.length; i++)
document.getElementById('orderedPrimes').innerHTML += v[i] + " ";
},
function (error) {
document.getElementById('orderedPrimes').innerHTML += " " + error.description;
},
function (p) {
var progressBar = document.getElementById("OrderedPrimesProgressBar");
progressBar.value = p;
});
}
function ButtonUnordered_Click() {
document.getElementById('unorderedPrimes').innerHTML = "Primes found (unordered): ";
nativeObject.onprimefoundevent = handler_unordered;
nativeObject.getPrimesUnordered(2, 10000).then(
function () { },
function (error) {
document.getElementById("unorderedPrimes").innerHTML += " " + error.description;
},
function (p) {
var progressBar = document.getElementById("UnorderedPrimesProgressBar");
progressBar.value = p;
});
}
var handler_unordered = function (n) {
document.getElementById('unorderedPrimes').innerHTML += n.target.toString() + " ";
};
function ButtonClear_Click() {
document.getElementById('logResult').innerHTML = "";
document.getElementById("unorderedPrimes").innerHTML = "";
document.getElementById('orderedPrimes').innerHTML = "";
document.getElementById("UnorderedPrimesProgressBar").value = 0;
document.getElementById("OrderedPrimesProgressBar").value = 0;
}
新增程式碼以新增事件接聽程式,方法是將 default.js 中 app.onactivated 對 WinJS.UI.processAll 的現有呼叫取代為以下在 then 區塊中實作事件註冊的程式碼。 有關詳細說明,請參閱建立「Hello, World」應用程式 (JS)。
args.setPromise(WinJS.UI.processAll().then( function completed() {
var logButton = document.getElementById("logButton");
logButton.addEventListener("click", LogButton_Click, false);
var orderedPrimeButton = document.getElementById("orderedPrimeButton");
orderedPrimeButton.addEventListener("click", ButtonOrdered_Click, false);
var buttonUnordered = document.getElementById("ButtonUnordered");
buttonUnordered.addEventListener("click", ButtonUnordered_Click, false);
var buttonClear = document.getElementById("Button_Clear");
buttonClear.addEventListener("click", ButtonClear_Click, false);
}));
按下 F5 即可執行應用程式。
建立 C# 用戶端應用程式
建立 C# 專案
在「方案總管」中,開啟解決方案節點的捷徑功能表,選擇新增、新專案。
展開 Visual C# (它可能嵌套在其他語言底下),在左側窗格中選擇 Windows,然後選擇通用,在中間窗格中選擇空白應用程式。
將此應用程式命名為 CS_Client,然後選擇確定按鈕。
開啟 CS_Client 專案節點的捷徑功能表,然後選擇設定為啟動專案。
新增對 WinRT_CPP 的專案參考:
開啟參考節點的捷徑功能表,然後選擇新增參考。
在參考管理器對話方塊的左窗格中,選擇專案,然後選擇解決方案。
在中央窗格中,選擇 WinRT_CPP,然後選擇確定按鈕。
新增定義使用者介面的 XAML
將以下程式碼複製到 MainPage.xaml 中的 Grid 元素中。
<ScrollViewer>
<StackPanel Width="1400">
<Button x:Name="Button1" Width="340" Height="50" Margin="0,20,20,20" Content="Synchronous Logarithm Calculation" FontSize="16" Click="Button1_Click_1"/>
<TextBlock x:Name="Result1" Height="100" FontSize="14"></TextBlock>
<Button x:Name="PrimesOrderedButton" Content="Prime Numbers Ordered" FontSize="16" Width="340" Height="50" Margin="0,20,20,20" Click="PrimesOrderedButton_Click_1"></Button>
<ProgressBar x:Name="PrimesOrderedProgress" IsIndeterminate="false" Height="40"></ProgressBar>
<TextBlock x:Name="PrimesOrderedResult" MinHeight="100" FontSize="10" TextWrapping="Wrap"></TextBlock>
<Button x:Name="PrimesUnOrderedButton" Width="340" Height="50" Margin="0,20,20,20" Click="PrimesUnOrderedButton_Click_1" Content="Prime Numbers Unordered" FontSize="16"></Button>
<ProgressBar x:Name="PrimesUnOrderedProgress" IsIndeterminate="false" Height="40" ></ProgressBar>
<TextBlock x:Name="PrimesUnOrderedResult" MinHeight="100" FontSize="10" TextWrapping="Wrap"></TextBlock>
<Button x:Name="Clear_Button" Content="Clear" HorizontalAlignment="Left" Margin="0,20,20,20" VerticalAlignment="Top" Width="341" Click="Clear_Button_Click" FontSize="16"/>
</StackPanel>
</ScrollViewer>
新增按鈕的事件處理程序
在方案總管中,開啟 MainPage.xaml.cs。 (該檔案可能會嵌套在 MainPage.xaml 底下。) 為 System.Text 新增 using 指示詞,然後在 MainPage 類別中新增用於 Logarithm 計算的事件處理程序。
private void Button1_Click_1(object sender, RoutedEventArgs e)
{
// Create the object
var nativeObject = new WinRT_CPP.Class1();
// Call the synchronous method. val is an IList that
// contains the results.
var val = nativeObject.ComputeResult(0);
StringBuilder result = new StringBuilder();
foreach (var v in val)
{
result.Append(v).Append(System.Environment.NewLine);
}
this.Result1.Text = result.ToString();
}
新增排序結果的事件處理程序:
async private void PrimesOrderedButton_Click_1(object sender, RoutedEventArgs e)
{
var nativeObject = new WinRT_CPP.Class1();
StringBuilder sb = new StringBuilder();
sb.Append("Primes found (ordered): ");
PrimesOrderedResult.Text = sb.ToString();
// Call the asynchronous method
var asyncOp = nativeObject.GetPrimesOrdered(2, 100000);
// Before awaiting, provide a lambda or named method
// to handle the Progress event that is fired at regular
// intervals by the asyncOp object. This handler updates
// the progress bar in the UI.
asyncOp.Progress = (asyncInfo, progress) =>
{
PrimesOrderedProgress.Value = progress;
};
// Wait for the operation to complete
var asyncResult = await asyncOp;
// Convert the results to strings
foreach (var result in asyncResult)
{
sb.Append(result).Append(" ");
}
// Display the results
PrimesOrderedResult.Text = sb.ToString();
}
為無序結果以及清除結果的按鈕新增事件處理程序,以便您可以再次執行程式碼。
private void PrimesUnOrderedButton_Click_1(object sender, RoutedEventArgs e)
{
var nativeObject = new WinRT_CPP.Class1();
StringBuilder sb = new StringBuilder();
sb.Append("Primes found (unordered): ");
PrimesUnOrderedResult.Text = sb.ToString();
// primeFoundEvent is a user-defined event in nativeObject
// It passes the results back to this thread as they are produced
// and the event handler that we define here immediately displays them.
nativeObject.primeFoundEvent += (n) =>
{
sb.Append(n.ToString()).Append(" ");
PrimesUnOrderedResult.Text = sb.ToString();
};
// Call the async method.
var asyncResult = nativeObject.GetPrimesUnordered(2, 100000);
// Provide a handler for the Progress event that the asyncResult
// object fires at regular intervals. This handler updates the progress bar.
asyncResult.Progress += (asyncInfo, progress) =>
{
PrimesUnOrderedProgress.Value = progress;
};
}
private void Clear_Button_Click(object sender, RoutedEventArgs e)
{
PrimesOrderedProgress.Value = 0;
PrimesUnOrderedProgress.Value = 0;
PrimesUnOrderedResult.Text = "";
PrimesOrderedResult.Text = "";
Result1.Text = "";
}
執行應用程式
透過在「方案總管」中開啟專案節點的捷徑功能表,並選擇設定為啟動專案,選擇 C# 專案或 JavaScript 專案作為啟動專案。 然後按 F5 以偵錯執行,或按 Ctrl+F5 執行而不偵錯。
在物件瀏覽器中檢查元件 (選擇性)
在物件瀏覽器中,您可以檢查 .winmd 檔案中定義的所有 Windows 執行階段類型。 這包括 Platform 命名空間和預設命名空間中的類型。 但是,由於 Platform::Collections 命名空間中的類型是在標頭檔 collections.h 中定義的,而不是在 winmd 檔案中,因此它們不會出現在物件瀏覽器中。
檢查元件
在功能表列上,選擇檢視、物件瀏覽器 (Ctrl+Alt+J)。
在物件瀏覽器的左窗格中,展開 WinRT_CPP 節點,以顯示在元件上定義的類型和方法。
偵錯秘訣
為了獲得更好的偵錯體驗,請從公用 Microsoft 符號伺服器下載偵錯符號:
下載偵錯符號
在功能表列上選擇工具、選項。
在選項對話方塊中,展開偵錯並選擇符號。
選擇 Microsoft Symbol Servers,然後選擇確定按鈕。
第一次下載符號可能需要一些時間。 若要在下次按 F5 時獲得更快的效能,請指定用於快取符號的本機目錄。
當您偵錯具有元件 DLL 的 JavaScript 解決方案時,您可以將偵錯工具設定為啟用逐步執行指令碼或逐步執行元件中的原生程式碼,但這兩個不能同時執行。 若要變更設定,請在方案總管中開啟 JavaScript 專案節點的捷徑功能表,然後依序選擇屬性、偵錯、偵錯工具類型。
請務必在套件設計工具中選取適當的功能。 您可以透過開啟 Package.appxmanifest 檔案來開啟套件設計工具。 例如,如果您嘗試以程式設計方式存取「圖片」資料夾中的文件,請務必選取套件設計工具功能窗格中的圖片庫核取方塊。
如果您的 JavaScript 程式碼無法識別元件中的公用屬性或方法,請務必在 JavaScript 中使用駝峰式大小寫。 例如,必須像在 JavaScript 中一樣ComputeResult
參考 C++ 方法computeResult
。
如果從解決方案中刪除 C++ Windows 執行階段元件專案,則也必須從 JavaScript 專案中手動移除專案參考。 如果不這樣做,後續的偵錯或建置作業就無法進行。 如果需要,您可以將組件參考新增到 DLL。