# ProudNet 實用程式

## 使用收藏

STL、ATL等也已經提供收藏類(<mark style="color:orange;">std.map</mark>, <mark style="color:orange;">std.vector</mark>, <mark style="color:orange;">CFastArray</mark>, <mark style="color:orange;">CAtlArray</mark>, <mark style="color:orange;">CAtlMap</mark>等)。 但是ProudNet在STL和ATL都難以使用的情況下，或在要求高速性能的情況下提供有效的類

| 類別    | 函數               | 註釋                                                                  |
| ----- | ---------------- | ------------------------------------------------------------------- |
| 數組類   | Proud.CFastArray | 內部使用 [**Fast heap**](#fast-heap) 。                                  |
| 鏈接列表類 | Proud.CFastList  | 內部使用Fast Heap。 與CFastArray不同，該類可以與創建者、消失者和輻射分配操作員一起使用。              |
| 地圖類   | Proud.CFastMap   | (Key, Value) 使用成對的哈希算法。 與CAtlMap的使用方法非常相似，STL.map的重複序列和一些方法可以相同地使用。 |
| 設定類   | Proud.CFastSet   | 與CFastMap不同，Key是僅有的類，其餘與CFastMap相同。                                 |

<br>

## 快速內存管理器

ProudNet內置高性能內存管理器，開發者可以利用高性能內存管理器加速應用程序的加工性能。

ProudNet支持的存儲管理器大致如下。

### -  Lookaside allocator

<mark style="color:orange;">Lookaside allocator</mark>應用了通常的memory pool技術。 如果您必須經常分配/ 釋放相同大小的內存， 推薦使用 <mark style="color:orange;">Lookaside allocator</mark> 。

主要機制如下。

> * 分配新的存儲塊時，分配新的系統內存。
> * 當解鎖存儲塊時,解鎖的塊將返回至<mark style="color:orange;">lookaside allocator</mark>。
> * 重新分配存儲塊時,返還給<mark style="color:orange;">lookaside allocator</mark>的存儲塊將被再利用。

這個過程以非常快的速度運行。 比OS環境下的內存分配速度要快得多。

但也存在缺點。

> * <mark style="color:orange;">Lookaside allocator</mark>總是隻能分配大小相同的內存。
> * 分配給 <mark style="color:orange;">Lookaside allocator</mark> 的內存塊必須在 <mark style="color:orange;">Lookaside allocator</mark> 被破壞之前全部釋放。

使用<mark style="color:orange;">Lookaside allocator</mark>的方法如下。

> * 首先使用 <mark style="color:orange;">Proud.CLookasideAllocator.New</mark> 方法創建對象。\
>   創建全局對象也可以。
> * 將存儲塊分配給 <mark style="color:orange;">Proud.CLookasideAllocator.Alloc</mark> 方法。
> * 解除設置爲 <mark style="color:orange;">Proud.CLookasideAllocator.Free</mark>。 Realloc不存在。
> * 解除所有存儲塊後，破壞 <mark style="color:orange;">Proud.CLookasideAllocator</mark> 對象。

### - Fast Heap

ProudNet的<mark style="color:orange;">Fast heap</mark>雖然比<mark style="color:orange;">Lookaside allocator</mark>稍微慢一些，但是比OS環境下的存儲器分配/解除速度更快，可以分配/解除各種大小的存儲器塊。

ProudNet 的 Fastheap 的實現類是 <mark style="color:orange;">Proud.CFastHeap</mark>。 <mark style="color:orange;">Proud.CFastHeap</mark> 還可以在所有存儲塊被破壞後移除 <mark style="color:orange;">Proud.CFastHeap</mark> 對象。

\ <mark style="color:orange;">Fast heap</mark>的使用方法如下。

> * 首先以<mark style="color:orange;">Proud.CFastHeap.New</mark>方法生成<mark style="color:orange;">Fast heap</mark>對象。 創建全局對象也可以。
> * 用 <mark style="color:orange;">Proud.CFastHeap.Alloc</mark> 方法分配內存塊 。
> * 解除爲<mark style="color:orange;">Proud.CFastHeap.Free</mark>。 可用<mark style="color:orange;">CFastHeap.Realloc</mark>重新分配內存塊。
> * 解除所有存儲塊後，破壞 <mark style="color:orange;">Proud.CFastHeap</mark> 對象。

## 指定為 C++ 類別的預設分配器

在C++類中容易接受<mark style="color:orange;">Fast heap</mark>或<mark style="color:orange;">Lookaside allocator</mark>的加速的方法是覆蓋C++的<mark style="color:orange;">operator new</mark>, <mark style="color:orange;">delete</mark>方法。

這樣，在創建和破壞C++類作爲new、delete操作符時，將使用<mark style="color:orange;">Fast heap</mark>或<mark style="color:orange;">Lookaside allocator</mark>代替系統的內存heap。

\
在以下示例中，當將類實例化到 <mark style="color:orange;">operator new</mark> 或 <mark style="color:orange;">delete</mark> 時，高性能內存管理器將允許您分配/ 釋放內存。 它們各有優缺點，請適當選擇使用。

<details>

<summary><span data-gb-custom-inline data-tag="emoji" data-code="27a1">➡️</span> 例子</summary>

```cpp
class Example
{
    static CLookasideAllocatorPtr gAlloc; // 或者用CFast Heap也OK。
public:
    void* operator new(size_t size)
    {
        return gAlloc->Alloc(size);
    }
    void operator delete(void* ptr, size_t size)
    {
        gAlloc->Free(ptr);
    }
};
 
CLookasideAllocatorPtr Example::gAlloc(CLookasideAllocator::New()); // 或者用CFast Heap也OK。
```

</details>

## 智能指示器

ProudNet擁有智能指針<mark style="color:orange;">Proud.RefCount</mark>類。

智能指針是指,只要存在參照生成客體的變數,就能起到保障該客體的存在本身的作用。 而且，只有當參照該客體的變數不再存在時，該客體纔會被破壞。

它還可以解決由於開發人員創建的錯誤（dangling）而引用已被銷毀的物件的問題或未銷毀物件的問題（leak）。

智能指針變量每次複製時客體參考計數增加1，下圖中Object Instance一直存在到參考計數爲0。

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/zeNVYMxTfryOj4sXCqLq/smart_pointer.png" alt=""><figcaption><p>智能指針與對象之間的關係</p></figcaption></figure>

<details>

<summary><span data-gb-custom-inline data-tag="emoji" data-code="27a1">➡️</span> 示例代碼</summary>

```cpp
class A {...};
 
void Foo()
{
    // 創建 A 對象
    Proud::RefCount<A> a(new A);
    
    // 變量b與變量a共享。 A的參考計數爲2。
    Proud::RefCount<A> b = a;
    
    // 變量 a 已解除 。 然而，A沒有被破壞，因爲參考計數仍然是1。
    a = Proud::RefCount<A>();
    
    // 變量b已解除 。 不再有變量引用A，因此A被破壞。 (即已調用 delete)
    b = Proud::RefCount<A>();
}
```

</details>

有時候想在多處參照客體的<mark style="color:orange;">智能指示器</mark>的狀態下強制破壞客體。

例如，如果<mark style="color:orange;">智能指示器</mark>參考擁有打開文件手柄的對象，則有時需要立即破壞擁有文件手柄的對象。

但如果<mark style="color:orange;">智能指示器</mark>到處參照客體,就無法知道客體被破壞的時間,因此可能會遇到難關。 在這種情況下，可以使用<mark style="color:orange;">Dispose Pattern</mark>明確執行多處參考對象的破壞。

### - Dispose Pattern

<mark style="color:orange;">Dispose Pattern</mark>是一種程序模式,即使不知道一個以上<mark style="color:orange;">智能指示器</mark>所參照的對象被破壞的時間,也能獲得明確破壞客體的效果。

如果要將<mark style="color:orange;">Dispose Pattern</mark>運用到智能類客體上,作爲客體的成員變數,具有"自我狀態",這意味着客體是否已經被破壞,無法使用。\
如果自己的狀態是"已經破壞的狀態",那麼在參考客體時就會發生錯誤,否則就要讓其正常執行。

下面是運用<mark style="color:orange;">Dispose Pattern</mark>的例子。

{% hint style="info" %} <mark style="color:orange;">Dispose Pattern</mark> 是 \
在Java或C#等<mark style="color:orange;">智能指示器</mark>和<mark style="color:orange;">Garbage Collector</mark>的程序設計語言中也有涉及。
{% endhint %}

<details>

<summary><span data-gb-custom-inline data-tag="emoji" data-code="27a1">➡️</span> 示例代碼</summary>

```cpp
class A
{
    // 如果是true,意味着該客體處於破壞(dispose)狀態。
    bool m_disposed;
    public:
    
    A()
    {
        // 剛生成的對象處於未dispose狀態。
        m_disposed = false;
    }
 
    ~A()
    {
        Dispose();
    }
 
    void Dispose()
    {
        if(m_disposed == false)
        {
            // 進行對象破壞相關的實際執行。
            // 例如，關閉手中的文件手柄。
            ...
        
            m_disposed = true;
        }
    }
};
 
typedef Proud::RefCount<A> APtr;
 
void Foo()
{
    APtr a(new A); // 創建對象
    APtr b = a;     // 兩個智能指針變量共享一個對象
    
    a->Dispose();   // 強行破壞客體。
    
    // 現在a、b客體都處於破壞狀態。
    // 因此，不能訪問a、b所參考的對象。
    ...
}
```

</details>

## 線程實用程式

ProudNet提供了一些線程實用類。

<table><thead><tr><th width="275">類別</th><th>註釋</th></tr></thead><tbody><tr><td>Proud.Thread </td><td>線程可以很容易地生成和破壞。</td></tr><tr><td>Proud.CriticalSection</td><td>可以創建critical section。</td></tr><tr><td>Proud.CriticalSectionLock</td><td>可以lock和unlock。</td></tr></tbody></table>

## 字符串類

ProudNet使字符串類<mark style="color:orange;">Proud.String</mark>, <mark style="color:orange;">Proud.StringA</mark>使字符串像ATL或STL的字符串類一樣可以簡便地處理。

{% hint style="warning" %}
使用 .Net Framework 的程序具有名爲 System.string 的符號。\
因此.Net Framework混用時，可能需要註明System或Proud中的一個命名空間。
{% endhint %}

\
舉例如下。

```cpp
Proud::String a;  // 空字符串
a = L"123";         // 爲字符串添加值 。
puts(a);            // a本身直接提供字符串緩衝器。
a += L"abc";        // 向字符串添加另一個字符串
if(L"123abc" == a)  // 如何比較字符串中的內容
{
    a.Replace(L"123", "def");   // 字符串內容替換
}
```

### - 創建字符串功能(format)

<mark style="color:orange;">Proud.StringT</mark> 提供了創建字符串的功能， 如 <mark style="color:orange;">sprintf()</mark> 。

```cpp
Proud::String a;
a.Format(L"%d %d %s", 1, 2, L"hahaha");
// 現在a="12 hahaha"。
```

### - [Unicode-多字節相互轉換](https://docs.proudnet.com/proudnet.cn/proudnet-note/notes#undefined-1)

### -  字符串處理性能

#### Copy-on-write

```cpp
// Proud.StringT 的 copy-on-write 功能只有在需要字符串時才顯示副本。 
// 在此之前，我們會互相共享字符串數據 。
 
Proud::String a = L"abc"; // a 擁有字符串'abc'
Proud::String b = a; // b共享與a相同的字符串數據
Proud::String c = b; // 現在a、b、c都共享相同的字符串數據。
c = L"bcd"; // a、b仍然共享字符串數據"abc"，但c不再共享，單獨擁有"bcd"
b = c; // b放棄與a共享"abc"，共享c擁有的"bcd"
```

#### 字符串長度測量

<mark style="color:orange;">Proud.StringT.GetLength</mark>在調用後立即返回預先測量的字符串長度。 即與<mark style="color:orange;">strlen()</mark>不同。

{% hint style="warning" %} <mark style="color:orange;">Proud.StringT</mark> 和 int 和 float 一樣， 不使用 thread safe 。\
因此，同時從多個執行緒存取同一個字串物件是不安全的（除非所有執行緒都只是讀取）。

這一點與ATL或STL的字符串類別相同。
{% endhint %}

## Timer Queue

<mark style="color:orange;">Timer Queue</mark>是在<mark style="color:orange;">線程池</mark>中執行tick event的模塊，在Windows XP，2000年以後版本的操作系統上提供名爲Windows Timer Queue的API。

每隔一段時間運行用戶指定的函數， 該函數在<mark style="color:orange;">線程池</mark>中的一個線程中運行。 如果所有線程正在運行(running state) 函數的執行將保留到線程中出現完成歷史任務線程爲止。

在<mark style="color:orange;">Timer Queue</mark>中呼叫的用戶函數是從<mark style="color:orange;">線程池</mark>中的線程中選擇一個，如果之前正在運行的用戶函數處於未運行狀態，也有無事可做的線程(idle state)，則選擇該線程並執行用戶函數。

假設有以下工作清單。

黑箭頭爲0.1s，A、B、C、D、E是每0.1s應做的工作項目。 A、D在0.1秒內結束，B在0.1秒內結束，C、E在0.1秒內無法結束。

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/nvAijpoED6L6JRwK7Nqu/timer_queue_1.png" alt=""><figcaption><p>每隔一段時間要運行的任務項目</p></figcaption></figure>

如果是[**服務器主環**](https://docs.proudnet.com/proudnet.cn/proudnet-note/notes/main_loop#undefined-1)方法，則這些操作項目如下圖所示。 由於在一個線程中運行所有任務項目，D、E不能按時啓動。

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/pZQwrzyGN7YO8NUt1uOb/timer_queue_2.png" alt=""><figcaption><p>在一個線程中運行任務項目時</p></figcaption></figure>

然而，在<mark style="color:orange;">Timer Queue</mark>方法中，動員了另一個線程來運行D，並且E及時運行在先前完成C的線程中。

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/ZLKBYDMlLZ24b8sFoNdu/timer_queue_3.png" alt=""><figcaption><p>在計時器隊列中運行任務項目時</p></figcaption></figure>

及時啓動必要任務，必要時進一步調動線程。

<mark style="color:orange;">Timer Queue</mark>主要在服務器程序中使用，因爲用戶函數可能同時在兩個或多個線程中運行。

實現並列性，但需承受並列性所具有的危險性，使用前請判斷該功能是否必要。

{% hint style="danger" %}
如果錯誤地使用 <mark style="color:orange;">Timer Queue</mark>\
服務器過程的線程會爆炸性地增加，對性能產生不利影響。\
如果沒有必須使用 <mark style="color:orange;">Timer Queue</mark>的原因， 請在 <mark style="color:orange;">Proud.CTimerThread</mark> 或[**服務器上使用計時器環路, RMI, 事件處理**](https://docs.proudnet.com/proudnet.cn/using_pn/server_client/using_server#rmi) 。
{% endhint %}

### - 如何使用計時器隊列

您需要存取 <mark style="color:orange;">Proud.CTimerQueue</mark> 類別。 這個類別是一個singleton。

如果將調用函數和調用週期設置爲 <mark style="color:orange;">Proud::NewTimerParam</mark> 結構，並將其作爲參數添加到 <mark style="color:orange;">Proud.CTimerQueue.NewTimer</mark> 中，則會獲得 <mark style="color:orange;">Proud.CTimerQueueTimer</mark> 對象。\
然後，在破壞 <mark style="color:orange;">Proud.CTimerQueueTimer</mark> 對象之前，指定的用戶函數每隔一段時間運行一次。

<details>

<summary><span data-gb-custom-inline data-tag="emoji" data-code="27a1">➡️</span> 示例代碼</summary>

```cpp
VOID NTAPI UserFunction(void* context, BOOLEAN TimerOrWaitFired)
{
    int *pCallCount = static_cast<int *>(context);
 
    // 用戶函數
    std::cout << "UserFunction : " << ++(*pCallCount) << std::endl;
}
 
int _tmain(int argc, TCHAR* argv[])
{
    // 宣佈 NewTimerParam 結構域變量 。
    NewTimerParam p1;
 
    // 用於測試計數的變量聲明
    int callCount = 0;
 
    // 用戶函數設置 。
    p1.m_callback = UserFunction;
 
    // 設置要接收的參數 。
    p1.m_pCtx = &callCount;
 
    // 設定爲1秒後開始回電。
    p1.m_DueTime = 1000;
 
    // 設置爲0.1秒回撥。
    p1.m_period = 100;
 
    // 每隔一段時間,在線程池中每0.1秒調用一次用戶函數。
    Proud::CTimerQueueTimer* ret = Proud::CTimerQueue::GetSharedPtr()->NewTimer(p1);
 
    std::cout << "PRESS ANY KEY TO EXIT" << std::endl;
 
    // 等待用戶回撥
    _getch();
 
    // 破壞計時器客體。 破壞後用戶函數不再被調用。
    delete ret;
}
```

</details>

## 留下日誌

在開發網絡遊戲的過程中,必然需要留下各種執行記錄(日誌)的功能。 ProudNet爲此提供日誌留存功能。

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/hCR8IMMGlWVWJMl1XMOn/event_log.png" alt=""><figcaption><p>Windows也有留下事件日誌的功能。</p></figcaption></figure>

ProudNet的登錄功能以非同步方式運行，因此，在調用要留下登錄的方法後，方法立即返回。 此外，將單獨線程的實際日誌記錄在文件或數據庫中。

### - 日誌記錄到文件

<mark style="color:orange;">Proud::CLogWriter</mark> 類是允許將日誌寫入文件的類 。

<details>

<summary><span data-gb-custom-inline data-tag="emoji" data-code="27a1">➡️</span> 示例代碼</summary>

```cpp
// 生成CLogWriter。
CAutoPtr<Proud::CLogWriter> logwriter;
logwriter.Attach(Proud::CLogWriter::New(L"log.txt"));
 
// 讓我們來寫日誌。 我們提供了兩個 WriteLine 函數。
logwriter->WriteLine( Proud::TraceID::TID_System, L"是系統日誌。 );
logwriter->WriteLine( "%是第d個日誌。", 1 );
 
// 換一個新的日誌文件。 如果創建新文件失敗， 則以 Proud::Exception 例外處理 。
logwriter->SetFileName(L"log2.txt);
```

</details>

### - 日誌記錄到數據庫

<mark style="color:orange;">Proud::CDbLogWriter</mark> 類是允許將日誌寫入DB 的類 。

爲此，您必須運行 <mark style="color:orange;">Sample/DbmsSchema</mark> 文件夾中的 <mark style="color:orange;">LogTable.sql</mark>，以預先生成 LogTable 。 DBMS的建立參考樣品數據庫建立的程序。

<details>

<summary><span data-gb-custom-inline data-tag="emoji" data-code="27a1">➡️</span> 示例代碼</summary>

```cpp
// 要接收錯誤的函數 。
class CTestLogWriterDelegate : public ILogWriterDelegate
{
    virtual void OnLogWriterException(Proud::AdoException& Err) override
    {
        // ...
    }
};
 
CTestLogWriterDelegate g_dblogDelegate;
 
void main()
{
    // ...
    // CDbLog Parameter 填充值。
    Proud::CDbLogParameter dbparam;
    dbparam.m_dbmsConnectionString = L"Data Source=localhost;Database=Log-Test;Trusted_Connection=yes";
    dbparam.m_loggerName = L"LoggerName";
    dbparam.m_dbLogTableName = L"DbLog";
 
    // 生成CDbLogWriter。
    CAutoPtr<Proud::CDbLogWriter> dbLogWriter;
    dbLogWriter.Attach(Proud::CDbLogWriter::New(dbparam, &g_dblogDelegate));
    
    // 加入ProudNet不提供的新Field吧。
    // 注意！！用戶想要加入想要的字段時，必須在DBMS的Log-Test表上提前生成字段。
    // 在DBMS中創建TestField，將datatype稱爲int時，如下文所示生成CPropNode並放入Write Line即可。
    Proud::CProperty newnode;
    Proud::String TestField = L"TestField";
    Proud::CVariant TestValue = 123;
    newnode.Add(TestField, TestValue);
    dbLogWriter->WriteLine(L"日誌內容。", &newnode);
    
    // ...
}
```

</details>

## 延遲測量功能

ProudNet以StopWatch的形式提供延遲測量功能。

如果是可用的版本，則必須使用比現有延遲測量函數更準確的該功能，而不是服務器的<mark style="color:orange;">GetLastPing</mark>或<mark style="color:orange;">GetRecentPing</mark>。

### (1) 開始延遲測量

調用 <mark style="color:orange;">StartRoundTripLatencyTest</mark> 將開始要測量延遲的目標和 relay 。

<details>

<summary><span data-gb-custom-inline data-tag="emoji" data-code="27a1">➡️</span> 示例代碼</summary>

```cpp
StartRoundTripLatencyTestParameter testParameter;
testParameter.testDuration = 40 * 1000; // 指定在 RoundTripLatency Test 中何時調用 StopRoundTripLatency Test 的變量。 單位是毫秒。 默認是500ms。
testParameter.pingIntervalMs = 400; // 指定在RoundTripLatency Test途中發送PING的週期的變數。 單位是毫秒。 默認是300ms。
ErrorType startError = netClient->StartRoundTripLatencyTest(HostID_Server, testParameter);
switch(startError)
{
    case ErrorType_Ok:
        std::cout << "success" << std::endl;
        break;
    case ErrorType_InvalidHostID:
        std::cout << "HostID是自己或不是連接的Peer時" << std::endl;
    break;
}
```

</details>

### (2) 延遲測量結束

如果您想在先前指定的測試Duration之前停止測試， 請調用<mark style="color:orange;">StopRoundTripLatencyTest</mark>函數 。 如果直到testDuration結束爲止不呼叫<mark style="color:orange;">StopRoundTripLatencyTest</mark>，則將自動停止測量。

<details>

<summary><span data-gb-custom-inline data-tag="emoji" data-code="27a1">➡️</span> 示例代碼</summary>

```cpp
ErrorType stopError = StopRoundTripLatencyTest(HostID_Server);
switch(stopError)
{
    case ErrorType_Ok:
        std::cout << "success" << std::endl;
        break;
    case ErrorType_InvalidHostID:
        std::cout << "非連接Peer時" << std::endl;
        break;
}
```

</details>

### (3) 獲取延遲測量值

<details>

<summary><span data-gb-custom-inline data-tag="emoji" data-code="27a1">➡️</span> 示例代碼</summary>

```cpp
RoundTripLatencyTestResult testResult;
ErrorType getError = GetRoundTripLatency(HostID_Server, testResult);
switch(getError)
{
    case ErrorType_Ok:
        std::cout << "success" << std::endl;
        std::cout << "測量期間的乒乓平均值 : " << testResult.latencyMs 
<< ", 測量期間的乒乓標準差 : " << testResult.standardDeviationMs 
<< ", 測量期間運行的乒乓次數 : " << testResult.totalTestCount << std::endl;
        break;
    case ErrorType_InvalidHostID:
        std::cout << "非連接Peer時" << std::endl;
        break;
    case ErrorType_ValueNotExist:
        std::cout << "從未使用過乒乓時" << std::endl; // 在這種情況下，RoundTripLatencyTestResult的總TestCount爲0。
        break;
}
```

</details>

## PIDL 編譯器插件

這是 Visual Studio 的附加元件，可協助您輕鬆設定 PIDL (ProudNet IDL) 檔案的自訂建置。

### - 安裝

1\.  運行 <mark style="color:orange;"><安裝路徑>\ProudNet\util\PIDL-addon.vsix</mark>。

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/drS58r04lAQnDRqvIzXf/addon1.png" alt=""><figcaption></figcaption></figure>

####

2\. 選擇要安裝附加元件的 Visual Studio 並繼續安裝。

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/2HUEWG2Zk8hU6LiUALrX/addon2.png" alt=""><figcaption></figcaption></figure>

3\. 安裝成功完成後，會顯示如下圖所示。

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/RZReysEpqhiVhZgJpc97/addon3.png" alt=""><figcaption></figcaption></figure>

### - 刪除

可以透過<mark style="color:orange;">Tools</mark> → <mark style="color:orange;">Extensions and Updates</mark>選單將其刪除。

{% hint style="info" %}
對於 Visual Studio 2010， <mark style="color:orange;">Tools</mark> → <mark style="color:orange;">Extension Manager</mark>
{% endhint %}

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/27lIp5xdgM8f5ClzmLpR/uninstall_addon.png" alt=""><figcaption></figcaption></figure>

### - 新增PIDL檔案視窗

調用 PIDL 新增視窗。\
選擇<mark style="color:orange;">選擇項目</mark> → <mark style="color:orange;">右鍵</mark> → <mark style="color:orange;">Add new PIDL file</mark>

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/pjpRktcLdJgsZzOuPDgW/add_new_pidl_file.png" alt=""><figcaption></figcaption></figure>

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/JZ7jI9bWNX7p88XXPG0A/add_new_pidl_file2.png" alt=""><figcaption></figcaption></figure>

> 1. 輸入 PIDL 文件名 。
> 2. 顯示添加 PIDL 文件的路徑 。
> 3. 添加外部 PIDL 文件 。
> 4. 創建新的 PIDL 文件 。
> 5. 關閉 PIDL 附加窗口 。

### - 添加 PIDL 文件

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/YP14eTE3D5yqNFpIymva/add_new_pidl_file3.png" alt=""><figcaption></figcaption></figure>

> 1. 輸入要添加的 PIDL 文件名
> 2. 點擊 New PIDL

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/vDWhwlvWZBPF3L0shgfX/add_new_pidl_file_result.png" alt=""><figcaption></figcaption></figure>

如上圖所示，添加了PIDL文件，該文件默認設置Custom Build。 與默認設置不同時，可通過文件屬性更改功能進行更改。

### - PIDL 文件屬性窗口

調用 PIDL 屬性窗口 。

<mark style="color:orange;">選擇 PIDL 文件</mark> → <mark style="color:orange;">右鍵點擊</mark> → <mark style="color:orange;">Properties</mark> 選擇

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/XzbkwTNl0o8rx5qQka1S/pidl_prop.png" alt=""><figcaption></figcaption></figure>

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/wvzlxDeIwjAnKDXAC8pM/pidl_prop2.png" alt=""><figcaption></figcaption></figure>

> 1. 檢查/ 更改 PIDL 文件中的 Custom Build Tool 設置 。\
>    \ <mark style="color:orange;">PIDL Compiler Location</mark>: 設置 PIDL 編譯器位置 。\ <mark style="color:orange;">PIDL Compiler Location is Relative</mark>: 輸入路徑的絕對/相對與否值。\ <mark style="color:orange;">Output Directory (All Languages)</mark>: 設置輸出路徑 。\ <mark style="color:orange;">Output Directory is Relative (All Languages)</mark>: 輸入路徑的絕對/相對與否值。

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/MQcCoJoiuRYKdVVJTq27/pidl_prop3.png" alt=""><figcaption></figcaption></figure>

> 1. 各語言設置窗口:可按各C++、C#、Java、Unreal Script語言生成。<br>
> 2. 特定於語言的設定視窗：\
>    \ <mark style="color:orange;">Generate Code:</mark> C++ 默認 Yes， 其餘語言是 False (可根據需要配置)\ <mark style="color:orange;">C++ Implementation File Extension</mark>: 僅限C++。
