c++ windows固定到任务栏(应用程序或快捷方式)

将一些常用的应用程序固定到任务栏,可以方便我们使用时快速打开。下面就介绍一下使用c++固定到任务栏的几种方法。

方法一

使用ShellExecute接口,但是这个方法在win10已经无效了。

::ShellExecute(NULL, L"taskbarpin", L"C:\\Windows\\System32\\notepad.exe", NULL, NULL, 0);

方法二

此方法在win10测试有效,按理来说其它环境应该也支持(xp是个奇葩,不一定)。

#include <atlbase.h>

bool TaskbarPinWin10(LPCTSTR lpszFilePath, LPCTSTR lpszFileName) {
  TCHAR  szVal[MAX_PATH] = { 0 };
  ULONG  uValSize = MAX_PATH;
  CRegKey  regKey;

  if (ERROR_SUCCESS != regKey.Open(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CommandStore\\shell\\Windows.taskbarpin"), KEY_READ)) {
    return false;
  }
  if (ERROR_SUCCESS != regKey.QueryStringValue(_T("ExplorerCommandHandler"), szVal, &uValSize)) {
    return false;
  }
  regKey.Close();
  if (ERROR_SUCCESS != regKey.Create(HKEY_CURRENT_USER, _T("SOFTWARE\\Classes\\*\\shell\\{:}"))) {
    return false;
  }
  if (ERROR_SUCCESS != regKey.SetStringValue(_T("ExplorerCommandHandler"), szVal)) {
    return false;
  }
  regKey.Close();

  IShellDispatch* pShellDisp = NULL;
  Folder *pFolder;
  FolderItem *pFolderItem;
  CComBSTR    stitle, str;
  HRESULT hr = S_OK;

  hr = ::CoCreateInstance(CLSID_Shell, NULL,
    CLSCTX_SERVER, IID_IShellDispatch, (LPVOID*)&pShellDisp);
  if (SUCCEEDED(hr)) {
    hr = pShellDisp->NameSpace(CComVariant(lpszFilePath), &pFolder);
    hr = pFolder->ParseName(CComBSTR(lpszFileName), &pFolderItem);
    if (SUCCEEDED(hr)) {
      pFolderItem->InvokeVerb(CComVariant(_T("{:}")));
    }
    hr = pShellDisp->Release();
    pShellDisp = NULL;
  }

  if (ERROR_SUCCESS != regKey.Open(HKEY_CURRENT_USER, _T("SOFTWARE\\Classes\\*\\shell"), KEY_READ | KEY_WRITE)) {
    return false;
  }

  regKey.RecurseDeleteKey(_T("{:}"));
  regKey.Close();

  return true;
}

void main() {
  ::CoInitialize(NULL);

  TaskbarPinWin10(L"C:\\Windows\\system32\\", L"notepad.exe");

  ::CoUninitialize();
}

方法三

此方法并未测试成功,原理是模拟点击右键菜单里的固定到任务栏,但是Win10添加了限制,判断了当前程序名是否是 explorer.exe,所以需要调用NtQuerySystemInformation修改PEB 里 PRTL_USER_PROCESS_PARAMETERS 中的 ImagePathName为 explorer.exe,要求新构造的 ImagePathName 长度不能比原来的 ImagePathName.Length 长,不然修改会失败。总之有点不实用,列出来仅作参考吧。

#include <winternl.h>
#include <atlcomcli.h>

void ChangeImageName(HANDLE hProcess) {
  typedef NTSTATUS(NTAPI* NtQueryInformationProcessPtr)(
    IN HANDLE ProcessHandle,
    IN PROCESSINFOCLASS ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength OPTIONAL);

  HMODULE hModule = GetModuleHandle(L"ntdll.dll");
  NtQueryInformationProcessPtr NtQueryInformationProcess =
    (NtQueryInformationProcessPtr)GetProcAddress(hModule, "NtQueryInformationProcess");

  if (!NtQueryInformationProcess)
    return;

  PROCESS_BASIC_INFORMATION pbi = { 0 };
  ULONG ulRetLength = 0;
  NTSTATUS ntStatus = NtQueryInformationProcess(
    hProcess,
    ProcessBasicInformation,
    &pbi,
    sizeof(PROCESS_BASIC_INFORMATION),
    &ulRetLength);

  BOOL bRet = (ntStatus >= 0);
  if (!bRet) {
    fprintf(stderr, "Could not get process information. Status = %X\n",
      ntStatus);
    return;
  }

  PPEB ppeb = pbi.PebBaseAddress;
  PRTL_USER_PROCESS_PARAMETERS pRtlProcParam = ppeb->ProcessParameters;


  //ref https://stackoverflow.com/a/38880235
  WCHAR szImagePath[MAX_PATH] = { 0 };
  wcscpy_s(szImagePath, pRtlProcParam->ImagePathName.Buffer);
  PathRemoveFileSpec(szImagePath);
  PathAddBackslash(szImagePath);
  wcscat_s(szImagePath, L"explorer.exe");

  int len = wcslen(szImagePath) * sizeof(WCHAR);
  int originalLen = pRtlProcParam->ImagePathName.Length;
  if (len > originalLen) {
    fwprintf(stderr, L"new ImagePathName cannot be longer than the original one\n");
    return;
  }
  SecureZeroMemory(pRtlProcParam->ImagePathName.Buffer, originalLen);
  memcpy(pRtlProcParam->ImagePathName.Buffer, szImagePath, len);
};

bool PinUnpinTaskbar(LPCWSTR szFilePath, bool pin) {
  HRESULT hr = S_OK;

  WCHAR szVerbName[MAX_PATH] = { 0 };
  HINSTANCE ht = LoadLibrary(L"Shell32.dll");

  //5386 is the DLL index for"Pin to Tas&kbar"
  //ref.//http://www.win7dll.info/shell32_dll.html
  UINT uID = pin ? 5386 : 5387;
  LoadString(ht, uID, szVerbName, MAX_PATH);

  CComPtr<IShellDispatch> pShellDisp;

  hr = CoCreateInstance(CLSID_Shell, nullptr, CLSCTX_SERVER, IID_IShellDispatch, reinterpret_cast<LPVOID*>(&pShellDisp));

  if (FAILED(hr))
    return false;

  CComPtr<Folder> pFolder;
  CComPtr<FolderItem> pFolderItem;

  WCHAR szTempPath[MAX_PATH] = { 0 };

  //folder
  wcscpy_s(szTempPath, szFilePath);
  PathRemoveFileSpec(szTempPath);
  CComVariant vDir(szTempPath);
  hr = pShellDisp->NameSpace(vDir, &pFolder);

  if (FAILED(hr))
    return false;

  //file name
  wcscpy_s(szTempPath, szFilePath);
  PathStripPath(szTempPath);
  hr = pFolder->ParseName(CComBSTR(szTempPath), &pFolderItem);

  if (FAILED(hr))
    return false;

  CComPtr<FolderItemVerbs> itemVerbs;
  hr = pFolderItem->Verbs(&itemVerbs);

  if (FAILED(hr))
    return false;

  LONG itemCount = 0;
  itemVerbs->get_Count(&itemCount);

  CComBSTR ccombstr(szVerbName);
  for (int i = 0; i < itemCount; i++) {
    CComPtr<FolderItemVerb> verb;
    itemVerbs->Item(CComVariant(i), &verb);
    if (verb) {
      CComBSTR name;
      hr = verb->get_Name(&name);

      if (SUCCEEDED(hr) && ccombstr == name) {
        verb->DoIt();
        fprintf(stdout, "verb founded, do it.\n");
        return true;
      }
    }
  }
  return false;
}

void main() {
  ::CoInitialize(NULL);

  ChangeImageName(GetCurrentProcess());
  PinUnpinTaskbar(L"C:\\Windows\\system32\\notepad.exe", true);

  ::CoUninitialize();
}