0%

番外:快速搜索脚本之win32版

几天前用python写了个快捷键打开搜索引擎的工具,但是后来又想搞得更像模像样一点,于是就着手用win32重新写了一遍,也加了一些新东西进去。win32开发是刚刚上手,一遍对着MSDN的例程一边一点点把功能堆上去的,但其实实际感受起来会发现api很多,用起来也很友好,比较烦人的也就是几种数据类型的转换。

思路还是和之前的差不多,从最最核心的获取剪切板和打开浏览器开始,有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
BOOL GetTextFromClipboard()
{
if (::OpenClipboard(NULL))
{
//获得剪贴板数据
HGLOBAL hMem = GetClipboardData(CF_TEXT);
if (NULL != hMem)
{
char* lpStr = (char*)::GlobalLock(hMem);
if (NULL != lpStr)
{
string engine_link;
//XXX
if (engine) {
engine_link = "http://www.baidu.com/s?wd=";
}
else {
engine_link = "https://www.google.com/search?q=";
}

string keyword(lpStr);
string search_link = engine_link + keyword;
TCHAR lk[MAX_PATH];
//把string 转成 tchar
#ifdef UNICODE
_stprintf_s(lk, MAX_PATH, _T("%S"), search_link.c_str());//%S宽字符
#else
_stprintf_s(lk, MAX_PATH, _T("%s"), search_link.c_str());//%s单字符
#endif
ShellExecute(NULL, _T("open"), _T(lk), NULL, NULL, SW_SHOW); //不要指定“explore.exe”,否则会打开的是文件资源管理器
::GlobalUnlock(hMem);
}
}
::CloseClipboard();
return TRUE;
}
return FALSE;
}

然后我希望这个程序运行时只在托盘显示图标,首先初始化托盘并设置菜单,使用函数Shell_NotifyIcon来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void InitTray(HINSTANCE hInstance, HWND hWnd)
{
nid.cbSize = sizeof(NOTIFYICONDATA);//结构体长度
nid.hWnd = hWnd;//窗口句柄
nid.uID = IDI_ICON1;//图标
nid.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1));//加载图标
nid.uCallbackMessage = WM_NOTIFYICON;//消息处理,这里很重要,处理鼠标点击
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
strcpy_s(nid.szTip, _T("托盘响应"));

hMenu = CreatePopupMenu();//生成托盘菜单
//为托盘菜单添加两个选项
AppendMenu(hMenu, MF_STRING, ID_BAIDU, TEXT("百度"));
AppendMenu(hMenu, MF_STRING, ID_GOOGLE, TEXT("谷歌"));
AppendMenu(hMenu, MF_STRING, ID_SHOW, TEXT("提示"));
AppendMenu(hMenu, MF_STRING, ID_EXIT, TEXT("退出"));

CheckMenuItem(hMenu, ID_BAIDU, MF_CHECKED);
CheckMenuItem(hMenu, ID_GOOGLE, MF_UNCHECKED);

Shell_NotifyIcon(NIM_ADD, &nid);

}

接着创建主窗体,WS_EX_TOOLWINDOW这一项可以使任务栏上不会出现图标:

1
2
3
4
5
HWND hWnd = CreateWindowEx(WS_EX_TOOLWINDOW, szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, NULL, NULL, hInstance, NULL);
if (!hWnd) {
MessageBox(NULL, _T("Call to CreateWindow failed!"), _T("Win32 Guided Tour"), NULL);
return 1;
}

创建主窗口时,使用ShowWindow(hWnd, SW_HIDE);这一条语句来令窗口不显示
于是UI部分大致就完成了
最后再编写系统消息处理部分,不知道为什么,自定义的热键组合在dispatch后没办法被WinProc捕获,于是只能在dispatch之前对按键消息进行处理。like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
//在dispatch之前先截获消息
if (msg.message == WM_HOTKEY)
{
GetTextFromClipboard();
//MessageBox(NULL, _T("Debug!"), _T("Win32 Guided Tour"), NULL);
}

TranslateMessage(&msg); //翻译消息
DispatchMessage(&msg); //派遣消息 调用WndProc函数

}
return (int)msg.wParam;

最后WinProc里我们来做UI上的交互,也就是菜单功能的实现,包括切换搜索引擎以及程序的退出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
PAINTSTRUCT ps;
HDC hdc;
TCHAR greeting[] = _T("Hello, World!");
switch (message) {
case WM_PAINT:
//要处理 WM_PAINT 消息,首先应调用 BeginPaint
//然后处理所有的逻辑以及在窗口中布局文本、按钮和其他控件等
//然后调用 EndPaint。
hdc = BeginPaint(hWnd, &ps);

// -----------------在这段代码对应用程序进行布局------------------------
// 对于此应用程序,开始调用和结束调用之间的逻辑是在窗口中显示字符串 “Hello,World!”。
// 请注意 TextOut 函数用于显示字符串。
TextOut(hdc, 50, 5, greeting, _tcslen(greeting));
// -----------------------布局模块结束----------------------------------

EndPaint(hWnd, &ps);
break;
case WM_NOTIFYICON://这是我们的宏定义
if ((wParam == IDI_ICON1) && (lParam == WM_LBUTTONDOWN)) // 鼠标左键按下时响应
{
//ShowWindow(hWnd, 10);//当点击鼠标的时候显示窗口
}
if ((wParam == IDI_ICON1) && (lParam == WM_RBUTTONDOWN)) // 鼠标左键按下时响应
{
//获取鼠标坐标
POINT pt; GetCursorPos(&pt);

//解决在菜单外单击左键菜单不消失的问题
SetForegroundWindow(hWnd);

//使菜单某项变灰
//EnableMenuItem(hMenu, ID_SHOW, MF_GRAYED);

//显示并获取选中的菜单
int cmd = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, NULL, hWnd,
NULL);
//利用菜单选项切换搜索引擎
if (cmd == ID_BAIDU) {
engine = TRUE;
CheckMenuItem(hMenu,ID_BAIDU,MF_CHECKED);
CheckMenuItem(hMenu, ID_GOOGLE, MF_UNCHECKED);
}
if (cmd == ID_GOOGLE) {
engine = FALSE;
CheckMenuItem(hMenu, ID_GOOGLE, MF_CHECKED);
CheckMenuItem(hMenu, ID_BAIDU, MF_UNCHECKED);
}
if (cmd == ID_SHOW)
MessageBox(hWnd, "快捷键 ctrl+上方向键 搜索", "提示", MB_OK);
if (cmd == ID_EXIT)
PostMessage(hWnd, WM_DESTROY, NULL, NULL);
}
break;
case WM_DESTROY:
UnregisterHotKey(NULL, hkid1);
Shell_NotifyIcon(NIM_DELETE, &nid);
PostQuitMessage(0);
break;
case WM_SYSCOMMAND:
if (wParam == SC_MINIMIZE) {
ShowWindow(hWnd, SW_HIDE);
//MessageBox(NULL, _T("Debug!"), _T("Win32 Guided Tour"), NULL);
//Shell_NotifyIcon(NIM_ADD, &nid); //最小化时隐藏窗口并设置系统托盘
}
default:
//DefWindowProc缺省窗口处理函数
//这个函数是默认的窗口处理函数,我们可以把不关心的消息都丢给它来处理
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}

其实思路清晰之后一遍做下来并没有想象中的那么痛苦,特别是这种一个个功能迭代的过程是乐在其中的。win32编程无非是搞清楚系统消息和窗体的关系,要不是到后面顶不住身体突发不适可能会把更多功能怼进去,但是目前至少以及达到了我最初设想的样子了,也就不想再做下去了,UI这方面不用QT或者MFC做还是比较难搞的。
嘛,告一段落吧。