Windows 7 taskbar and startmenu pin
在Windows 7上,用户可以将自己喜欢的软件“钉”在开始菜单或任务栏,使用起来更加方便。但有时候我们也需要用程序来将这个过程自动化,比如在IT环境里定制客户机,或者我们从一台Win7系统迁移到另一台Win7系统时。
怎么知道已有哪些软件被“钉”在开始菜单或任务栏:
当软件“钉”在开始菜单或任务栏后,系统会在"%appdata%\microsoft\internet explorer\Quick Launch\User Pinned"下的"StartMenu"和"TaskBar"目录内创建软件的链接。我们只要看这两个目录下分别有哪些有效链接就可以知道开始菜单和任务栏分别钉有哪些软件。对于每一个链接,我们可以知道它链接了那个程序,然后在目标系统上,我们就可以将这个程序钉在开始菜单或任务栏上(目标系统上也要有这个程序)。
如何把软件“钉”在开始菜单或任务栏:
需要注意的是只能将链接钉在开始菜单或任务栏,而且链接必须指向可执行程序,所以我们需要先为目标程序创建一个链接,然后调用ShellExecute,将链接钉住,钉完后这个链接可以删掉。任务栏上钉与解除时传递给ShellExecute的lpOperation参数是taskbarpin/taskbarunpin,而开始菜单的lpOperation参数是startpin/startunpin。
示例,将IE钉在任务栏上:
1. 在桌面上为IE创建链接IE.lnk
2. ShellExecute(NULL, "taskbarpin", "c:\users\username\Desktop\IE.lnk", NULL, NULL, 0)
以上有一个限制,就是不知道在任务栏上链接的顺序。
Windows XP 任务栏的遍历
2009-05-04 9:26
这里的方法可以找到XP下的任务栏,并对任务栏进行遍历,在其他系统上就需要相应的改动了。这里是先找到ToolbarWindow32这个窗口,然后再用通用的遍历Toolbar的方法查找各个按钮;这里有一点特别的是,要用到跨进程缓冲区(因为任务栏和遍历程序不是在同一个进程)。
在XP下有一个“分组相似任务栏按钮”特性,如果有一个新的进程窗口要在任务栏上显示,则系统会创建两个按钮,一个按钮有BTNS_DROPDOWN style,默认隐藏,在任务分组后显示(这个style指定它显示一个箭头图标);另一个按钮就是通常我们看到的任务栏按钮。
struct TBBUTTONDATA是从网上找到的,没有在微软的文档中发现,但在XP上验证是有效的。
在遍历按钮,SendMessage的时候,要注意是Zero-based index 还是 Command ID,现在一些网上的资料范例在需要传Command ID的时候传Zero-based index做参数,这是错误的。
HWND GetTaskButtonHost(HWND hShellTrayWnd)
{
HWND hWnd = FindWindowEx(hShellTrayWnd, NULL, _T("ReBarWindow32"), NULL);
if (hWnd == NULL)
return FALSE;
hWnd = FindWindowEx(hWnd, NULL, _T("MSTaskSwWClass"), NULL);
if (hWnd == NULL)
return FALSE;
hWnd = FindWindowEx(hWnd, NULL, _T("ToolbarWindow32"), NULL);
return hWnd;
}
//MSDN: To continue enumeration, the callback function must return TRUE; to stop enumeration, it must return FALSE.
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
TCHAR strClsName[MAX_PATH+1] = _T("");
GetClassName(hwnd, strClsName, MAX_PATH+1);
if (_tcsicmp(strClsName, _T("Shell_TrayWnd")) == 0)
{
HWND hShellTrayWnd = GetTaskButtonHost(hwnd);
if (hShellTrayWnd)
{
HWND *pHwnd = (HWND *)lParam;
*pHwnd = hShellTrayWnd;
return FALSE;
}
}
return TRUE;
}
struct TBBUTTONDATA
{
HWND hwnd; //the handle of the window on the taskbar
UINT uID;
UINT uCallbackMessage;
DWORD Reserved[2];
HICON hIcon;
};
//This solution works on XP.
void EnumTasks()
{
HWND hWnd = NULL;
EnumWindows(EnumWindowsProc, (LPARAM)&hWnd);
if (hWnd == NULL)
return;
DWORD dwProcID = 0;
GetWindowThreadProcessId(hWnd, &dwProcID);
HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, dwProcID);
if (hProcess == NULL)
return;
LPTSTR pProcBuf = NULL; //pointer to the buffer allocated in another process
TBBUTTON tbb;
TBBUTTONDATA tbbData;
TCHAR strCaption[MAX_PATH+1] = _T("");
pProcBuf = (LPTSTR)VirtualAllocEx(hProcess, NULL, sizeof(strCaption), MEM_COMMIT, PAGE_READWRITE);
if (pProcBuf == NULL)
{
CloseHandle(hProcess);
return;
}
DWORD nTaskCount = ::SendMessage(hWnd, TB_BUTTONCOUNT, 0, 0);
for (DWORD i = 0; i < nTaskCount; i++)
{
ZeroMemory(&tbb, sizeof(TBBUTTON));
WriteProcessMemory(hProcess, pProcBuf, &tbb, sizeof(TBBUTTON), NULL);
SendMessage(hWnd, TB_GETBUTTON, i/*Zero-based index*/, (LPARAM)pProcBuf);
ReadProcessMemory(hProcess, pProcBuf, &tbb, sizeof(TBBUTTON), NULL);
//Group similar task-bar buttons(a feature of Windows): if the top-level window is
//shown on task-bar, Windows will create another dropdown button for each process.
if (tbb.fsStyle & BTNS_DROPDOWN)
continue;
ReadProcessMemory(hProcess, (LPCVOID)tbb.dwData, &tbbData, sizeof(TBBUTTONDATA), NULL);
DWORD nDesireLen = SendMessage(hWnd, TB_GETBUTTONTEXT, tbb.idCommand, 0); //length not including null terminator
if (nDesireLen >= sizeof(strCaption))
continue;
ZeroMemory(strCaption, sizeof(strCaption));
WriteProcessMemory(hProcess, pProcBuf, strCaption, sizeof(strCaption), NULL);
SendMessage(hWnd, TB_GETBUTTONTEXT, tbb.idCommand, (LPARAM)pProcBuf);
ReadProcessMemory(hProcess, pProcBuf, strCaption, sizeof(strCaption), NULL);
OutputDebugInfo(L"%s\n", strCaption);
}
VirtualFreeEx(hProcess, pProcBuf, 0, MEM_RELEASE);
CloseHandle(hProcess);
}
前一篇Windows XP 任务栏的遍历介绍了在XP下遍历任务栏的方法,可以精确遍历出任务栏的按钮;这篇博客介绍一种通用的遍历任务栏的方法(不仅限于XP),但可能结果不是太精确。
对于什么样的窗口才会在任务栏上创建按钮,MSDN上的说法是:
The Shell creates a button on the taskbar whenever an application creates a window that isn't owned. To ensure that the window button is placed on the taskbar, create an unowned window with the WS_EX_APPWINDOW extended style. To prevent the window button from being placed on the taskbar, create the unowned window with the WS_EX_TOOLWINDOW extended style. As an alternative, you can create a hidden window and make this hidden window the owner of your visible window.
做了一些测试,总结的结果是:
1、如果窗口没有被其他窗口拥有(GetWindow(hwnd, GW_OWNER) == 0),那么默认情况下它会在任务栏中创建按钮,除非:
a). 窗口被隐藏了
或者:
b). 窗口有WS_EX_TOOLWINDOW风格,且没有WS_EX_APPWINDOW风格
2、如果窗口被其他窗口拥有,默认不会在任务栏创建按钮,除非:
a). 窗口可见,且有WS_EX_APPWINDOW风格
从1、2点可以得出结论,如果窗口可见,有WS_EX_APPWINDOW和WS_EX_TOOLWINDOW风格,那么,这个窗口是一个Tool window,且在任务栏上有按钮。
范例:
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
LONG lExStyle = ::GetWindowLong(hwnd, GWL_EXSTYLE);
if (::IsWindowVisible(hwnd) &&
( (lExStyle & WS_EX_APPWINDOW) ||
(GetWindow(hwnd, GW_OWNER) == NULL && (lExStyle & WS_EX_TOOLWINDOW) == 0) )
)
{
TCHAR strTitle[MAX_PATH+1] = _T("");
GetWindowText(hwnd, strTitle, MAX_PATH+1);
OutputDebugInfo(L"%s\n", strTitle);
}
return TRUE;
}
EnumWindows(EnumWindowsProc, NULL);
Vista UAC : 以管理员权限运行程序
2009-04-25 20:21
在Windows Vista启用UAC后,程序启动后默认没有管理员权限,
即使当前的用户属于管理员组。如果要使程序获得管理员权限,还需要一些额外的工作。可以从两个角度来看待:
一、从程序用户的角度:
1、用鼠标右击某个应用程序(例如cmd.exe), 再选择"Run As Administrator"(在旧版本里是"Run Elevated")来以管理员权限运行它
2、在程序(或其快捷方式)的属性Compatibility中选择Run this program as an administrator来运行
3、在程序的安装目录,添加一个Manifest文件,使程序以管理员权限运行
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestversion="1.0">
<trustinfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedprivileges>
<requestedexecutionlevel level="requireAdministrator" uiaccess="false">
</requestedexecutionlevel>
</requestedprivileges>
</security>
</trustinfo>
4、有编程经验的用户也可以写脚本来运行程序,如:
objShell = new ActiveXObject("Shell.Application");
objShell.ShellExecute(app, args, "", "runas");
C#示例:
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/c c:\\test\\script.cmd";
startInfo.UseShellExecute = true;
startInfo.Verb = "RunAs";
Process process = new Process();
process.StartInfo = startInfo;
process.Start();
C/C++, 可调用ShellExecute或ShellExecuteEx, 把lpOperation/lpVerb设成"RunAs"就可以
二、从开发人员角度:
1、在应用程序RC中加入MANIFEST类型资源.
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestversion="1.0">
<trustinfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedprivileges>
<requestedexecutionlevel level="requireAdministrator" uiaccess="false">
</requestedexecutionlevel>
</requestedprivileges>
</security>
</trustinfo>
2、以上几种方法都使得程序刚运行就要求管理员权限,要在程序运行中动态要求管理员权限则可以使用COM Elevation Moniker。可参考:http://msdn2.microsoft.com/en-us/library/ms679687.aspx