#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <tchar.h>
#define VK_E 0x45
#define VK_Q 0x51
#define VK_V 0x56
#define IDC_BUTTON_START 101
#define IDC_BUTTON_STOP 102
#define IDC_EDIT_INTERVAL 103
#define IDC_COMBO_HOTKEY 104
#define IDC_LISTBOX_CLIPBOARD_RECORDS 105
#define IDC_BUTTON_DELETE 107
#define IDC_BUTTON_CLEAR 109 // 新增清空按钮ID
#define IDC_CHECKBOX_TOPMOST 108 // 复选框ID
#define TIMER_ID_RESET_INTERVAL 1
#ifndef LB_END
#define LB_END (-1)
#endif
WCHAR
** g_ClipboardRecords = NULL;
int
g_ClipboardRecordCount = 0;
HWND
g_hListBox = NULL;
HINSTANCE
g_hInst = NULL;
HHOOK
g_hHook = NULL;
BOOL
g_bHookInstalled = FALSE;
HWND
g_hMainWnd = NULL;
int
g_nInputInterval = 20;
BOOL
g_bTerminateInput = FALSE;
int
g_nSelectedHotkey = 0;
HANDLE
g_hInputThread = NULL;
WCHAR
* g_pszClipboardText = NULL;
BOOL
g_bTopMost = FALSE;
LRESULT
CALLBACK KeyboardProc(
int
nCode,
WPARAM
wParam,
LPARAM
lParam);
LRESULT
CALLBACK WndProc(
HWND
hWnd,
UINT
message,
WPARAM
wParam,
LPARAM
lParam);
void
SetClipboardHook();
void
RemoveClipboardHook();
void
SimulateTextInput(
const
WCHAR
* text);
void
SimulateCharInput(
WCHAR
ch);
void
CreateMainWindow();
void
UpdateUIState();
DWORD
WINAPI SimulateTextInputThread(
LPVOID
lpParam);
BOOL
APIENTRY DllMain(
HMODULE
hModule,
DWORD
ul_reason_for_call,
LPVOID
lpReserved) {
g_hInst = (
HINSTANCE
)hModule;
return
TRUE;
}
LRESULT
CALLBACK KeyboardProc(
int
nCode,
WPARAM
wParam,
LPARAM
lParam) {
if
(nCode >= 0) {
KBDLLHOOKSTRUCT* pkbllhs = (KBDLLHOOKSTRUCT*)lParam;
if
(wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) {
BOOL
bBlock = FALSE;
BOOL
bAltPressed = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0;
BOOL
bCtrlPressed = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
switch
(g_nSelectedHotkey) {
case
0:
if
(pkbllhs->vkCode == VK_E && bCtrlPressed && !bAltPressed) {
bBlock = TRUE;
}
break
;
case
1:
if
(pkbllhs->vkCode == VK_Q && bCtrlPressed && !bAltPressed) {
bBlock = TRUE;
}
break
;
case
2:
if
(pkbllhs->vkCode == VK_V && bCtrlPressed && !bAltPressed) {
bBlock = TRUE;
}
break
;
case
3:
if
(pkbllhs->vkCode == VK_V && bAltPressed && !bCtrlPressed) {
bBlock = TRUE;
}
break
;
}
if
(bBlock) {
if
(g_hInputThread != NULL) {
g_bTerminateInput = TRUE;
}
else
{
if
(OpenClipboard(NULL)) {
HANDLE
hData = GetClipboardData(CF_UNICODETEXT);
if
(hData != NULL) {
g_pszClipboardText = (
WCHAR
*)GlobalLock(hData);
if
(g_pszClipboardText != NULL) {
g_hInputThread = CreateThread(NULL, 0, SimulateTextInputThread, (
LPVOID
)g_pszClipboardText, 0, NULL);
}
GlobalUnlock(hData);
}
CloseClipboard();
}
}
return
1;
}
}
}
return
CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
void
SetClipboardHook() {
if
(!g_bHookInstalled) {
g_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, g_hInst, 0);
if
(g_hHook != NULL) {
g_bHookInstalled = TRUE;
UpdateUIState();
}
}
}
void
RemoveClipboardHook() {
if
(g_bHookInstalled) {
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
g_bHookInstalled = FALSE;
UpdateUIState();
}
}
void
SimulateTextInput(
const
WCHAR
* text) {
g_bTerminateInput = FALSE;
for
(
const
WCHAR
* p = text; *p != L
'\0'
&& !g_bTerminateInput; ++p) {
SimulateCharInput(*p);
Sleep(g_nInputInterval);
}
if
(g_bTerminateInput) {
g_bTerminateInput = FALSE;
}
}
void
SimulateCharInput(
WCHAR
ch) {
INPUT input[2] = {0};
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = 0;
input[0].ki.wScan = ch;
input[0].ki.dwFlags = KEYEVENTF_UNICODE;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0;
input[1].ki.wScan = ch;
input[1].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
SendInput(2, input,
sizeof
(INPUT));
}
void
CreateMainWindow() {
WNDCLASSEX wc = {0};
wc.cbSize =
sizeof
(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = g_hInst;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName =
"ClipboardHookWndClass"
;
if
(RegisterClassEx(&wc)) {
g_hMainWnd = CreateWindowEx(
0,
"ClipboardHookWndClass"
,
"剪贴板文本输入器2.0"
,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 500,
NULL,
NULL,
g_hInst,
NULL
);
if
(g_hMainWnd) {
ShowWindow(g_hMainWnd, SW_SHOW);
UpdateUIState();
AddClipboardFormatListener(g_hMainWnd);
}
}
}
void
UpdateUIState() {
if
(g_hMainWnd) {
EnableWindow(GetDlgItem(g_hMainWnd, IDC_BUTTON_START), !g_bHookInstalled);
EnableWindow(GetDlgItem(g_hMainWnd, IDC_BUTTON_STOP), g_bHookInstalled);
}
}
LRESULT
CALLBACK WndProc(
HWND
hWnd,
UINT
message,
WPARAM
wParam,
LPARAM
lParam) {
static
HBRUSH
hBrush;
static
HBRUSH
hButtonBrush;
static
HBRUSH
hButtonHoverBrush;
static
HBRUSH
hButtonPressedBrush;
static
HFONT
hFont;
static
HWND
hEditInterval = NULL;
static
HWND
hComboHotkey = NULL;
static
HWND
hCheckboxTopMost = NULL;
static
HWND
hStartButton = NULL;
static
HWND
hStopButton = NULL;
static
HWND
hDeleteButton = NULL;
static
HWND
hClearButton = NULL;
switch
(message) {
case
WM_CREATE:
{
hBrush = CreateSolidBrush(RGB(240, 240, 240));
hButtonBrush = CreateSolidBrush(RGB(64, 128, 255));
hButtonHoverBrush = CreateSolidBrush(RGB(50, 100, 200));
hButtonPressedBrush = CreateSolidBrush(RGB(40, 80, 160));
hFont = CreateFont(14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS,
"Segoe UI"
);
SetClassLong(hWnd, GCL_HBRBACKGROUND, (
LONG
)hBrush);
hStartButton = CreateWindow(
"BUTTON"
,
"开启"
,
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON | BS_FLAT,
20, 30, 80, 30, hWnd, (
HMENU
)IDC_BUTTON_START, g_hInst, NULL);
hStopButton = CreateWindow(
"BUTTON"
,
"关闭"
,
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON | BS_FLAT,
120, 30, 80, 30, hWnd, (
HMENU
)IDC_BUTTON_STOP, g_hInst, NULL);
hCheckboxTopMost = CreateWindow(
"BUTTON"
,
"窗口置顶"
,
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_AUTOCHECKBOX,
220, 30, 100, 30, hWnd, (
HMENU
)IDC_CHECKBOX_TOPMOST, g_hInst, NULL);
hEditInterval = CreateWindow(
"EDIT"
,
"50"
,
WS_TABSTOP | WS_VISIBLE | WS_CHILD | ES_NUMBER | WS_EX_CLIENTEDGE,
170, 80, 140, 30, hWnd, (
HMENU
)IDC_EDIT_INTERVAL, g_hInst, NULL);
CreateWindow(
"STATIC"
,
"输入间隔(毫秒):"
,
WS_VISIBLE | WS_CHILD | SS_LEFT,
20, 80, 130, 30, hWnd, NULL, g_hInst, NULL);
CreateWindow(
"STATIC"
,
"快捷键(再按终止):"
,
WS_VISIBLE | WS_CHILD | SS_LEFT,
20, 120, 150, 100, hWnd, NULL, g_hInst, NULL);
hComboHotkey = CreateWindow(
"COMBOBOX"
, NULL,
WS_TABSTOP | WS_VISIBLE | WS_CHILD | CBS_DROPDOWNLIST | WS_VSCROLL | WS_EX_CLIENTEDGE,
160, 120, 165, 100, hWnd, (
HMENU
)IDC_COMBO_HOTKEY, g_hInst, NULL);
SendMessage(hComboHotkey, CB_ADDSTRING, 0, (
LPARAM
)
"Ctrl+E"
);
SendMessage(hComboHotkey, CB_ADDSTRING, 0, (
LPARAM
)
"Ctrl+Q"
);
SendMessage(hComboHotkey, CB_ADDSTRING, 0, (
LPARAM
)
"Ctrl+V"
);
SendMessage(hComboHotkey, CB_ADDSTRING, 0, (
LPARAM
)
"Alt+V"
);
SendMessage(hComboHotkey, CB_ADDSTRING, 0, (
LPARAM
)
"无快捷键"
);
SendMessage(hComboHotkey, CB_SETCURSEL, 0, 0);
SetTimer(hWnd, TIMER_ID_RESET_INTERVAL, 10, NULL);
g_hListBox = CreateWindow(
"LISTBOX"
, NULL,
WS_TABSTOP | WS_VISIBLE | WS_CHILD | LBS_STANDARD,
20, 160, 360, 280, hWnd, (
HMENU
)IDC_LISTBOX_CLIPBOARD_RECORDS, g_hInst, NULL);
hDeleteButton = CreateWindow(
"BUTTON"
,
"删除"
,
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON | BS_FLAT,
120, 430, 80, 30, hWnd, (
HMENU
)IDC_BUTTON_DELETE, g_hInst, NULL);
hClearButton = CreateWindow(
"BUTTON"
,
"清空"
,
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON | BS_FLAT,
220, 430, 80, 30, hWnd, (
HMENU
)IDC_BUTTON_CLEAR, g_hInst, NULL);
HFONT
hBigFont = CreateFont(28, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS,
"Segoe UI"
);
SendMessage(hWnd, WM_SETFONT, (
WPARAM
)hFont, MAKELPARAM(TRUE, 0));
SendMessage(hStartButton, WM_SETFONT, (
WPARAM
)hFont, MAKELPARAM(TRUE, 0));
SendMessage(hStopButton, WM_SETFONT, (
WPARAM
)hFont, MAKELPARAM(TRUE, 0));
SendMessage(hEditInterval, WM_SETFONT, (
WPARAM
)hFont, MAKELPARAM(TRUE, 0));
SendMessage(hComboHotkey, WM_SETFONT, (
WPARAM
)hFont, MAKELPARAM(TRUE, 0));
SendMessage(g_hListBox, WM_SETFONT, (
WPARAM
)hBigFont, MAKELPARAM(TRUE, 0));
SendMessage(hDeleteButton, WM_SETFONT, (
WPARAM
)hFont, MAKELPARAM(TRUE, 0));
SendMessage(hClearButton, WM_SETFONT, (
WPARAM
)hFont, MAKELPARAM(TRUE, 0));
SendMessage(hCheckboxTopMost, WM_SETFONT, (
WPARAM
)hFont, MAKELPARAM(TRUE, 0));
UpdateUIState();
break
;
}
case
WM_PAINT:
{
PAINTSTRUCT ps;
HDC
hdc = BeginPaint(hWnd, &ps);
RECT rect;
GetClientRect(hWnd, &rect);
FillRect(hdc, &rect, hBrush);
GetClientRect(hStartButton, &rect);
OffsetRect(&rect, 20, 30);
FillRect(hdc, &rect, hButtonBrush);
GetClientRect(hStopButton, &rect);
OffsetRect(&rect, 120, 30);
FillRect(hdc, &rect, hButtonBrush);
GetClientRect(hDeleteButton, &rect);
OffsetRect(&rect, 120, 320);
FillRect(hdc, &rect, hButtonBrush);
GetClientRect(hClearButton, &rect);
OffsetRect(&rect, 220, 320);
FillRect(hdc, &rect, hButtonBrush);
GetClientRect(hCheckboxTopMost, &rect);
OffsetRect(&rect, 220, 30);
FillRect(hdc, &rect, hBrush);
EndPaint(hWnd, &ps);
break
;
}
case
WM_CLIPBOARDUPDATE:
{
if
(OpenClipboard(NULL)) {
HANDLE
hData = GetClipboardData(CF_UNICODETEXT);
if
(hData != NULL) {
WCHAR
* text = (
WCHAR
*)GlobalLock(hData);
if
(text != NULL) {
BOOL
found = FALSE;
int
index = -1;
for
(
int
i = 0; i < g_ClipboardRecordCount; i++) {
if
(wcscmp(g_ClipboardRecords[i], text) == 0) {
found = TRUE;
index = i;
break
;
}
}
if
(found) {
WCHAR
* temp = g_ClipboardRecords[index];
for
(
int
i = index; i > 0; i--) {
g_ClipboardRecords[i] = g_ClipboardRecords[i - 1];
}
g_ClipboardRecords[0] = temp;
SendMessageW(g_hListBox, LB_DELETESTRING, index, 0);
SendMessageW(g_hListBox, LB_INSERTSTRING, (
WPARAM
)0, (
LPARAM
)temp);
SendMessageW(g_hListBox, LB_SETCURSEL, 0, 0);
}
else
{
WCHAR
* newText = _wcsdup(text);
if
(newText != NULL) {
g_ClipboardRecords = (
WCHAR
**)
realloc
(g_ClipboardRecords, (g_ClipboardRecordCount + 1) *
sizeof
(
WCHAR
*));
if
(g_ClipboardRecords != NULL) {
g_ClipboardRecords[g_ClipboardRecordCount] = newText;
g_ClipboardRecordCount++;
for
(
int
i = g_ClipboardRecordCount - 1; i > 0; i--) {
g_ClipboardRecords[i] = g_ClipboardRecords[i - 1];
}
g_ClipboardRecords[0] = newText;
SendMessageW(g_hListBox, LB_INSERTSTRING, (
WPARAM
)0, (
LPARAM
)newText);
SendMessageW(g_hListBox, LB_SETCURSEL, 0, 0);
}
}
}
GlobalUnlock(hData);
}
}
CloseClipboard();
}
break
;
}
case
WM_CTLCOLORSTATIC:
{
HDC
hdc = (
HDC
)wParam;
HWND
hwndStatic = (
HWND
)lParam;
SetBkColor(hdc, RGB(240, 240, 240));
SetTextColor(hdc, RGB(0, 0, 0));
return
(
LRESULT
)GetStockObject(NULL_BRUSH);
}
case
WM_COMMAND:
{
int
wmId = LOWORD(wParam);
if
(wmId == IDC_BUTTON_START) {
SetClipboardHook();
}
else
if
(wmId == IDC_BUTTON_STOP) {
RemoveClipboardHook();
}
else
if
(wmId == IDC_EDIT_INTERVAL) {
if
(HIWORD(wParam) == EN_CHANGE) {
char
buffer[32];
GetWindowTextA(hEditInterval, buffer, 32);
g_nInputInterval =
atoi
(buffer);
}
}
else
if
(wmId == IDC_COMBO_HOTKEY) {
if
(HIWORD(wParam) == CBN_SELCHANGE) {
int
index = SendMessageW(hComboHotkey, CB_GETCURSEL, 0, 0);
g_nSelectedHotkey = index;
}
}
else
if
(wmId == IDC_BUTTON_DELETE) {
int
index = SendMessageW(g_hListBox, LB_GETCURSEL, 0, 0);
if
(index != LB_ERR) {
free
(g_ClipboardRecords[index]);
for
(
int
i = index; i < g_ClipboardRecordCount - 1; i++) {
g_ClipboardRecords[i] = g_ClipboardRecords[i + 1];
}
g_ClipboardRecordCount--;
SendMessageW(g_hListBox, LB_DELETESTRING, index, 0);
if
(g_ClipboardRecordCount > 0) {
int
newIndex = min(index, g_ClipboardRecordCount - 1);
SendMessageW(g_hListBox, LB_SETCURSEL, newIndex, 0);
}
}
}
else
if
(wmId == IDC_BUTTON_CLEAR) {
for
(
int
i = 0; i < g_ClipboardRecordCount; i++) {
free
(g_ClipboardRecords[i]);
}
free
(g_ClipboardRecords);
g_ClipboardRecords = NULL;
g_ClipboardRecordCount = 0;
SendMessageW(g_hListBox, LB_RESETCONTENT, 0, 0);
}
else
if
(wmId == IDC_LISTBOX_CLIPBOARD_RECORDS) {
if
(HIWORD(wParam) == LBN_SELCHANGE) {
int
index = SendMessageW(g_hListBox, LB_GETCURSEL, 0, 0);
if
(index != LB_ERR && g_ClipboardRecords[index] != NULL) {
if
(OpenClipboard(NULL)) {
EmptyClipboard();
HGLOBAL
hMem = GlobalAlloc(GMEM_MOVEABLE, (wcslen(g_ClipboardRecords[index]) + 1) *
sizeof
(
WCHAR
));
if
(hMem) {
WCHAR
* pText = (
WCHAR
*)GlobalLock(hMem);
wcscpy(pText, g_ClipboardRecords[index]);
GlobalUnlock(hMem);
SetClipboardData(CF_UNICODETEXT, hMem);
}
CloseClipboard();
}
}
}
}
else
if
(wmId == IDC_CHECKBOX_TOPMOST) {
if
(HIWORD(wParam) == BN_CLICKED) {
g_bTopMost = IsDlgButtonChecked(hWnd, IDC_CHECKBOX_TOPMOST);
SetWindowPos(hWnd, g_bTopMost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}
}
break
;
}
case
WM_TIMER:
{
if
(wParam == TIMER_ID_RESET_INTERVAL) {
KillTimer(hWnd, TIMER_ID_RESET_INTERVAL);
g_nInputInterval = 10;
char
buffer[32];
sprintf
(buffer,
"%d"
, g_nInputInterval);
SetWindowText(hEditInterval, buffer);
}
break
;
}
case
WM_CLOSE:
RemoveClipboardHook();
DestroyWindow(hWnd);
break
;
case
WM_DESTROY:
{
DeleteObject(hBrush);
DeleteObject(hButtonBrush);
DeleteObject(hButtonHoverBrush);
DeleteObject(hButtonPressedBrush);
DeleteObject(hFont);
for
(
int
i = 0; i < g_ClipboardRecordCount; i++) {
free
(g_ClipboardRecords[i]);
}
free
(g_ClipboardRecords);
PostQuitMessage(0);
break
;
}
default
:
return
DefWindowProc(hWnd, message, wParam, lParam);
}
return
0;
}
int
WINAPI WinMain(
HINSTANCE
hInstance,
HINSTANCE
hPrevInstance,
LPSTR
lpCmdLine,
int
nCmdShow) {
CreateMainWindow();
MSG msg;
while
(GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return
0;
}
DWORD
WINAPI SimulateTextInputThread(
LPVOID
lpParam) {
SimulateTextInput((
WCHAR
*)lpParam);
g_hInputThread = NULL;
return
0;
}