feat(diagnostic): 阶段 0A 输入链路被动探测与线程归属复现
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
#include <easyx.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr UINT ProbeMessage = WM_APP + 0x41;
|
||||
std::atomic<DWORD> probeWndProcThread{ 0 };
|
||||
std::atomic<DWORD> keyWndProcThread{ 0 };
|
||||
std::atomic<unsigned int> keyWndProcCount{ 0 };
|
||||
HANDLE probeObserved = nullptr;
|
||||
WNDPROC previousWndProc = nullptr;
|
||||
|
||||
LRESULT CALLBACK ProbeWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (message == ProbeMessage)
|
||||
{
|
||||
probeWndProcThread.store(GetCurrentThreadId(), std::memory_order_release);
|
||||
if (probeObserved)
|
||||
SetEvent(probeObserved);
|
||||
}
|
||||
else if (message == WM_KEYDOWN || message == WM_KEYUP)
|
||||
{
|
||||
keyWndProcThread.store(GetCurrentThreadId(), std::memory_order_release);
|
||||
keyWndProcCount.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
return previousWndProc
|
||||
? CallWindowProcA(previousWndProc, hwnd, message, wParam, lParam)
|
||||
: DefWindowProcA(hwnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
bool postFixedKey(HWND hwnd)
|
||||
{
|
||||
constexpr LPARAM KeyDownLParam = 1 | (0x1E << 16);
|
||||
constexpr LPARAM KeyUpLParam = KeyDownLParam | (1LL << 30) | (1LL << 31);
|
||||
return PostMessageA(hwnd, WM_KEYDOWN, 'A', KeyDownLParam) != FALSE &&
|
||||
PostMessageA(hwnd, WM_KEYUP, 'A', KeyUpLParam) != FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const std::string outputPath = argc > 1 ? argv[1] : "easyx-thread-probe.json";
|
||||
const DWORD processId = GetCurrentProcessId();
|
||||
const DWORD mainThreadBeforeInit = GetCurrentThreadId();
|
||||
HWND hwnd = initgraph(320, 180, 1);
|
||||
const DWORD mainThreadAfterInit = GetCurrentThreadId();
|
||||
DWORD windowProcessId = 0;
|
||||
const DWORD windowOwnerThread = GetWindowThreadProcessId(hwnd, &windowProcessId);
|
||||
const bool windowUnicode = IsWindowUnicode(hwnd) != FALSE;
|
||||
ShowWindow(hwnd, SW_HIDE);
|
||||
|
||||
probeObserved = CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
previousWndProc = reinterpret_cast<WNDPROC>(
|
||||
SetWindowLongPtrA(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&ProbeWndProc)));
|
||||
const DWORD subclassError = previousWndProc ? ERROR_SUCCESS : GetLastError();
|
||||
|
||||
bool probePosted = false;
|
||||
bool probeDelivered = false;
|
||||
bool keyPosted = false;
|
||||
DWORD peekCallerThread = 0;
|
||||
unsigned int easyxKeyCount = 0;
|
||||
if (previousWndProc && probeObserved)
|
||||
{
|
||||
probePosted = PostMessageA(hwnd, ProbeMessage, 0, 0) != FALSE;
|
||||
probeDelivered = probePosted && WaitForSingleObject(probeObserved, 5000) == WAIT_OBJECT_0;
|
||||
keyPosted = postFixedKey(hwnd);
|
||||
|
||||
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5);
|
||||
while (std::chrono::steady_clock::now() < deadline && easyxKeyCount < 2)
|
||||
{
|
||||
ExMessage message{};
|
||||
peekCallerThread = GetCurrentThreadId();
|
||||
if (peekmessage(&message, EX_KEY, true))
|
||||
{
|
||||
if ((message.message == WM_KEYDOWN || message.message == WM_KEYUP) &&
|
||||
message.vkcode == 'A')
|
||||
{
|
||||
++easyxKeyCount;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const DWORD observedProbeThread = probeWndProcThread.load(std::memory_order_acquire);
|
||||
const DWORD observedKeyThread = keyWndProcThread.load(std::memory_order_acquire);
|
||||
const unsigned int observedKeyWndProcCount = keyWndProcCount.load(std::memory_order_relaxed);
|
||||
const bool separateWindowThread = windowOwnerThread != 0 && windowOwnerThread != mainThreadAfterInit;
|
||||
const bool wndProcMatchesOwner = observedProbeThread != 0 && observedProbeThread == windowOwnerThread;
|
||||
const bool keyCrossedBothBackends = keyPosted && observedKeyWndProcCount >= 2 && easyxKeyCount >= 2;
|
||||
const bool completed = hwnd != nullptr && previousWndProc != nullptr && probeDelivered &&
|
||||
wndProcMatchesOwner && keyCrossedBothBackends;
|
||||
|
||||
{
|
||||
std::ofstream output(outputPath, std::ios::trunc);
|
||||
output << "{\n"
|
||||
<< " \"schemaVersion\": 1,\n"
|
||||
<< " \"processId\": " << processId << ",\n"
|
||||
<< " \"windowProcessId\": " << windowProcessId << ",\n"
|
||||
<< " \"mainThreadBeforeInit\": " << mainThreadBeforeInit << ",\n"
|
||||
<< " \"mainThreadAfterInit\": " << mainThreadAfterInit << ",\n"
|
||||
<< " \"windowOwnerThread\": " << windowOwnerThread << ",\n"
|
||||
<< " \"probeWndProcThread\": " << observedProbeThread << ",\n"
|
||||
<< " \"keyWndProcThread\": " << observedKeyThread << ",\n"
|
||||
<< " \"peekCallerThread\": " << peekCallerThread << ",\n"
|
||||
<< " \"windowUnicode\": " << (windowUnicode ? "true" : "false") << ",\n"
|
||||
<< " \"subclassError\": " << subclassError << ",\n"
|
||||
<< " \"probePosted\": " << (probePosted ? "true" : "false") << ",\n"
|
||||
<< " \"probeDelivered\": " << (probeDelivered ? "true" : "false") << ",\n"
|
||||
<< " \"keyPosted\": " << (keyPosted ? "true" : "false") << ",\n"
|
||||
<< " \"keyWndProcCount\": " << observedKeyWndProcCount << ",\n"
|
||||
<< " \"easyxKeyCount\": " << easyxKeyCount << ",\n"
|
||||
<< " \"separateWindowThread\": " << (separateWindowThread ? "true" : "false") << ",\n"
|
||||
<< " \"wndProcMatchesOwner\": " << (wndProcMatchesOwner ? "true" : "false") << ",\n"
|
||||
<< " \"keyCrossedBothBackends\": " << (keyCrossedBothBackends ? "true" : "false") << ",\n"
|
||||
<< " \"completed\": " << (completed ? "true" : "false") << "\n"
|
||||
<< "}\n";
|
||||
}
|
||||
|
||||
if (previousWndProc)
|
||||
SetWindowLongPtrA(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(previousWndProc));
|
||||
if (probeObserved)
|
||||
CloseHandle(probeObserved);
|
||||
closegraph();
|
||||
return completed ? 0 : 2;
|
||||
}
|
||||
Reference in New Issue
Block a user