diff --git a/native.d.ts b/native.d.ts index 481ee69..2b68828 100644 --- a/native.d.ts +++ b/native.d.ts @@ -1,2 +1,3 @@ export function selectGameExecutable(): string export function checkGameIsRunning(processName: string): boolean +export function whoUseThePort(port: number): object diff --git a/native/binding.gyp b/native/binding.gyp index 970e65b..f2a91c2 100644 --- a/native/binding.gyp +++ b/native/binding.gyp @@ -8,6 +8,18 @@ "utils.h", "define.h" ], + "cflags!": [ + "-fno-exceptions" + ], + "cflags_cc!": [ + "-fno-exceptions" + ], + "defines": [ + "NAPI_DISABLE_CPP_EXCEPTIONS" + ], + "include_dirs": [ + " #include +#include #include #include -#include -using std::string, std::wstring, std::cout, std::endl; +using std::string, std::wstring, std::cout, std::to_string; +using Napi::Object, Napi::Env, Napi::Function, Napi::Value, Napi::CallbackInfo, Napi::TypeError, Napi::Error; typedef unsigned char byte; +typedef unsigned long ul; typedef unsigned long long ull; - -typedef napi_env Env; -typedef napi_value Value; -typedef napi_status Status; -typedef napi_callback Callback; -typedef napi_callback_info CallbackInfo; - -#define GetBoolean napi_get_boolean -#define ThrowError napi_throw_error -#define GetUTF8String napi_get_value_string_utf8 -#define CreateFunction napi_create_function -#define GetCallbackInfo napi_get_cb_info -#define CreateUTF8String napi_create_string_utf8 -#define SetNamedProperty napi_set_named_property diff --git a/native/main.cc b/native/main.cc index 394e7c6..c88938f 100644 --- a/native/main.cc +++ b/native/main.cc @@ -1,58 +1,96 @@ -#include #include "utils.h" #include "define.h" +#include +#pragma comment(lib,"ws2_32.lib") +#pragma comment(lib,"iphlpapi.lib") namespace native { - Value checkGameIsRunning(Env env, CallbackInfo info) { - ull argc = 0; - Value args[1]; - if (GetCallbackInfo(env, info, &argc, args, nullptr, nullptr) != napi_ok) { - return nullptr; + Value checkGameIsRunning(const CallbackInfo &info) { + Env env = info.Env(); + if (info.Length() != 1 || !info[0].IsString()) { + TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException(); + return env.Null(); } - char nameBuffer[256]; - GetUTF8String(env, args[0], (char *) &nameBuffer, sizeof(nameBuffer), nullptr); - wstring pn = StringToWString(nameBuffer); + wstring name = StringToWString(info[0].As().Utf8Value()); bool isRunning = false; PROCESSENTRY32 entry; entry.dwSize = sizeof(entry); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); if (Process32First(snapshot, &entry) == TRUE) { while (Process32Next(snapshot, &entry) == TRUE) { - if (wstring(entry.szExeFile) == pn) { + if (wstring(entry.szExeFile) == name) { isRunning = true; } } } CloseHandle(snapshot); - Value ret; - GetBoolean(env, isRunning, &ret); - return ret; + return Napi::Boolean::New(env, isRunning); } - Value selectGameExecutable(Env env, CallbackInfo args) { - Value path; - LSTATUS retcode = OpenFile(env, path); - if (retcode != ERROR_SUCCESS) { - SetLastError(retcode); - return nullptr; + Value selectGameExecutable(const CallbackInfo &info) { + Env env = info.Env(); + Napi::String path; + if (OpenFile(env, path) != ERROR_SUCCESS) { + Error::New(env, "Failed to open file: " + to_string(CommDlgExtendedError())).ThrowAsJavaScriptException(); + return env.Null(); } return path; } - Value init(Env env, Value exports) { - EnablePrivilege(L"SeDebugPrivilege"); - if (RegisterFunction(env, exports, checkGameIsRunning, "checkGameIsRunning") != napi_ok) { - ThrowError(env, nullptr, "Failed to register checkGameIsRunning"); - return nullptr; + Value whoUseThePort(const CallbackInfo &info) { + Env env = info.Env(); + if (info.Length() != 1 || !info[0].IsNumber()) { + TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException(); + return env.Null(); } - if (RegisterFunction(env, exports, selectGameExecutable, "selectGameExecutable") != napi_ok) { - ThrowError(env, nullptr, "Failed to register selectGameExecutable"); - return nullptr; + DWORD dwSize = 0; + PMIB_TCPTABLE_OWNER_PID pTcpTable = nullptr; + GetExtendedTcpTable(pTcpTable, &dwSize, TRUE,AF_INET,TCP_TABLE_OWNER_PID_ALL,0); + pTcpTable = (PMIB_TCPTABLE_OWNER_PID)new byte[dwSize]; + if(GetExtendedTcpTable(pTcpTable,&dwSize,TRUE,AF_INET,TCP_TABLE_OWNER_PID_ALL,0) != NO_ERROR) { + Error::New(env, "GetExtendedTcpTable failed").ThrowAsJavaScriptException(); + return env.Null(); } + int port = info[0].As().Int32Value(); + auto nNum = (int)pTcpTable->dwNumEntries; + DWORD pid = 0; + for(int i = 0; i < nNum; i++) { + if (htons(pTcpTable->table[i].dwLocalPort) == port) { + pid = pTcpTable->table[i].dwOwningPid; + break; + } + } + delete pTcpTable; + Value ret = env.Null(); + if (pid != 0) { + HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); + if (hProcess == nullptr) { + Error::New(env, "OpenProcess error: " + to_string(GetLastError())).ThrowAsJavaScriptException(); + return env.Null(); + } + TCHAR fnBuf[MAX_PATH]; + DWORD length = MAX_PATH; + if (QueryFullProcessImageName(hProcess, 0, fnBuf, &length) == 0) { + Error::New(env, "QueryFullProcessImageName error: " + to_string(GetLastError())).ThrowAsJavaScriptException(); + return env.Null(); + } + Object obj = Object::New(env); + obj.Set("pid", Napi::Number::New(env, pid)); + obj.Set("path", Napi::String::New(env, WStringToString(fnBuf))); + ret = obj; + } + return ret; + } + + Object init(Env env, Object exports) { + EnablePrivilege(env, L"SeDebugPrivilege"); + exports.Set("whoUseThePort", Function::New(env, whoUseThePort)); + exports.Set("checkGameIsRunning", Function::New(env, checkGameIsRunning)); + exports.Set("selectGameExecutable", Function::New(env, selectGameExecutable)); return exports; } - NAPI_MODULE(NODE_GYP_MODULE_NAME, init) + NODE_API_MODULE(addon, init) } diff --git a/native/package.json b/native/package.json index d308147..a201fec 100644 --- a/native/package.json +++ b/native/package.json @@ -8,5 +8,8 @@ "gypfile": true, "devDependencies": { "node-gyp": "^9.0.0" + }, + "dependencies": { + "node-addon-api": "^4.3.0" } } diff --git a/native/utils.cc b/native/utils.cc index 6cfd9f6..0de8688 100644 --- a/native/utils.cc +++ b/native/utils.cc @@ -23,7 +23,17 @@ string WStringToString(const wstring &src) { return result; } -LSTATUS OpenFile(Env env, Value &result, HWND parent) { +void Log(Env env, const string &msg) { + auto logFunc = env.Global().Get("console").As().Get("log").As(); + logFunc.Call({ Napi::String::New(env, msg) }); +} + +void Log(Env env, const wstring &msg) { + auto logFunc = env.Global().Get("console").As().Get("log").As(); + logFunc.Call({ Napi::String::New(env, WStringToString(msg)) }); +} + +LSTATUS OpenFile(Env env, Napi::String &result, HWND parent) { OPENFILENAME open; ZeroMemory(&open, sizeof(open)); WCHAR file[32768]; @@ -36,30 +46,17 @@ LSTATUS OpenFile(Env env, Value &result, HWND parent) { open.lpstrFilter = L"国服/国际服主程序 (YuanShen/GenshinImpact.exe)\0YuanShen.exe;GenshinImpact.exe\0"; open.lStructSize = sizeof(open); if(GetOpenFileName(&open)) { - string s = WStringToString(file); - if (CreateUTF8String(env, s.c_str(), NAPI_AUTO_LENGTH, &result) == napi_ok) { - return ERROR_SUCCESS; - } else { - return ERROR_ERRORS_ENCOUNTERED; - } + result = Napi::String::New(env, WStringToString(file)); + return ERROR_SUCCESS; } else { - return (LSTATUS)CommDlgExtendedError(); + return ERROR_ERRORS_ENCOUNTERED; } } -Status RegisterFunction(Env env, Value exports, Callback cb, const string &name) { - Value fn; - Status status = CreateFunction(env, nullptr, 0, cb, nullptr, &fn); - if (status != napi_ok) return status; - status = SetNamedProperty(env, exports, name.c_str(), fn); - if (status != napi_ok) return status; - return napi_ok; -} - -BOOL EnablePrivilege(const wstring &name) { +BOOL EnablePrivilege(Env env, const wstring &name) { HANDLE hToken; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) { - cout << "OpenProcessToken error: %lu\n" << GetLastError() << endl; + Error::New(env, "OpenProcessToken error: " + to_string(GetLastError())).ThrowAsJavaScriptException(); return FALSE; } TOKEN_PRIVILEGES tp; @@ -67,15 +64,15 @@ BOOL EnablePrivilege(const wstring &name) { tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!LookupPrivilegeValue(nullptr, name.c_str(), &tp.Privileges[0].Luid)) { - cout << "LookupPrivilegeValue error: %lu\n" << GetLastError() << endl; + Error::New(env, "LookupPrivilegeValue error: " + to_string(GetLastError())).ThrowAsJavaScriptException(); return FALSE; } if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), nullptr, nullptr)) { - cout << "AdjustTokenPrivileges error: %lu\n" << GetLastError() << endl; + Error::New(env, "AdjustTokenPrivileges error: " + to_string(GetLastError())).ThrowAsJavaScriptException(); return FALSE; } if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { - cout <<"The token does not have the specified privilege." << endl; + Error::New(env, "The token does not have the specified privilege.").ThrowAsJavaScriptException(); return FALSE; } CloseHandle(hToken); diff --git a/native/utils.h b/native/utils.h index f1a42b6..35a657e 100644 --- a/native/utils.h +++ b/native/utils.h @@ -4,9 +4,10 @@ string WStringToString(const wstring &src); wstring StringToWString(const string &src); -LSTATUS OpenFile(Env env, Value &result, HWND parent = GetConsoleWindow()); -Status RegisterFunction(Env env, Value exports, Callback cb, const string &name); -BOOL EnablePrivilege(const wstring &name); +LSTATUS OpenFile(Env env, Napi::String &result, HWND parent = GetConsoleWindow()); +BOOL EnablePrivilege(Env env, const wstring &name); +void Log(Env env, const string &msg); +void Log(Env env, const wstring &msg); #ifndef GENSHIN_EXPORT_NATIVE_UTILS_H #define GENSHIN_EXPORT_NATIVE_UTILS_H