27.02.2021: win32ctrl initial commit.
This commit is contained in:
commit
4f660b1398
5 changed files with 922 additions and 0 deletions
23
CMakeLists.txt
Normal file
23
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
project(win32ctrl)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
set(SOURCES
|
||||||
|
win32ctrl.cpp
|
||||||
|
win32util.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(HEADERS
|
||||||
|
win32ctrl.h
|
||||||
|
win32util.h
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(win32ctrl SHARED ${SOURCES} ${HEADERS})
|
||||||
|
target_include_directories(win32ctrl PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
target_link_libraries(win32ctrl PRIVATE
|
||||||
|
comctl32
|
||||||
|
)
|
||||||
511
win32ctrl.cpp
Normal file
511
win32ctrl.cpp
Normal file
|
|
@ -0,0 +1,511 @@
|
||||||
|
#include "win32ctrl.h"
|
||||||
|
#include "win32util.h"
|
||||||
|
#include <commctrl.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strsafe.h>
|
||||||
|
#include <exception>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
/* AppException */
|
||||||
|
|
||||||
|
AppException::AppException(AppMonitor* app,
|
||||||
|
const std::string& text)
|
||||||
|
{
|
||||||
|
m_pApp = app;
|
||||||
|
m_Text = text;
|
||||||
|
m_dwError = GetLastError();
|
||||||
|
}
|
||||||
|
|
||||||
|
static char s_szExcBuf[256] = {0};
|
||||||
|
|
||||||
|
const char* AppException::what() const noexcept
|
||||||
|
{
|
||||||
|
StringCchPrintfA(s_szExcBuf, 256, "AppMonitor#%05d: %s",
|
||||||
|
GetApp()->GetAppProcessId(), m_Text.c_str());
|
||||||
|
return s_szExcBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AppMonitor */
|
||||||
|
|
||||||
|
void AppMonitor::Init()
|
||||||
|
{
|
||||||
|
InitCommonControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
AppMonitor::AppMonitor()
|
||||||
|
: m_ExePath(L"")
|
||||||
|
{
|
||||||
|
m_hAppProcess = INVALID_HANDLE_VALUE;
|
||||||
|
m_dwPid = 0;
|
||||||
|
m_bWow64 = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppMonitor::AppMonitor(const std::wstring& app)
|
||||||
|
: m_ExePath(app)
|
||||||
|
{
|
||||||
|
m_hAppProcess = INVALID_HANDLE_VALUE;
|
||||||
|
m_dwPid = 0;
|
||||||
|
m_bWow64 = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppMonitor::~AppMonitor()
|
||||||
|
{
|
||||||
|
if (m_hAppProcess != INVALID_HANDLE_VALUE)
|
||||||
|
CloseHandle(m_hAppProcess);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _enumapp_s {
|
||||||
|
AppMonitor* m_pMonitor;
|
||||||
|
AppMonitor::EnumFunc m_Func;
|
||||||
|
};
|
||||||
|
|
||||||
|
BOOL CALLBACK _EnumAppWindows(HWND hWnd, LPARAM lParam)
|
||||||
|
{
|
||||||
|
AppMonitor* app = (AppMonitor*)lParam;
|
||||||
|
|
||||||
|
DWORD dwPid = 0;
|
||||||
|
GetWindowThreadProcessId(app->m_Tmp.m_hWnd
|
||||||
|
? app->m_Tmp.m_hWnd : hWnd, &dwPid);
|
||||||
|
if (dwPid && dwPid != app->GetAppProcessId())
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return app->m_Tmp.m_Func(app, hWnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppMonitor::EnumAppWindows(EnumFunc func)
|
||||||
|
{
|
||||||
|
m_Tmp.m_hWnd = NULL;
|
||||||
|
m_Tmp.m_Func = func;
|
||||||
|
|
||||||
|
EnumWindows(_EnumAppWindows, (LPARAM)this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppMonitor::EnumAppControls(HWND hWnd, EnumFunc func)
|
||||||
|
{
|
||||||
|
m_Tmp.m_hWnd = hWnd;
|
||||||
|
m_Tmp.m_Func = func;
|
||||||
|
|
||||||
|
EnumChildWindows(hWnd, _EnumAppWindows, (LPARAM)this);
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND AppMonitor::FindAppWindow(const std::string& wndClass)
|
||||||
|
{
|
||||||
|
m_Tmp.m_hWnd = NULL;
|
||||||
|
m_Tmp.m_Class = wndClass;
|
||||||
|
|
||||||
|
EnumAppWindows(
|
||||||
|
[](AppMonitor* app, HWND hWnd) {
|
||||||
|
if (app->GetWindowClass(hWnd) == app->m_Tmp.m_Class)
|
||||||
|
{
|
||||||
|
app->m_Tmp.m_hWnd = hWnd;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return m_Tmp.m_hWnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppMonitor::StartApp(const std::wstring& cmdLine)
|
||||||
|
{
|
||||||
|
if (m_ExePath.empty())
|
||||||
|
throw AppException(this, "m_ExePath.empty()");
|
||||||
|
|
||||||
|
auto pszCmdLine = std::make_unique<wchar_t[]>(MAX_CMDLINE);
|
||||||
|
StringCchPrintfW(pszCmdLine.get(), MAX_CMDLINE, L"\"%s\" %s",
|
||||||
|
m_ExePath.c_str(), cmdLine.c_str());
|
||||||
|
|
||||||
|
PROCESS_INFORMATION pi;
|
||||||
|
STARTUPINFOW si;
|
||||||
|
|
||||||
|
ZeroMemory(&pi, sizeof(pi));
|
||||||
|
ZeroMemory(&si, sizeof(si));
|
||||||
|
|
||||||
|
si.cb = sizeof(si);
|
||||||
|
BOOL bCreated = CreateProcessW(m_ExePath.c_str(), pszCmdLine.get(),
|
||||||
|
NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
|
||||||
|
if (!bCreated)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_hAppProcess = pi.hProcess;
|
||||||
|
m_dwPid = pi.dwProcessId;
|
||||||
|
|
||||||
|
if (!IsWow64Process(m_hAppProcess, &m_bWow64))
|
||||||
|
m_bWow64 = FALSE;
|
||||||
|
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppMonitor::CloseApp()
|
||||||
|
{
|
||||||
|
EnumAppWindows(
|
||||||
|
[](AppMonitor* app, HWND hWnd) {
|
||||||
|
SendMessage(hWnd, WM_CLOSE, 0, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
CloseHandle(m_hAppProcess);
|
||||||
|
m_hAppProcess = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppMonitor::Terminate()
|
||||||
|
{
|
||||||
|
TerminateProcess(m_hAppProcess, 0);
|
||||||
|
CloseHandle(m_hAppProcess);
|
||||||
|
m_hAppProcess = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppMonitor::IsAppRunning() const
|
||||||
|
{
|
||||||
|
DWORD dwWait = WaitForSingleObject(m_hAppProcess, 0);
|
||||||
|
if (dwWait != WAIT_OBJECT_0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppMonitor::IsWow64() const
|
||||||
|
{
|
||||||
|
return !!m_bWow64;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppMonitor::WaitAppIdle(DWORD dwInterval)
|
||||||
|
{
|
||||||
|
return WaitForInputIdle(GetAppProcess(), dwInterval) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppMonitor::MonitorSetup()
|
||||||
|
{
|
||||||
|
EnumAppWindows(
|
||||||
|
[](AppMonitor* app, HWND hWnd) {
|
||||||
|
app->OnAppWindow(hWnd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppMonitor::OnAppWindow(HWND hWnd)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppMonitor::SetExePath(const std::wstring& exePath)
|
||||||
|
{
|
||||||
|
m_ExePath = exePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AppMonitor::GetWindowClass(HWND hWnd)
|
||||||
|
{
|
||||||
|
char szClass[64] = {0};
|
||||||
|
GetClassNameA(hWnd, szClass, 64);
|
||||||
|
return std::string(szClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND AppMonitor::GetChild(HWND hWnd, const std::string& wndClass,
|
||||||
|
const std::wstring& wndText)
|
||||||
|
{
|
||||||
|
return FindWindowExW(hWnd, NULL, TextToWchar(wndClass).c_str(),
|
||||||
|
wndText.empty() ? NULL : wndText.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
AppMem AppMonitor::MemAlloc(unsigned uLen)
|
||||||
|
{
|
||||||
|
void* pThisMem;
|
||||||
|
void* pAppMem;
|
||||||
|
|
||||||
|
pThisMem = (char*)malloc(uLen);
|
||||||
|
if (!pThisMem)
|
||||||
|
throw AppException(this, "!malloc");
|
||||||
|
memset(pThisMem, '\0', uLen);
|
||||||
|
pAppMem = VirtualAllocEx(m_hAppProcess,
|
||||||
|
NULL, uLen, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
if (!pAppMem)
|
||||||
|
{
|
||||||
|
free(pThisMem);
|
||||||
|
throw AppException(this, "!VirtualAllocEx");
|
||||||
|
}
|
||||||
|
else if (IsWow64() && (uintptr_t)pAppMem > 0xFFFFFFFF)
|
||||||
|
{
|
||||||
|
free(pThisMem);
|
||||||
|
throw AppException(this, "WOW64 VirtualAllocEx >4GB addr");
|
||||||
|
}
|
||||||
|
|
||||||
|
return AppMem(pThisMem, pAppMem, uLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppMonitor::MemFree(AppMem& mem)
|
||||||
|
{
|
||||||
|
free(mem.This());
|
||||||
|
VirtualFreeEx(m_hAppProcess, mem.App(), 0, MEM_RELEASE);
|
||||||
|
mem = AppMem();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppMonitor::MemWriteApp(const AppMem& mem)
|
||||||
|
{
|
||||||
|
SIZE_T szTmp = mem.Size();
|
||||||
|
if(!WriteProcessMemory(m_hAppProcess, mem.App(),
|
||||||
|
mem.This(), mem.Size(), &szTmp))
|
||||||
|
{
|
||||||
|
throw AppException(this, "!WriteProcessMemory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppMonitor::MemReadApp(const AppMem& mem)
|
||||||
|
{
|
||||||
|
SIZE_T szTmp = mem.Size();
|
||||||
|
if(!ReadProcessMemory(m_hAppProcess, mem.App(),
|
||||||
|
mem.This(), mem.Size(), &szTmp))
|
||||||
|
{
|
||||||
|
throw AppException(this, "!ReadProcessMemory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppMonitor::AppMessage(HWND hWnd, UINT uMsg,
|
||||||
|
WPARAM wParam, LPARAM lParam,
|
||||||
|
DWORD_PTR* pResult,
|
||||||
|
unsigned uTimeOut)
|
||||||
|
{
|
||||||
|
DWORD_PTR dwResult = 0;
|
||||||
|
LRESULT lResult;
|
||||||
|
|
||||||
|
if (IsWindowUnicode(hWnd))
|
||||||
|
{
|
||||||
|
lResult = SendMessageTimeoutW(
|
||||||
|
hWnd, uMsg, wParam, lParam,
|
||||||
|
SMTO_ABORTIFHUNG, uTimeOut,
|
||||||
|
&dwResult);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lResult = SendMessageTimeoutA(
|
||||||
|
hWnd, uMsg, wParam, lParam,
|
||||||
|
SMTO_ABORTIFHUNG, uTimeOut,
|
||||||
|
&dwResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pResult) *pResult = dwResult;
|
||||||
|
|
||||||
|
if (lResult) // ок
|
||||||
|
return true;
|
||||||
|
else if (GetLastError() == ERROR_TIMEOUT) // таймаут
|
||||||
|
throw AppTimeOut(this, uMsg);
|
||||||
|
else // ошибка
|
||||||
|
{
|
||||||
|
throw AppException(this, "!SendMessageTimeoutW");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppMonitor::AppPostMessage(HWND hWnd, UINT uMsg,
|
||||||
|
WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
if (!hWnd)
|
||||||
|
throw AppException(this, "!hWnd");
|
||||||
|
if (!PostMessageW(hWnd, uMsg, wParam, lParam))
|
||||||
|
throw AppException(this, "!PostMessageW");
|
||||||
|
}
|
||||||
|
|
||||||
|
AppMem AppMonitor::NewString(HWND hWnd, DWORD dwChars)
|
||||||
|
{
|
||||||
|
if (!dwChars) dwChars = 256;
|
||||||
|
return MemAlloc(IsWindowUnicode(hWnd)
|
||||||
|
? sizeof(wchar_t) * dwChars
|
||||||
|
: sizeof(char) * dwChars
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring AppMonitor::ReadString(HWND hWnd, const AppMem& str)
|
||||||
|
{
|
||||||
|
std::wstring ret;
|
||||||
|
MemReadApp(str);
|
||||||
|
|
||||||
|
if (IsWindowUnicode(hWnd))
|
||||||
|
{
|
||||||
|
wchar_t* pszText = (wchar_t*)str.This();
|
||||||
|
pszText[(uint64_t)(str.Size()-1)/sizeof(wchar_t)] = L'\0';
|
||||||
|
ret = std::wstring(pszText);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char* pszText = (char*)str.This();
|
||||||
|
pszText[(uint64_t)(str.Size()-1)/sizeof(char)] = '\0';
|
||||||
|
ret = AnsiToWchar(pszText);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring AppMonitor::GetWindowTextStr(HWND hWnd)
|
||||||
|
{
|
||||||
|
auto pszText = std::make_unique<wchar_t[]>(MAX_WM_TEXT);
|
||||||
|
|
||||||
|
SetLastError(0);
|
||||||
|
int iLen = GetWindowTextW(hWnd, pszText.get(), MAX_WM_TEXT);
|
||||||
|
if (iLen < 0)
|
||||||
|
throw AppException(this, "!GetWindowTextW");
|
||||||
|
|
||||||
|
pszText[iLen] = L'\0';
|
||||||
|
return std::wstring(pszText.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring AppMonitor::GetControlTextStr(HWND hWnd)
|
||||||
|
{
|
||||||
|
DWORD_PTR dwLength = 0, dwRead = 0;
|
||||||
|
AppMessage(hWnd, WM_GETTEXTLENGTH, 0, 0,
|
||||||
|
&dwLength);
|
||||||
|
if (dwLength <= 0)
|
||||||
|
return L"";
|
||||||
|
|
||||||
|
std::wstring ret;
|
||||||
|
if (IsWindowUnicode(hWnd))
|
||||||
|
{
|
||||||
|
auto pszText = std::make_unique<wchar_t[]>(dwLength * 2);
|
||||||
|
AppMessage(hWnd, WM_GETTEXT,
|
||||||
|
(WPARAM)dwLength+1, (LPARAM)pszText.get(),
|
||||||
|
&dwRead);
|
||||||
|
//dwRead = (DWORD_PTR)SendMessage(hWnd, WM_GETTEXT,
|
||||||
|
// (WPARAM)dwLength+1, (LPARAM)pszText.get());
|
||||||
|
pszText[dwRead] = L'\0';
|
||||||
|
ret = std::wstring(pszText.get());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto pszText = std::make_unique<char[]>(dwLength * 2);
|
||||||
|
AppMessage(hWnd, WM_GETTEXT,
|
||||||
|
(WPARAM)dwLength+1, (LPARAM)pszText.get(),
|
||||||
|
&dwRead);
|
||||||
|
//dwRead = (DWORD_PTR)SendMessage(hWnd, WM_GETTEXT,
|
||||||
|
// (WPARAM)dwLength+1, (LPARAM)pszText.get());
|
||||||
|
pszText[dwRead] = '\0';
|
||||||
|
ret = AnsiToWchar(std::string(pszText.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD_PTR AppMonitor::TV_GetNextItem(HWND hTree, DWORD dwFlags,
|
||||||
|
DWORD_PTR dwItem)
|
||||||
|
{
|
||||||
|
DWORD_PTR dwRetItem = 0;
|
||||||
|
AppMessage(hTree, TVM_GETNEXTITEM,
|
||||||
|
(WPARAM)dwFlags, dwItem, &dwRetItem);
|
||||||
|
return dwRetItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t mask;
|
||||||
|
uint32_t hItem;
|
||||||
|
uint32_t state;
|
||||||
|
uint32_t stateMask;
|
||||||
|
uint32_t dwText;
|
||||||
|
int32_t cchTextMax;
|
||||||
|
int32_t iImage;
|
||||||
|
int32_t iSelectedImage;
|
||||||
|
int32_t cChildren;
|
||||||
|
uint32_t lParam;
|
||||||
|
} tvitem32_t;
|
||||||
|
|
||||||
|
std::tuple<std::wstring,int>
|
||||||
|
AppMonitor::TV_GetItem32(HWND hTree, DWORD_PTR dwItem)
|
||||||
|
{
|
||||||
|
DWORD_PTR dwRet;
|
||||||
|
AppMem item = MemAlloc(sizeof(tvitem32_t));
|
||||||
|
AppMem str = NewString(hTree, MAX_TV_TEXT);
|
||||||
|
|
||||||
|
std::wstring text = L"";
|
||||||
|
int icon = 0;
|
||||||
|
|
||||||
|
tvitem32_t* tvItem = item.As<tvitem32_t>();
|
||||||
|
tvItem->mask = TVIF_HANDLE | TVIF_TEXT | TVIF_IMAGE;
|
||||||
|
tvItem->hItem = (uint32_t)dwItem;
|
||||||
|
tvItem->cchTextMax = MAX_TV_TEXT;
|
||||||
|
tvItem->dwText = (uint32_t)((uintptr_t)str.App());
|
||||||
|
|
||||||
|
MemWriteApp(item);
|
||||||
|
AppMessage(hTree, IsWindowUnicode(hTree)
|
||||||
|
? TVM_GETITEMW : TVM_GETITEMA,
|
||||||
|
0, (LPARAM)item.App(), &dwRet
|
||||||
|
);
|
||||||
|
if ((BOOL)dwRet)
|
||||||
|
{
|
||||||
|
MemReadApp(item);
|
||||||
|
|
||||||
|
text = ReadString(hTree, str);
|
||||||
|
icon = tvItem->iImage;
|
||||||
|
|
||||||
|
MemFree(item);
|
||||||
|
MemFree(str);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MemFree(item);
|
||||||
|
MemFree(str);
|
||||||
|
throw AppException(this, "TVM_GETITEM !dwRet");
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_tuple(text, icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t mask;
|
||||||
|
uint64_t hItem;
|
||||||
|
uint32_t state;
|
||||||
|
uint32_t stateMask;
|
||||||
|
uint64_t dwText;
|
||||||
|
int32_t cchTextMax;
|
||||||
|
int32_t iImage;
|
||||||
|
int32_t iSelectedImage;
|
||||||
|
int32_t cChildren;
|
||||||
|
uint64_t lParam;
|
||||||
|
} tvitem64_t;
|
||||||
|
|
||||||
|
std::tuple<std::wstring,int>
|
||||||
|
AppMonitor::TV_GetItem64(HWND hTree, DWORD_PTR dwItem)
|
||||||
|
{
|
||||||
|
DWORD_PTR dwRet;
|
||||||
|
AppMem item = MemAlloc(sizeof(tvitem64_t));
|
||||||
|
AppMem str = NewString(hTree, MAX_TV_TEXT);
|
||||||
|
|
||||||
|
std::wstring text = L"";
|
||||||
|
int icon = 0;
|
||||||
|
|
||||||
|
tvitem64_t* tvItem = item.As<tvitem64_t>();
|
||||||
|
tvItem->mask = TVIF_HANDLE | TVIF_TEXT | TVIF_IMAGE;
|
||||||
|
tvItem->hItem = (uint64_t)dwItem;
|
||||||
|
tvItem->cchTextMax = MAX_TV_TEXT;
|
||||||
|
tvItem->dwText = (uint64_t)((uintptr_t)str.App());
|
||||||
|
|
||||||
|
MemWriteApp(item);
|
||||||
|
AppMessage(hTree, IsWindowUnicode(hTree)
|
||||||
|
? TVM_GETITEMW : TVM_GETITEMA,
|
||||||
|
0, (LPARAM)item.App(), &dwRet
|
||||||
|
);
|
||||||
|
if ((BOOL)dwRet)
|
||||||
|
{
|
||||||
|
MemReadApp(item);
|
||||||
|
|
||||||
|
text = ReadString(hTree, str);
|
||||||
|
icon = tvItem->iImage;
|
||||||
|
|
||||||
|
MemFree(item);
|
||||||
|
MemFree(str);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MemFree(item);
|
||||||
|
MemFree(str);
|
||||||
|
throw AppException(this, "TVM_GETITEM !dwRet");
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_tuple(text, icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<std::wstring,int>
|
||||||
|
AppMonitor::TV_GetItem(HWND hTree, DWORD_PTR dwItem)
|
||||||
|
{
|
||||||
|
if (IsWow64()) return TV_GetItem32(hTree, dwItem);
|
||||||
|
else return TV_GetItem64(hTree, dwItem);
|
||||||
|
}
|
||||||
170
win32ctrl.h
Normal file
170
win32ctrl.h
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
#ifndef __WIN32CTRL_H
|
||||||
|
#define __WIN32CTRL_H
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <exception>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
class AppMem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AppMem(void* pThisMem = NULL, void* pAppMem = NULL, size_t uLen = 0)
|
||||||
|
: m_pThisMem(pThisMem), m_pAppMem(pAppMem), m_uMemLen(uLen)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Zero()
|
||||||
|
{
|
||||||
|
if (m_pThisMem)
|
||||||
|
ZeroMemory(m_pThisMem, m_uMemLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void* This() const { return m_pThisMem; }
|
||||||
|
inline void* App() const { return m_pAppMem; }
|
||||||
|
inline unsigned Size() const { return m_uMemLen; }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T* As()
|
||||||
|
{
|
||||||
|
return (T*)This();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void* m_pThisMem;
|
||||||
|
void* m_pAppMem;
|
||||||
|
unsigned m_uMemLen;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AppMonitor;
|
||||||
|
|
||||||
|
class AppException : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AppException(AppMonitor* app,
|
||||||
|
const std::string& text);
|
||||||
|
|
||||||
|
virtual AppMonitor* GetApp() const
|
||||||
|
{
|
||||||
|
return m_pApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual DWORD GetError() const
|
||||||
|
{
|
||||||
|
return m_dwError;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const char* what() const noexcept;
|
||||||
|
private:
|
||||||
|
AppMonitor* m_pApp;
|
||||||
|
std::string m_Text;
|
||||||
|
DWORD m_dwError;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AppTimeOut : public AppException
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AppTimeOut(AppMonitor* app, UINT uMsg)
|
||||||
|
: AppException(app, "[AppTimeOut]"),
|
||||||
|
m_uMsg(uMsg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual UINT GetMessage() const
|
||||||
|
{
|
||||||
|
return m_uMsg;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
UINT m_uMsg;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX_CMDLINE 1024
|
||||||
|
#define APP_MSG_TIMEOUT 60*1000
|
||||||
|
#define MAX_WM_TEXT 4096
|
||||||
|
#define MAX_TV_TEXT 256
|
||||||
|
|
||||||
|
class AppMonitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void Init();
|
||||||
|
|
||||||
|
AppMonitor();
|
||||||
|
AppMonitor(const std::wstring& exePath);
|
||||||
|
virtual ~AppMonitor();
|
||||||
|
|
||||||
|
typedef std::function<bool(AppMonitor*, HWND)> EnumFunc;
|
||||||
|
|
||||||
|
friend BOOL CALLBACK _EnumAppWindows(HWND, LPARAM);
|
||||||
|
void EnumAppWindows(EnumFunc func);
|
||||||
|
void EnumAppControls(HWND hWnd, EnumFunc func);
|
||||||
|
HWND FindAppWindow(const std::string& wndClass);
|
||||||
|
|
||||||
|
virtual HANDLE GetAppProcess() const
|
||||||
|
{
|
||||||
|
return m_hAppProcess;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual DWORD GetAppProcessId() const
|
||||||
|
{
|
||||||
|
return m_dwPid;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool StartApp(const std::wstring& cmdLine);
|
||||||
|
virtual void CloseApp();
|
||||||
|
virtual void Terminate();
|
||||||
|
virtual bool IsAppRunning() const;
|
||||||
|
virtual bool IsWow64() const;
|
||||||
|
virtual bool WaitAppIdle(DWORD dwInterval = INFINITE);
|
||||||
|
|
||||||
|
virtual void MonitorSetup();
|
||||||
|
protected:
|
||||||
|
virtual void OnAppWindow(HWND hWnd);
|
||||||
|
void SetExePath(const std::wstring& exe);
|
||||||
|
public:
|
||||||
|
virtual std::string GetWindowClass(HWND hWnd);
|
||||||
|
virtual HWND GetChild(HWND hWnd, const std::string& wndClass,
|
||||||
|
const std::wstring& wndText = L"");
|
||||||
|
|
||||||
|
virtual AppMem MemAlloc(unsigned uLen);
|
||||||
|
virtual void MemFree(AppMem& mem);
|
||||||
|
virtual void MemWriteApp(const AppMem& mem);
|
||||||
|
virtual void MemReadApp(const AppMem& mem);
|
||||||
|
|
||||||
|
virtual bool AppMessage(HWND hWnd, UINT uMsg,
|
||||||
|
WPARAM wParam, LPARAM lParam,
|
||||||
|
DWORD_PTR* pResult = NULL,
|
||||||
|
unsigned uTimeOut = APP_MSG_TIMEOUT);
|
||||||
|
virtual void AppPostMessage(HWND hWnd, UINT uMsg,
|
||||||
|
WPARAM wParam, LPARAM lParam);
|
||||||
|
|
||||||
|
virtual AppMem NewString(HWND hWnd, DWORD dwChars);
|
||||||
|
virtual std::wstring ReadString(HWND hWnd, const AppMem& str);
|
||||||
|
|
||||||
|
virtual std::wstring GetWindowTextStr(HWND hWnd);
|
||||||
|
virtual std::wstring GetControlTextStr(HWND hWnd);
|
||||||
|
|
||||||
|
virtual DWORD_PTR TV_GetNextItem(HWND hTree, DWORD dwFlags,
|
||||||
|
DWORD_PTR dwItem = 0);
|
||||||
|
private:
|
||||||
|
std::tuple<std::wstring,int>
|
||||||
|
TV_GetItem32(HWND hTree, DWORD_PTR dwItem);
|
||||||
|
std::tuple<std::wstring,int>
|
||||||
|
TV_GetItem64(HWND hTree, DWORD_PTR dwItem);
|
||||||
|
public:
|
||||||
|
virtual std::tuple<std::wstring,int>
|
||||||
|
TV_GetItem(HWND hTree, DWORD_PTR dwItem);
|
||||||
|
private:
|
||||||
|
std::wstring m_ExePath;
|
||||||
|
|
||||||
|
HANDLE m_hAppProcess;
|
||||||
|
DWORD m_dwPid;
|
||||||
|
BOOL m_bWow64;
|
||||||
|
|
||||||
|
struct _app_tmp_s {
|
||||||
|
EnumFunc m_Func = NULL;
|
||||||
|
std::string m_Class;
|
||||||
|
HWND m_hWnd = NULL;
|
||||||
|
} m_Tmp;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
190
win32util.cpp
Normal file
190
win32util.cpp
Normal file
|
|
@ -0,0 +1,190 @@
|
||||||
|
#include "win32util.h"
|
||||||
|
#include <io.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
std::wstring TextToWchar(const std::string& text)
|
||||||
|
{
|
||||||
|
size_t uWcharLen = MultiByteToWideChar(CP_UTF8, 0,
|
||||||
|
text.c_str(), text.size(), NULL, 0);
|
||||||
|
auto pszWchar = std::make_unique<wchar_t[]>(uWcharLen+1);
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, text.c_str(), text.size(),
|
||||||
|
pszWchar.get(), uWcharLen);
|
||||||
|
pszWchar[uWcharLen] = L'\0';
|
||||||
|
return std::wstring(pszWchar.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string WcharToText(const std::wstring& text)
|
||||||
|
{
|
||||||
|
size_t uMbsLen = WideCharToMultiByte(CP_UTF8, 0,
|
||||||
|
text.c_str(), text.size(), NULL, 0, NULL, NULL);
|
||||||
|
auto pszChar = std::make_unique<char[]>(uMbsLen +1);
|
||||||
|
WideCharToMultiByte(CP_UTF8, 0, text.c_str(), text.size(),
|
||||||
|
pszChar.get(), uMbsLen, NULL, NULL);
|
||||||
|
pszChar[uMbsLen] = '\0';
|
||||||
|
return std::string(pszChar.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring AnsiToWchar(const std::string& text, int cp)
|
||||||
|
{
|
||||||
|
size_t uWcharLen = MultiByteToWideChar(cp, 0,
|
||||||
|
text.c_str(), text.size(), NULL, 0);
|
||||||
|
auto pszWchar = std::make_unique<wchar_t[]>(uWcharLen +1);
|
||||||
|
MultiByteToWideChar(cp, 0, text.c_str(), text.size(),
|
||||||
|
pszWchar.get(), uWcharLen);
|
||||||
|
pszWchar[uWcharLen] = L'\0';
|
||||||
|
return std::wstring(pszWchar.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string WcharToAnsi(const std::wstring& text, int cp)
|
||||||
|
{
|
||||||
|
size_t uMbsLen = WideCharToMultiByte(cp, 0,
|
||||||
|
text.c_str(), text.size(), NULL, 0, NULL, NULL);
|
||||||
|
auto pszChar = std::make_unique<char[]>(uMbsLen +1);
|
||||||
|
WideCharToMultiByte(cp, 0, text.c_str(), text.size(),
|
||||||
|
pszChar.get(), uMbsLen, NULL, NULL);
|
||||||
|
pszChar[uMbsLen] = '\0';
|
||||||
|
return std::string(pszChar.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _findwnd_s {
|
||||||
|
DWORD m_dwPid;
|
||||||
|
const char* m_pClass;
|
||||||
|
HWND m_hWnd;
|
||||||
|
DWORD m_dwTid;
|
||||||
|
};
|
||||||
|
|
||||||
|
static BOOL CALLBACK _CheckWindow(HWND hWnd, LPARAM lParam)
|
||||||
|
{
|
||||||
|
struct _findwnd_s* wnd = (struct _findwnd_s*)lParam;
|
||||||
|
char szClassName[64] = {0};
|
||||||
|
|
||||||
|
DWORD dwPid;
|
||||||
|
DWORD dwTid = GetWindowThreadProcessId(hWnd, &dwPid);
|
||||||
|
if (dwPid != wnd->m_dwPid)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
GetClassNameA(hWnd, szClassName, 64);
|
||||||
|
if (!strcmp(szClassName, wnd->m_pClass))
|
||||||
|
{
|
||||||
|
wnd->m_hWnd = hWnd;
|
||||||
|
wnd->m_dwTid = dwTid;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FindProcessWindow(DWORD dwPid, const char* pClass,
|
||||||
|
HWND& hWnd, DWORD& uiThread)
|
||||||
|
{
|
||||||
|
struct _findwnd_s wnd = { 0 };
|
||||||
|
|
||||||
|
wnd.m_dwPid = dwPid;
|
||||||
|
wnd.m_pClass = pClass;
|
||||||
|
wnd.m_hWnd = NULL;
|
||||||
|
|
||||||
|
EnumWindows(_CheckWindow, (LPARAM)&wnd);
|
||||||
|
if (wnd.m_hWnd)
|
||||||
|
{
|
||||||
|
hWnd = wnd.m_hWnd;
|
||||||
|
uiThread = wnd.m_dwTid;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReconnectIO(bool OpenNewConsole)
|
||||||
|
{
|
||||||
|
int hConHandle;
|
||||||
|
intptr_t iStdHandle;
|
||||||
|
FILE *fp;
|
||||||
|
bool MadeConsole;
|
||||||
|
|
||||||
|
MadeConsole=false;
|
||||||
|
if(!AttachConsole(ATTACH_PARENT_PROCESS))
|
||||||
|
{
|
||||||
|
if(!OpenNewConsole)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
MadeConsole=true;
|
||||||
|
if(!AllocConsole())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
iStdHandle = (intptr_t)GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
hConHandle = _open_osfhandle(iStdHandle, _O_TEXT);
|
||||||
|
fp = _fdopen( hConHandle, "w" );
|
||||||
|
*stdout = *fp;
|
||||||
|
setvbuf( stdout, NULL, _IONBF, 0 );
|
||||||
|
|
||||||
|
iStdHandle = (intptr_t)GetStdHandle(STD_INPUT_HANDLE);
|
||||||
|
hConHandle = _open_osfhandle(iStdHandle, _O_TEXT);
|
||||||
|
fp = _fdopen( hConHandle, "r" );
|
||||||
|
*stdin = *fp;
|
||||||
|
setvbuf( stdin, NULL, _IONBF, 0 );
|
||||||
|
|
||||||
|
iStdHandle = (intptr_t)GetStdHandle(STD_ERROR_HANDLE);
|
||||||
|
hConHandle = _open_osfhandle(iStdHandle, _O_TEXT);
|
||||||
|
fp = _fdopen( hConHandle, "w" );
|
||||||
|
*stderr = *fp;
|
||||||
|
setvbuf( stderr, NULL, _IONBF, 0 );
|
||||||
|
|
||||||
|
std::ios_base::sync_with_stdio();
|
||||||
|
|
||||||
|
return MadeConsole;
|
||||||
|
}
|
||||||
|
|
||||||
|
listdir ListDirectory(const std::wstring& path)
|
||||||
|
{
|
||||||
|
auto pFind = std::make_unique<WIN32_FIND_DATAW>();
|
||||||
|
std::wstring findPath = path + L"\\*";
|
||||||
|
|
||||||
|
std::vector<std::wstring> files, dirs;
|
||||||
|
HANDLE hFind = FindFirstFileW(findPath.c_str(), pFind.get());
|
||||||
|
if (hFind)
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
if (!wcscmp(pFind->cFileName, L"."))
|
||||||
|
continue;
|
||||||
|
if (!wcscmp(pFind->cFileName, L".."))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (pFind->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||||
|
dirs.emplace_back(pFind->cFileName);
|
||||||
|
else files.emplace_back(pFind->cFileName);
|
||||||
|
} while (FindNextFileW(hFind, pFind.get()));
|
||||||
|
FindClose(hFind);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_tuple(files, dirs);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t GetDirectorySize(const std::wstring& path)
|
||||||
|
{
|
||||||
|
uint64_t uDirSize = 0;
|
||||||
|
auto [files, dirs] = ListDirectory(path);
|
||||||
|
|
||||||
|
for (auto& file : files)
|
||||||
|
{
|
||||||
|
LARGE_INTEGER fileSize = {0};
|
||||||
|
HANDLE hFile = CreateFileW((path + L"\\" + file).c_str(),
|
||||||
|
GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
if (hFile == INVALID_HANDLE_VALUE)
|
||||||
|
continue;
|
||||||
|
if (GetFileSizeEx(hFile, &fileSize))
|
||||||
|
uDirSize += fileSize.QuadPart;
|
||||||
|
CloseHandle(hFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& dir : dirs)
|
||||||
|
uDirSize += GetDirectorySize(path + L"\\" + dir);
|
||||||
|
|
||||||
|
return uDirSize;
|
||||||
|
}
|
||||||
28
win32util.h
Normal file
28
win32util.h
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef __WIN32UTIL_H
|
||||||
|
#define __WIN32UTIL_H
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
std::wstring TextToWchar(const std::string& text);
|
||||||
|
std::string WcharToText(const std::wstring& text);
|
||||||
|
std::wstring AnsiToWchar(const std::string& text, int cp = CP_THREAD_ACP);
|
||||||
|
std::string WcharToAnsi(const std::wstring& text, int cp = CP_THREAD_ACP);
|
||||||
|
|
||||||
|
bool FindProcessWindow(DWORD dwPid, const char* pClass,
|
||||||
|
HWND& hWnd, DWORD& uiThread);
|
||||||
|
|
||||||
|
bool ReconnectIO(bool OpenNewConsole);
|
||||||
|
|
||||||
|
using listdir = std::tuple<
|
||||||
|
std::vector<std::wstring>,
|
||||||
|
std::vector<std::wstring>
|
||||||
|
>;
|
||||||
|
|
||||||
|
listdir ListDirectory(const std::wstring& path);
|
||||||
|
uint64_t GetDirectorySize(const std::wstring& path);
|
||||||
|
|
||||||
|
#endif
|
||||||
Loading…
Add table
Reference in a new issue