Files
StellarX-kaifa/tests/EasyXThreadProbe.cpp
T

138 lines
5.2 KiB
C++

#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;
}