> For the complete documentation index, see [llms.txt](https://docs.proudnet.com/proudnet.cn/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.proudnet.com/proudnet.cn/proudnet-note/notes/minidump.md).

# MiniDump (Error Dump System)

<mark style="color:orange;">MiniDump (Error Dump System)</mark>是ProudNet提供的一項功能，當遊戲用戶端或遊戲伺服器在網路遊戲服務過程中崩潰時，開發者可以立即收集錯誤訊息，以便追蹤原因並及時採取行動。

錯誤信息作爲<mark style="color:orange;">\*.DMP</mark>文件進行收集，開發者可以從開發工具（如VisualStudio等）中打開收集到的轉儲文件，檢查源文件在哪個行發生了衝突。

## 構建錯誤轉儲系統教程

錯誤轉儲系統構建教程包括遊戲服務器和客戶端。

### **(1)** 安裝 DbgHelp 庫

將 <mark style="color:orange;">dbghelp.dll</mark> 複製到安裝 OS 的文件夾 (<mark style="color:orange;">C:\Windows\system32</mark>) 或應用程序當前文件夾中 。\ <mark style="color:orange;">dbghelp.dll</mark> 在 <mark style="color:orange;">(ProudNet 安裝路徑)\ProudNet\Sample\Bin</mark> 中 。

### **(2)** 配置 Visual Studio 編譯選項

爲實現<mark style="color:orange;">MiniDump (Error Dump System)</mark>，請如下進行C++編譯設置。

<figure><img src="/files/hSzKTG3yKJi2q0RL5GDO" alt=""><figcaption><p>配置用於使用錯誤轉儲系統的編譯</p></figcaption></figure>

{% hint style="warning" %}
Visual Studio 2003無法如上設定 \
選擇"No"後，應在<mark style="color:orange;">Configuration Properties</mark> -> <mark style="color:orange;">C/C++</mark> -> <mark style="color:orange;">Command Line</mark> -> <mark style="color:orange;">Additional Options</mark>中添加<mark style="color:orange;">/EHa</mark>。
{% endhint %}

&#x20;

### **(3)** 轉儲服務器製作示例

負責收集轉儲客戶端發送的錯誤信息轉儲文件。

{% tabs %}
{% tab title="C++" %}

```cpp
#include "../../../include/ProudNetServer.h"
#include "../../../include/DumpServer.h"
#include "MyDumpServer.h"
class CMyDumpServer : public IDumpServerDelegate
{
    ...
    virtual void OnStartServer(CStartServerParameter &refParam) override;
    String GetDumpFilePath(HostID clientEnid, const Proud::AddrPort& clientAddr, CPnTime dumpTime) override;
    void Run();
}
void main(int argc, char* argv[])
{
    CMyDumpServer srv;
    srv.Run();
}
```

{% endtab %}
{% endtabs %}

### **(4)** 轉儲客戶端程序

轉儲客戶端負責將包含錯誤信息的轉儲文件傳輸到轉儲服務器。

進程中有衝突時， 自動臨時 \*.創建DMP轉儲文件後重新執行該過程，<mark style="color:orange;">命令參數(Command Argument)</mark>將附加<mark style="color:orange;">錯誤等級信息</mark>。

這樣作爲命令因子進入的<mark style="color:orange;">錯誤等級信息</mark>要根據嚴重程度進行分支處理,<mark style="color:orange;">MiniDumpAction\_AlarmCrash</mark>屬於嚴重錯誤,因此必須填寫將轉儲文件傳送給轉儲服務器的代碼。

### (5) 爲遊戲服務器創建轉儲客戶端

遊戲服務器一般不提供用戶UI，因此不顯示錯誤報告對話框需要編寫代碼以便將<mark style="color:orange;">\*.DMP</mark>轉儲文件直接發送到轉儲服務器。

### (6) 爲遊戲客戶端創建轉儲客戶端

遊戲客戶端一般提供用戶UI，因此顯示錯誤報告生成的需要編寫代碼向遊戲用戶確認<mark style="color:orange;">\*.DMP</mark>轉儲文件是發送到轉儲服務器還是省略後進行。

{% hint style="danger" %}
**注意**

* 不支持所有移動環境下的轉儲客戶端。
* 必須使用<mark style="color:orange;">Proud.CMiniDumpParameter</mark>初始化轉儲系統。
* 對於Unicode編程模型，可以定義main函數的寬字符版本，wmain函數的argv和envp參數應採用<mark style="color:orange;">wchar\_t\*</mark>格式。
* 對於 <mark style="color:orange;">MiniDumpAction\_AlarmCrash, MiniDumpAction\_DoNothing</mark>，必須在分支處理後呼叫 return 來終止程序，因為它可能會陷入無限迴圈。
  {% endhint %}

{% tabs %}
{% tab title="C++" %}

```cpp
#include "stdafx.h"
#include <atlpath.h>
#include "../../include/MiniDumper.h"
#include "../../include/DumpCommon.h"
 
using namespace Proud;
 
const int _MAX_PATH2 = 8192;
#define _COUNTOF(array) (sizeof(array)/sizeof(array[0]))
 
void GetDumpFilePath(LPWSTR output)
{
    WCHAR path[_MAX_PATH2];
    WCHAR drive[_MAX_PATH2];
    WCHAR dir[_MAX_PATH2];
    WCHAR fname[_MAX_PATH2];
    WCHAR ext[_MAX_PATH2];
    WCHAR module_file_name[_MAX_PATH2];
    
    GetModuleFileNameW(NULL, module_file_name, _COUNTOF(module_file_name));
    _tsplitpath_s(module_file_name, drive, _MAX_PATH2, dir, _MAX_PATH2, fname, _MAX_PATH2, ext, _MAX_PATH2);
    _tmakepath_s(path, _MAX_PATH2, drive, dir, L"", L"");
    wsprintf(output, L"%s%s.DMP", path, fname);
};
 
void AccessViolation()
{
    int* a = 0;
    *a = 1;
}
 
// void wmain(int argc, wchar_t* argv[])
int main(int argc, char* argv[])
{
    int nRetCode = 0;
    int *data = 0;
    int menu = 0;
 
    WCHAR dumpFileName[_MAX_PATH2] = { 0, };
    GetDumpFilePath(dumpFileName);
 
    CMiniDumpParameter parameter;
    parameter.m_dumpFileName = dumpFileName;
    parameter.m_miniDumpType = SmallMiniDumpType;
 
    switch (CMiniDumper::Instance().Startup(parameter))
    {
    case MiniDumpAction_AlarmCrash:
        // 오류 발생으로 새로운 프로세스에서 덤프 파일을 생성한 후, 이 값이 return이 됩니다.
        // 생성된 덤프 파일을 메일로 보내거나 에러 창을 보이는 등 유저가 덤프 파일 생성 후, 처리해야할 작업을 처리해주시면 됩니다.
 
        // A dump file is created at a new process due to error occurrence and then this value will be returned.
        // After a user create a dump file, do works that need to be done such as sending a created dump file by email or showing an error window.
 
        // 因出现错误，在新的process中生成转储文件后该值将被返还。
        // 将生成的转储文件以邮件的形式发送，或可以看到 Error对话框的用户生成转存文件后，处理应处理的事即可
 
        // エラー発生により新しいプロセスからダンプファイルを生成した後、この値がreturnされます。
        // 生成されたダンプファイルをメールで送ったり、エラーメッセージが提示されるなどユーザーがダンプファイル生成後、処理すべきの作業をしてください。
        ...
        return nRetCode;
 
    case MiniDumpAction_DoNothing:
        // 유저 호출로 새로운 프로세스에서 덤프 파일을 생성한 후, 이 값이 반환됩니다.
        // 이 경우에는 아무것도 하지 말아야합니다.
    
        // After creating a dump file at a new process by calling a user, this value will be returned.
        // In this case, you should not do anything.
 
        // 因用户呼叫，在新的process中生成转储文件后，该值将被返还。
        // 在这种情况，不要做任何事情。.
 
        // ユーザー呼び出しにより新しいプロセスからダンプファイルを生成した後、この値が返還されます。
        // この場合何もしないでください。
        ...
        return nRetCode;
 
    default:
        // MiniDumpAction_None
        // 일반적으로 앱 실행 시, 이 값이 반환됩니다.
        // 여기서는 일반적으로 처리해야할 일을 처리해주시면 됩니다.
 
        // When executing apps, this value will be returned.
        // In this case, do works that generally need to be done.
 
        // 一般运行App时，该值将被返还。
        //在这里处理一般应处理的事情即可。
 
        // 一般的にアプリ実行後、この値が返還されます。
        // ここでは一般的に処理すべきの事を処理してください。
        ...
        break;
    }
 
    while (1)
    {
        puts("MENU: 1. Access Violation('a')");
        printf("> ");
        
        menu = getchar();
 
        switch (menu)
        {
        case 'a':
            AccessViolation();
            break;
        default:
            break;
        }
    }
 
    return 0;
}
```

{% endtab %}
{% endtabs %}

## 利用錯誤轉儲系統

### &#x20;- 在轉儲系統中攔截 C 運行錯誤

錯誤轉儲系統基本上沒有檢測到STL中的out-of-the-range錯誤或pure virtual function在運行時間調用錯誤。

錯誤轉儲系統僅處理Structured Exception，因爲上述錯誤是在C runtime library中消化的。 因此，這些錯誤必須在C runtime library消化之前繞過StructuredException才能留下錯誤轉儲。<br>

下面是留下錯誤轉儲的方法。

{% tabs %}
{% tab title="C++" %}

```cpp
// Pure virtual function called" 允許錯誤轉儲系統接收錯誤 。
void myPurecallHandler(void)
{
    printf("In _purecall_handler.");
 
    int* a = 0;
    *a = 1; // 誘發崩潰。 切換到錯誤轉儲系統 。
}
 
int main()
{
    /* 將pure virtual function called錯誤處理器繞過用戶定義的函數。
    只要在程序開始的時候放進去就可以了。*/
    _set_purecall_handler(myPurecallHandler);
    ...
}
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="C++" %}

```cpp
// 將STL的"out of the range錯誤"繞過錯誤轉儲系統接收 。
// 注意!! _CrtSetReportHook 使用 ATLTRACE 可能進入設定的函數，因此必須忽略 retportType==_CRT_WARN。
 
int YourReportHook(int reportType, char *message, int *returnValue)
{
    //忽略_CRT_WARN or 0 。
    if (reprotType != _CRT_WARN)
    {
        int* a = 0;
        *a = 1; // 誘發崩潰。 切換到錯誤轉儲系統 。
    }
 
    return 1;
}
 
int main()
{
    /* 將C runtime library error的方向盤繞過用戶定義的函數。
    只要在程序開始的時候放進去就可以了。*/
    _CrtSetReportHook(YourReportHook);
 
    std::vector<int> a;
    a[1] = 0; // 測試錯誤處理器是否被繞過
 
}
```

{% endtab %}
{% endtabs %}

### - 無中斷的錯誤轉儲系統(Exception Logger)

ProudNet提供在程序發生錯誤時不關閉程序，繼續保留錯誤位置的功能。 這被稱爲<mark style="color:orange;">無中斷的錯誤轉儲系統(Exception Logger)</mark>。

一般遊戲服務器在發生衝突時,會立即留下情況並重新啓動程序,但不得已時,在不重新啓動程序的情況下,維持出現錯誤的情況,有時需要強行維持遊戲服務器的運行。

但是，如果內存狀態已經崩潰的服務器程序繼續運行，會很危險，所以要注意。

\
**無中斷錯誤轉儲系統製作前檢查事項**

> * 必須安裝DbgHelp庫（<mark style="color:orange;">dbghelp.dll</mark>）。
> * 您需要爲 Visual Studio 設置 C++ Exception 編譯選項。
> * **Include** : <mark style="color:orange;">DumpCommon.h</mark>, <mark style="color:orange;">MiniDumper.h</mark>
> * **Link** : <mark style="color:orange;">ProudNetCommon.lib</mark>

**製作示例**

在程序的起點(Main Entry Point)上, 呼叫 <mark style="color:orange;">CExceptionLogger::Instance().Init()</mark> 函數來初始化 <mark style="color:orange;">CExceptionLogger</mark> 實例。 繼承 <mark style="color:orange;">IExceptionLoggerDelegate</mark> 抽象類別並重寫 <mark style="color:orange;">GetDumpDirectory()</mark> 成員來定義轉儲檔案路徑。 如果返回空格 ("")，則轉儲檔案將保存在目前資料夾中。

{% hint style="warning" %}
在Windows XP和Windows 2003 Server或之後版本的操作系統上運行。\
如果將 <mark style="color:orange;">ATLTRACE()</mark> 或 <mark style="color:orange;">OutputDebugString()</mark> 與 <mark style="color:orange;">CExceptionLogger</mark> 類混合使用，則程序性能可能會因日誌記錄的負載而降低。
{% endhint %}

此示例源文件位於 <mark style="color:orange;"><安裝文件夾>\Sample\SimpleExceptionLogger</mark> 中。

{% hint style="danger" %} <mark style="color:orange;">SimpleExceptionLogger</mark>例程自1.7.33863版本以來不支持。
{% endhint %}

{% tabs %}
{% tab title="C++" %}

```cpp
#include "stdafx.h"
#include <atlpath.h>
#include "../../include/DumpCommon.h"
#include "../../include/MiniDumper.h"
 
using namespace Proud;
 
class CIExceptionLoggerDelegate : public IExceptionLoggerDelegate
{
    public:
    virtual String GetDumpDirectory()
    {
        return L"";
    }
};
 
CIExceptionLoggerDelegate g_loggerInfo;
 
void AccessViolation()
{
    try
    {
        int* a = 0;
 
        // 이 루틴은 크래쉬를 의도적으로 발생시킵니다.
        // This routine incurs crash on purpose.
        // 该例程将会故意造成崩溃。
        // このルーティンはクラッシュを意図的に発生させます
        *a = 1;
    }
    catch (...) // catch(...) syntax itself is the usable C++ keyword!
    {
        // 因以上try語句發生碰撞時
        // 程序沒有結束，運行點會到這裏。
        // 另一方面，錯誤日誌會被exception logger保留爲文件。
        // When crash occurs by the above try syntax,
        // the execution point moves to here without terminating the program.
        // At the same time, exception logger leaves an error log file.
    }
}
 
void main(int argc, char* argv[])
{
    int menu = 0;
    CExceptionLogger::Instance().Init(&g_loggerInfo);
 
    while (1)
    {
        puts("MENU: 1. Access Violation('a')");
        printf("> ");
 
        menu = getchar();
 
        switch (menu)
        {
        case 'a':
            AccessViolation();
            break;
        default:
            break;
        }
    }
}
```

{% endtab %}
{% endtabs %}

### - 將進程的當前狀態保留爲轉儲文件

即使沒有出現錯誤，此功能也可以將進程的當前狀態保留爲轉儲文件，如果保存的轉儲文件與創建該轉儲程序時一起生成的調試信息文件(<mark style="color:orange;">.pdb</mark>)一起打開，則可以在源級別上查看進程運行中的情況。

<figure><img src="/files/yQNfUDLhlVQdi4J1TzbM" alt=""><figcaption><p>通過此功能，即使在調試困難的環境下，您也可以將進程的當前狀態保留爲自卸。</p></figcaption></figure>

此功能即使在調試困難的環境下也能使過程保持自卸狀態。 \
調用 <mark style="color:orange;">Proud.CMiniDumper.WriteDumpFromHere</mark> 將調用時點的進程中的所有線程的調用棧保存爲轉儲文件。
