雖然我們在桌面按下滑鼠右鍵,就可以在檢視功能的子選單中選擇是否要顯示桌面圖示,但需使用滑鼠又要兩個操作步驟感覺就很不好用。
昨天晚上終於狠下心來把它完成,雖然我之前已經 Google 過了,但肩膀受傷後人就變得很懶,除了工作以外的程式都很懶得動工,這個星期老婆進公司上班故不用張羅三餐,我連自己的午餐都隨便吃,可能是偷懶的緣故,感覺這星期肩膀狀況還不錯,所以昨天才會想來做這件事。
好久沒寫 Windows 相關的程式了,很多 HWND 等的參數都變得很陌生。
一開始在沒 Google 前,直覺就是可能跟 registry 有關,也找到相關的 registry。
[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced]
"HideIcons"=dword:00000001
原本以為只要修改這個值後,再想辦法用廣播方式送出 WM_SETTINGCHANGE 訊息,應該就可以生效,但據我實驗的結果,桌面程式知道我動了 registry,但並不會生效,我猜它應該只是讀 registry 得知目前的值,才決定是否要在 "顯示桌面圖示" 前打勾。
好吧,只好走 Google 來的方式,來解決這個問題。
要特別注意的一點,上面有說應該要看 OS 版本來決定對誰發送訊息,但我在寫 Code 前有先用 Spy 這類的軟體來找尋視窗,發現在 Windows 10 跟連結的說法不太一樣。
順序應該是如下:
01. 找到 Progman 這個 Window。
02. 找到 class 為 "SHELLDLL_DefView" 的視窗。
03. 送出 WM_COMMAND 訊息。
int toggleIconByShell() {
int res = 0;
HWND progman;
HWND defView;
// Find SHELLDLL_DefView class
progman = FindWindowExA(NULL, NULL, "Progman", "Program Manager");
printf("Program is 0x%p\n", progman);
defView = FindWindowExA(progman, NULL, "SHELLDLL_DefView", "");
printf("SHELLDLL_DefView is 0x%p\n", defView);
// Send message to defView
int lParam = 0;
int wParam = 0x7402;
LSTATUS lResult = SendMessage(defView, WM_COMMAND, wParam, lParam);
if (lResult != ERROR_SUCCESS) {
printErrorCode(lResult);
res = lResult;
}
printf("OK\n");
return res;
}
2021/06/24 更新
又把文章仔細看了一遍,SHELLDLL_DefView 在某些情況下,會從原本的 Progman 跑到 WorkerW 下面,故需要針對不同情況去找 Handle,將尋找 Handle 獨立出來變成函數,增加可讀性。
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
void printErrorCode(LSTATUS lResult)
{
printf("ErrorCode = %d(0x%08X) \n", (int)lResult, (int)lResult);
}
BOOL EnumCallback(HWND hwnd, LPARAM lParam)
{
char name[256];
memset(name, 0x00, sizeof(name));
GetClassNameA(hwnd, name, sizeof(name));
if (strcmp(name, "WorkerW") == 0) {
HWND *address = (HWND*)lParam;
HWND defView = FindWindowExA(hwnd, NULL, "SHELLDLL_DefView", "");
if (defView != NULL) {
*address = defView;
return FALSE;
}
}
return TRUE;
}
HWND getDefViewHandle()
{
HWND progman;
HWND defView;
// Find SHELLDLL_DefView class
progman = FindWindowExA(NULL, NULL, "Progman", "Program Manager");
printf("Program is 0x%p\n", progman);
defView = FindWindowExA(progman, NULL, "SHELLDLL_DefView", "");
printf("SHELLDLL_DefView is 0x%p\n", defView);
if (defView == NULL) {
HWND *address = &defView;
// defView is moved to "WorkerW"
EnumWindows(EnumCallback, (LPARAM)address);
printf("After searching WorkerW, SHELLDLL_DefView is 0x%p\n", *address);
defView = *address;
// SHELLDLL_DefView is not the child of WorkerW.
if (defView == NULL) {
HWND desktop = GetShellWindow();
defView = FindWindowExA(desktop, NULL, "SHELLDLL_DefView", "");
printf("After searching desktop, SHELLDLL_DefView is 0x%p\n", defView);
}
}
return defView;
}
int toggleIconByShell()
{
int res = 0;
HWND defView = getDefViewHandle();
if (defView == NULL) {
printf("SHELLDLL_DefView not found \n");
return -1;
}
// Send message to defView
int lParam = 0;
int wParam = 0x7402;
LSTATUS lResult = SendMessage(defView, WM_COMMAND, wParam, lParam);
if (lResult != ERROR_SUCCESS) {
printErrorCode(lResult);
res = lResult;
}
printf("OK\n");
return res;
}
int main(void) {
toggleIconByShell();
system("pause");
return 0;
}
2021/06/25 更新
在已經知道訣竅的情況下,抓取訊息就變得很簡單,但當初怎樣找到標的物和要送的訊息,我想這才是最困難的部份吧。