// NetTunnelSvr.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <Windows.h>
#include <strsafe.h>
#include <tchar.h>

#define WG_TUNNEL_SVR_NAME TEXT("WireGuard_DLL_SVR")

typedef BOOL(WINAPI WIREGUARD_TUNNEL_SERVICE_FUNC)(_In_z_ LPCWSTR Name);
static WIREGUARD_TUNNEL_SERVICE_FUNC *WireGuardTunnelService;

static void LogToSystemEventLog(int wErrorType, int wCustumerCode, const TCHAR *szMsg) {
    HANDLE hEventSource;
    DWORD  dwEventIdentifer;

    switch (wErrorType) {
        case EVENTLOG_SUCCESS:
        case EVENTLOG_AUDIT_SUCCESS:
            dwEventIdentifer = 0x00;
            break;
        case EVENTLOG_INFORMATION_TYPE:
            dwEventIdentifer = 0x01;
            break;
        case EVENTLOG_WARNING_TYPE:
            dwEventIdentifer = 0x02;
            break;
        case EVENTLOG_ERROR_TYPE:
        case EVENTLOG_AUDIT_FAILURE:
            dwEventIdentifer = 0x03;
            break;
        default:
            dwEventIdentifer = 0;
            break;
    }
    // 移位获得Sev，前面给出的 wErrorType 为 EVENTLOG_ERROR_TYPE，对应着下图 “级别” 一列显示“错误”图标
    dwEventIdentifer <<= 30;
    dwEventIdentifer |= static_cast<WORD>(wCustumerCode);    // 前面自定义了Code，对应着下图中 事件ID 20

    hEventSource = RegisterEventSource(nullptr, WG_TUNNEL_SVR_NAME);

    if (nullptr != hEventSource) {

        LPCTSTR lpszStrings[2] = {
            WG_TUNNEL_SVR_NAME,
            szMsg};    //要写入日志的信息有两行，分别是 服务名，和前面给出的szMsg，对应着下图“以下是包含在事件中的信息”

        ReportEvent(hEventSource,        // event log handle
                    wErrorType,          // event type
                    0,                   // event category
                    dwEventIdentifer,    // event identifier
                    nullptr,             // no security identifier
                    2,                   // size of lpszStrings array
                    0,                   // no binary data
                    lpszStrings,         // array of strings
                    nullptr);            // no binary data
        DeregisterEventSource(hEventSource);
    }
}

static HMODULE InitializeTunnelLibrary() {
    const HMODULE tunnel = LoadLibraryExW(L"tunnel.dll",
                                          nullptr,
                                          LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32);
    if (!tunnel) {
        TCHAR tMsg[MAX_PATH * sizeof(TCHAR)];
        StringCbPrintf(tMsg, MAX_PATH * sizeof(TCHAR), TEXT("LoadLibraryExW Error: %d\n"), GetLastError());
        LogToSystemEventLog(EVENTLOG_ERROR_TYPE, 0x01, tMsg);
        return nullptr;
    }

#define X(Name) ((*(FARPROC *)&(Name) = GetProcAddress(tunnel, #Name)) == nullptr)
    if (X(WireGuardTunnelService))
#undef X
    {
        const DWORD LastError = GetLastError();
        FreeLibrary(tunnel);
        SetLastError(LastError);
        return nullptr;
    }
    return tunnel;
}

int _tmain(int wargc, _TCHAR *wargv[]) {
    TCHAR tMsg[MAX_PATH] = {};

    if (wargc == 3 && !wcscmp(wargv[1], L"/service")) {
        BOOL          ret;
        const HMODULE hModule = InitializeTunnelLibrary();

        if (!hModule || !WireGuardTunnelService) {
            StringCbPrintf(tMsg, MAX_PATH, TEXT("Init WireGuardTunnelService Service Error: %d\n"), GetLastError());
            LogToSystemEventLog(EVENTLOG_ERROR_TYPE, 0x01, tMsg);
            return -1;
        }

        ret = WireGuardTunnelService(wargv[2]);

        if (ret) {
            StringCbPrintf(tMsg, MAX_PATH, TEXT("Start WireGuardTunnelService Service Successed\n"));
            LogToSystemEventLog(EVENTLOG_INFORMATION_TYPE, 0x00, tMsg);
        } else {
            StringCbPrintf(tMsg, MAX_PATH, TEXT("Start WireGuardTunnelService Service failed: %d\n"), GetLastError());
            LogToSystemEventLog(EVENTLOG_ERROR_TYPE, 0x02, tMsg);
        }

        return ret;
    }
    return 0;
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧:
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件