fix(shell): support QQ 9.9.36 Windows file query aliases
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(NapCatWindowsLoaderTest LANGUAGES CXX)
|
||||
add_executable(windows-loader-test probe.cpp)
|
||||
@@ -0,0 +1,96 @@
|
||||
#include <windows.h>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
using QueryByName = BOOL(WINAPI *)(PCWSTR, FILE_INFO_BY_NAME_CLASS, PVOID, ULONG);
|
||||
|
||||
int wmain(int argumentCount, wchar_t **arguments)
|
||||
{
|
||||
if (argumentCount != 3)
|
||||
return 2;
|
||||
|
||||
const wchar_t *providers[] = {L"kernel32.dll", L"kernelbase.dll", L"api-ms-win-core-file-l2-1-4.dll"};
|
||||
HMODULE providerModules[3]{};
|
||||
QueryByName nativeQueries[3]{};
|
||||
unsigned char nativeCode[3][12]{};
|
||||
unsigned char nativeInfo[3][4][1024]{};
|
||||
BOOL nativeResults[3][4]{};
|
||||
DWORD nativeErrors[3][4]{};
|
||||
for (int providerIndex = 0; providerIndex < 3; ++providerIndex)
|
||||
{
|
||||
providerModules[providerIndex] = LoadLibraryW(providers[providerIndex]);
|
||||
auto query = reinterpret_cast<QueryByName>(GetProcAddress(providerModules[providerIndex], "GetFileInformationByName"));
|
||||
if (!query)
|
||||
return 3;
|
||||
nativeQueries[providerIndex] = query;
|
||||
std::memcpy(nativeCode[providerIndex], reinterpret_cast<void *>(query), sizeof(nativeCode[providerIndex]));
|
||||
for (int infoClass = 0; infoClass < 4; ++infoClass)
|
||||
{
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
nativeResults[providerIndex][infoClass] = query(arguments[2], static_cast<FILE_INFO_BY_NAME_CLASS>(infoClass), nativeInfo[providerIndex][infoClass], sizeof(nativeInfo[providerIndex][infoClass]));
|
||||
nativeErrors[providerIndex][infoClass] = GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
FILE_STAT_BASIC_INFORMATION expected{};
|
||||
if (!nativeQueries[0](arguments[2], FileStatBasicByNameInfo, &expected, sizeof(expected)))
|
||||
return 4;
|
||||
if (_wputenv_s(L"NAPCAT_LOAD_PATH", arguments[2]) || _wputenv_s(L"NAPCAT_PATCH_PACKAGE", arguments[2]))
|
||||
return 5;
|
||||
if (!LoadLibraryW(arguments[1]))
|
||||
{
|
||||
std::printf("LoadLibrary failed: %lu\n", GetLastError());
|
||||
return 6;
|
||||
}
|
||||
|
||||
const std::wstring missingRoot = L"C:\\napcat-loader-test-" + std::to_wstring(GetCurrentProcessId());
|
||||
const std::wstring loaderPath = missingRoot + L"\\loadNapCat.js";
|
||||
const std::wstring packagePath = missingRoot + L"\\resources\\app\\package.json";
|
||||
const std::wstring unrelatedPath = missingRoot + L"\\unrelated.txt";
|
||||
const wchar_t *redirectedPaths[] = {loaderPath.c_str(), packagePath.c_str()};
|
||||
int failures = 0;
|
||||
for (int providerIndex = 0; providerIndex < 3; ++providerIndex)
|
||||
{
|
||||
auto query = reinterpret_cast<QueryByName>(GetProcAddress(providerModules[providerIndex], "GetFileInformationByName"));
|
||||
bool preserved = std::memcmp(nativeCode[providerIndex], reinterpret_cast<void *>(nativeQueries[providerIndex]), sizeof(nativeCode[providerIndex])) == 0;
|
||||
std::wprintf(L"%ls native code preserved: %ls\n", providers[providerIndex], preserved ? L"PASS" : L"FAIL");
|
||||
failures += !preserved;
|
||||
for (int infoClass = 0; infoClass < 4; ++infoClass)
|
||||
{
|
||||
unsigned char actual[1024]{};
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
BOOL result = query(arguments[2], static_cast<FILE_INFO_BY_NAME_CLASS>(infoClass), actual, sizeof(actual));
|
||||
DWORD error = GetLastError();
|
||||
bool passed = result == nativeResults[providerIndex][infoClass] &&
|
||||
(result ? std::memcmp(actual, nativeInfo[providerIndex][infoClass], sizeof(actual)) == 0 : error == nativeErrors[providerIndex][infoClass]);
|
||||
std::wprintf(L"%ls information class %d native parity: %ls\n", providers[providerIndex], infoClass, passed ? L"PASS" : L"FAIL");
|
||||
failures += !passed;
|
||||
}
|
||||
for (const auto *redirectedPath : redirectedPaths)
|
||||
{
|
||||
FILE_STAT_BASIC_INFORMATION actual{};
|
||||
BOOL result = query(redirectedPath, FileStatBasicByNameInfo, &actual, sizeof(actual));
|
||||
bool passed = result && actual.EndOfFile.QuadPart == expected.EndOfFile.QuadPart &&
|
||||
actual.FileId.QuadPart == expected.FileId.QuadPart && actual.FileAttributes == expected.FileAttributes;
|
||||
std::wprintf(L"%ls redirect %ls: %ls\n", providers[providerIndex], redirectedPath, passed ? L"PASS" : L"FAIL");
|
||||
failures += !passed;
|
||||
}
|
||||
FILE_STAT_BASIC_INFORMATION missing{};
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
BOOL nativeResult = nativeQueries[providerIndex](unrelatedPath.c_str(), FileStatBasicByNameInfo, &missing, sizeof(missing));
|
||||
DWORD nativeError = GetLastError();
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
BOOL result = query(unrelatedPath.c_str(), FileStatBasicByNameInfo, &missing, sizeof(missing));
|
||||
bool passed = !result && result == nativeResult && GetLastError() == nativeError;
|
||||
std::wprintf(L"%ls unrelated missing file: %ls\n", providers[providerIndex], passed ? L"PASS" : L"FAIL");
|
||||
failures += !passed;
|
||||
}
|
||||
FILE_STAT_BASIC_INFORMATION direct{};
|
||||
BOOL result = GetFileInformationByName(loaderPath.c_str(), FileStatBasicByNameInfo, &direct, sizeof(direct));
|
||||
bool passed = result && direct.FileId.QuadPart == expected.FileId.QuadPart && direct.EndOfFile.QuadPart == expected.EndOfFile.QuadPart;
|
||||
std::printf("static import redirection: %s\n", passed ? "PASS" : "FAIL");
|
||||
failures += !passed;
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
@@ -9,6 +9,26 @@ on:
|
||||
permissions: write-all
|
||||
|
||||
jobs:
|
||||
windows-loader-test:
|
||||
name: Windows Loader Test
|
||||
needs: build
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: NapCat.Shell
|
||||
path: shell-dist
|
||||
- name: Verify Windows loader file queries
|
||||
shell: pwsh
|
||||
run: |
|
||||
cmake -S .github/tests/windows-loader -B "$env:RUNNER_TEMP/windows-loader-test" -A x64
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
cmake --build "$env:RUNNER_TEMP/windows-loader-test" --config Release
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
& "$env:RUNNER_TEMP/windows-loader-test/Release/windows-loader-test.exe" "$pwd/shell-dist/NapCatWinBootHook.dll" "$pwd/shell-dist/loadNapCat.js"
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
|
||||
typecheck-and-test:
|
||||
name: Type Check & Test
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -47,6 +47,7 @@ jobs:
|
||||
- name: Build NapCat.${{ matrix.target.name }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NAPCAT_VERSION: ${{ inputs.version }}
|
||||
run: |
|
||||
pnpm i
|
||||
pnpm --filter napcat-webui-frontend run build || exit 1
|
||||
@@ -192,6 +193,9 @@ jobs:
|
||||
needs: assemble-windows-node
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ inputs.version }}
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: NapCat.Shell.Windows.Node
|
||||
@@ -206,6 +210,16 @@ jobs:
|
||||
& .\node.exe -e "const wrapper = require('./wrapper.node'); if (!wrapper.NodeQQNTWrapperUtil) throw new Error('Missing NodeQQNTWrapperUtil'); console.log('wrapper.node native-load PASS'); process.exit(0);"
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
|
||||
- name: Verify Windows loader file queries
|
||||
shell: pwsh
|
||||
run: |
|
||||
cmake -S .github/tests/windows-loader -B "$env:RUNNER_TEMP/windows-loader-test" -A x64
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
cmake --build "$env:RUNNER_TEMP/windows-loader-test" --config Release
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
& "$env:RUNNER_TEMP/windows-loader-test/Release/windows-loader-test.exe" "$pwd/windows-node/napcat/NapCatWinBootHook.dll" "$pwd/windows-node/napcat/loadNapCat.js"
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
|
||||
release-napcat:
|
||||
needs: [build, assemble-windows-node, validate-windows-node]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user