修改程序在桌面和开始的快捷图标
最近给客户升级产品时,有的时候出现客户修改了应用程序的图标,客户只做了升级但是没有做重新安装就出现了:客户修改了应用程序的图标 但是桌面的快捷方式没有更改的问题!基于这个问题,我们做了对桌面图标的删除和修改桌面和开始菜单的快捷图标。
一、快捷方式的实质
Windows的快捷方式实际上是一个带有扩展名LNK的数据文件,其中包含有用于访问Windows某一对象(即在资源管理器中所能浏览的所有对象,包括文件、文件夹、驱动器及打印机等)的有关信息,如目标对象的路径和名称,工作目录,要传递的命令行参数,运行时的初始显示状态,图标及其快捷键等。通过在快捷方式上单击鼠标右键并在弹出菜单中选择“属性"可以观察该快捷方式的这些性质。
快捷方式的数据文件如果存放在“/Windows/Desktop"子目录下,这个快捷方式就会显示在桌面上,而如果存放在“/Windows/Start Menu/Programs" 子目录下,这个快捷方式就会作为“开始"菜单的一个菜单项出现。而桌面上的文件夹和“开始"菜单的菜单组则是上述两个子目录下的子目录的表现。
二、编程思想
Windows外壳(Shell)的快捷方式是以OLE技术的组件对象模型COM(Component Object Model)为基础而设计的。利用COM模型,一个应用程序可以调用另一个应用程序的某些功能。
在了解了上述基本原理后,创建Windows的快捷方式就比较容易了。首先利用OLE通过调用CoCreateInstance()函数建立一个IID_IShellLink实例,并同时得到其接口指针。利用这个接口指针可以对其各项属性进行设置。为了使这些信息以快捷方式的数据文件(*.lnk)格式保存起来,还需要从IID_IShellLink对象取得其 IID_IPersistFile接口指针,以便于调用其成员函数Save(),保存前面设置的信息。至于如何删除快捷方式以及创建和删除文件夹,则只需要简单地调用文件操作函数SHFileOperation()。
3、应用举例
为了程序更好的封装和移植,在这里我使用了win32终端程序来实现,输入时1和2实现向开始菜单和桌面添加快捷方式,输入3和4用来实现删除开始菜单和桌面添加快捷方式。由于我们的程序安装的过程中会把快捷键的图标放到系统的公共界面,在这里需要程序有管理员权限才能对图标进行修改。如果放在用户自己的界面的就不用可管理员权限了。
程序中的YLChangeQuicklyIcom.h文件
#define KEY_PATHS_DESKTOP L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
#define KEY_PATHS_START_MENU L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders
//需要删除的快捷图标的名字
#define CUSTOMARY_APP_NAME "ChangIcon"
/*
*需要删除的快捷图标的所在的分组
*/
//#define CUSTOMARY_APP_GROUP "GoCom"
////需要删除的快捷图标的名字
#define NOW_APP_NAME "ChangIcon"
//需要添加的快捷图标的所在的分组 如果没有这个文件的话可以创建 在我的代码中没有创建 因为这边没有这个需求
//#define NOW_APP_GROUP "GoCom"
//现在程序的生成的
#define CUSTOMARY_NAME "ChangIcon.exe"
#define RUN_APP_NAME "ChangIcon.exe"
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <afx.h>
#include <shellapi.h>
typedef enum ShortcutsType
{
ShortcutsTypeStartMenu,
ShortcutsTypeDesktop
}ShortcutsType;
#pragma once
class YLChangeQuicklyIcom
{
public:
YLChangeQuicklyIcom();
~YLChangeQuicklyIcom();
public:
//向开始菜单中加入快捷方式
static void AddToStartMenu(std::string shortcutDisplay);
static void AddToDesktop(std::string shortcutDisplay);
static void DeleteStartMenu(std::string shortcutDisplay);
static void DeleteDesktop(std::string shortcutDisplay);
};
程序中的YLChangeQuicklyIcom.m文件
#include "stdafx.h"
#include "YLChangeQuicklyIcom.h"
#include <shlobj.h>
YLChangeQuicklyIcom::YLChangeQuicklyIcom()
{
}
YLChangeQuicklyIcom::~YLChangeQuicklyIcom()
{
}
void StringReplace(std::string &strBase, std::string strSrc, std::string strDes)
{
std::string::size_type pos = 0;
std::string::size_type srcLen = strSrc.size();
std::string::size_type desLen = strDes.size();
pos = strBase.find(strSrc, pos);
while ((pos != std::string::npos))
{
strBase.replace(pos, srcLen, strDes);
pos = strBase.find(strSrc, (pos + desLen));
}
}
void CharToTchar(const char * _char, TCHAR * tchar)
{
int iLength;
iLength = MultiByteToWideChar(CP_ACP, 0, _char, strlen(_char) + 1, NULL, 0);
MultiByteToWideChar(CP_ACP, 0, _char, strlen(_char) + 1, tchar, iLength);
}
void TcharToChar(const TCHAR * tchar, char * _char)
{
int iLength;
//获取字节长度
iLength = WideCharToMultiByte(CP_ACP, 0, tchar, -1, NULL, 0, NULL, NULL);
//将tchar值赋给_char
WideCharToMultiByte(CP_ACP, 0, tchar, -1, _char, iLength, NULL, NULL);
}
/*
* wchar转化为char类型
*/
char* WcharToChar(wchar_t* wc)
{
int len = WideCharToMultiByte(CP_ACP, 0, wc, wcslen(wc), NULL, 0, NULL, NULL);
CHAR* m_char = new char[len + 1];
WideCharToMultiByte(CP_ACP, 0, wc, wcslen(wc), m_char, len, NULL, NULL);
m_char[len] = '\0';
return m_char;
}
/*
* char转化为wchar类型
*/
wchar_t* CharToWchar(char* c)
{
int len = MultiByteToWideChar(CP_ACP, 0, c, strlen(c), NULL, 0);
WCHAR* m_wchar = new wchar_t[len + 1];
MultiByteToWideChar(CP_ACP, 0, c, strlen(c), m_wchar, len);
m_wchar[len] = '\0';
return m_wchar;
}
/*
*获取快捷键的方式
*/
TCHAR* Get_run_path_app()
{
TCHAR exeFullPath[MAX_PATH];
CHAR exeFullPathChar[MAX_PATH];
GetModuleFileName(NULL, exeFullPath, MAX_PATH);
TcharToChar(exeFullPath, exeFullPathChar);
std::string exeFullPathStr = exeFullPathChar;
StringReplace(exeFullPathStr,std::string(CUSTOMARY_NAME),std::string(RUN_APP_NAME));
TCHAR tempPath[MAX_PATH];
CharToTchar(exeFullPathStr.c_str(), tempPath);
return tempPath;
}
/*
* regeditPath 要打开的注册表的位置
* regeditKey 要获取的注册表中value的key
*/
bool GetRegeditValueOfKey(WCHAR* regeditPath, WCHAR* regeditKey, std::string& regeditValue, ShortcutsType shortcutsType)
{
/*
*chwDir 用于存储注册表中读出来的Desktop的值
*result 打开注册表返回的结果
*dwType 用于装载取回数据类型的一个变量
*dwSize 用于装载lpData缓冲区长度的一个变量。 一旦返回,它会设为实际装载到缓冲区的字节数
*/
WCHAR chwDir[512];
long result;
HKEY hKey = 0;
DWORD dwType;
DWORD dwSize;
/*
* 在这里注意 KEY_WOW64_64KEY 权限值必须使用否则将查找不到指定的路径,返回2时是找不到注册表路径
* 这里可以根据自己的需求根据自己的需求来修改
*/
if (shortcutsType == ShortcutsTypeDesktop)
{
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regeditPath, NULL, KEY_WOW64_64KEY | KEY_READ, &hKey);
}
else if(shortcutsType == ShortcutsTypeStartMenu)
{
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regeditPath, NULL, KEY_WOW64_64KEY | KEY_READ, &hKey);
}
//result = RegOpenKeyEx(HKEY_CLASSES_ROOT, regeditPath, NULL, KEY_WOW64_64KEY | KEY_READ, &hKey);
if (result == 0)
{
//std::cout << "@@@@@@@@@@@@@@@@@@@@注册表打开成功@@@@@@@@@@@@@@@@@@@@" << endl;
dwSize = 512;
// 查询Desktop的键值并把查询到的结果保存在字符串“chwDir”中(因为Windows并不一定安装在c盘中,所以要查询注册表)
result = RegQueryValueEx(hKey, regeditKey, 0, &dwType, (LPBYTE)chwDir, &dwSize);
if (result == 0)
{
//std::cout << "@@@@@@@@@@@@@@@@@@@@获取value数据成功@@@@@@@@@@@@@@@@@@@@" << endl;
regeditValue.append(WcharToChar(chwDir));
}
else
{
return false;
}
}
else
{
return false;
}
result = RegCloseKey(hKey);
return true;
}
//向开始菜单中加入快捷方式
void YLChangeQuicklyIcom::AddToStartMenu(std::string shortcutDisplay)
{
std::string chsDir;
// 如果要放在用户的自己的桌面下面将"Common Programs"改成"Programs"
bool result = GetRegeditValueOfKey(KEY_PATHS_START_MENU, L"Common Programs", chsDir,ShortcutsTypeStartMenu);
if (result)
{
//chsDir.clear();
chsDir.append("\\");
//chsDir.append(std::string(NOW_APP_GROUP));
//chsDir.append("\\");
chsDir.append(NOW_APP_NAME);
chsDir.append(".lnk");
//在“开始“菜单里建立“Myshortcut”菜单项
HRESULT hres;
/*得到IshellLink接口指针 在这里需要添加shlobj.h文件*/
IShellLink* pShellLink;
/*初始化COM类库*/
CoInitialize(NULL);
hres = CoCreateInstance(CLSID_ShellLink,NULL, CLSCTX_INPROC_SERVER, IID_IShellLink,(LPVOID *)&pShellLink);
/*
*判断是否成功的获取了IShellLink
*/
if (SUCCEEDED(hres))
{
//std::cout << "@@@@@@@@@@@@@@@@@@@@IShellLink获取成功@@@@@@@@@@@@@@@@@@@@" << endl;
IPersistFile* pPf;
// 得到当前运行的可执行程序的全路径名
//GetModuleFileName(NULL, exeFullPath, MAX_PATH);
pShellLink->SetPath(Get_run_path_app());
// 设置快捷方式的路径
pShellLink->SetDescription(L"Yun"); // 设置快捷方式的描述
hres = pShellLink->QueryInterface(IID_IPersistFile, (LPVOID *)&pPf);
if (SUCCEEDED(hres))
{
//std::cout << "@@@@@@@@@@@@@@@@@@@@开始菜单添加成功@@@@@@@@@@@@@@@@@@@@" << endl;
WCHAR sz[MAX_PATH];
// 字符串为ANSI 格式,须转化为Unicode格式
MultiByteToWideChar(CP_ACP, 0, chsDir.c_str(), -1, sz, MAX_PATH);
hres = pPf->Save(sz, TRUE); // 保存链接
pPf->Release();
}
pShellLink->Release();
}
else
{
//std::cout << "@@@@@@@@@@@@@@@@@@@@IShellLink获取失败@@@@@@@@@@@@@@@@@@@@" << endl;
}
}
else
{
//std::cout << "@@@@@@@@@@@@@@@@@@@@注册表打开失败@@@@@@@@@@@@@@@@@@@@" << endl;
//std::cout << result;
}
}
void YLChangeQuicklyIcom::AddToDesktop(std::string shortcutDisplay)
{
//std::cout << "向桌面加入快捷方式" << endl;
std::string chsDir;
// 如果要放在用户的自己的桌面下面将"Common Desktop"改成"Desktop"
bool result = GetRegeditValueOfKey(KEY_PATHS_DESKTOP, L"Common Desktop", chsDir, ShortcutsTypeDesktop);
if (result)
{
chsDir.append("\\");
chsDir.append(std::string(NOW_APP_NAME));
chsDir.append(".lnk");
/*CoCreateInstance的返回值*/
HRESULT hres;
/*得到IshellLink接口指针 在这里需要添加shlobj.h文件*/
IShellLink* pShellLink;
/*初始化COM类库*/
CoInitialize(NULL);
hres = CoCreateInstance(CLSID_ShellLink,NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&pShellLink);
if (SUCCEEDED(hres))
{
/*
* 获得IshellLink接口指针 成功
*/
IPersistFile* pPf;
pShellLink->SetPath(Get_run_path_app());
pShellLink->SetDescription(L"Yun");
hres = pShellLink->QueryInterface(IID_IPersistFile, (LPVOID *)&pPf);
if (SUCCEEDED(hres))
{
//std::cout << "@@@@@@@@@@@@@@@@@@@@桌面菜单添加成功@@@@@@@@@@@@@@@@@@@@" << endl;
WCHAR sz[MAX_PATH];
// 字符串为ANSI 格式,须转化为Unicode格式
MultiByteToWideChar(CP_ACP, 0, chsDir.c_str(), -1, sz, MAX_PATH);
hres = pPf->Save(sz, TRUE); // 保存链接
pPf->Release();
}
pShellLink->Release();
}
}
}
void YLChangeQuicklyIcom::DeleteStartMenu(std::string shortcutDisplay)
{
//std::cout << "删除开始菜单中的快捷方式" << endl;
std::string chsDir;
bool result = GetRegeditValueOfKey(KEY_PATHS_START_MENU, L"Common Programs", chsDir, ShortcutsTypeStartMenu);
if (result)
{
CFileFind findfile;
//WCHAR str[MAX_PATH] = );
SetCurrentDirectory(CharToWchar((char*)chsDir.c_str()));
/*if (!findfile.FindFile(CharToWchar((char*)shortcutDisplay.c_str())))
{
std::cout << "没有文件";
return;
}*/
//strcat(chDir, "//Myshortcut.lnk");
chsDir.append("\\");
//chsDir.append(CUSTOMARY_APP_GROUP);
//chsDir.append("\\");
chsDir.append(CUSTOMARY_APP_NAME);
chsDir.append(".lnk");
char lpDelDir[512];
memset(lpDelDir, 0, 512);
strcat_s(lpDelDir, chsDir.c_str());
//这里直接删除
bool deleteSu = DeleteFile(CharToWchar(lpDelDir));
if (deleteSu)
{
//std::cout << "删除成功";
}
else
{
//std::cout << "删除失败";
}
}
}
void YLChangeQuicklyIcom::DeleteDesktop(std::string shortcutDisplay)
{
//std::cout << "删除桌面的快捷方式" << endl;
std::string chsDir;
bool result = GetRegeditValueOfKey(KEY_PATHS_DESKTOP, L"Common Desktop", chsDir, ShortcutsTypeDesktop);
if (result)
{
CFileFind findfile;
//WCHAR str[MAX_PATH] = );
SetCurrentDirectory(CharToWchar((char*)chsDir.c_str()));
/*if (!findfile.FindFile(CharToWchar((char*)std::string(CUSTOMARY_APP_NAME).c_str())))
{
std::cout << "没有文件";
return;
}*/
//strcat(chDir, "//Myshortcut.lnk");
chsDir.append("\\");
chsDir.append(std::string(CUSTOMARY_APP_NAME));
chsDir.append(".lnk");
char lpDelDir[512];
memset(lpDelDir, 0, 512);
strcat_s(lpDelDir, chsDir.c_str());
//这里直接删除
bool deleteSu = DeleteFile(CharToWchar(lpDelDir));
if (deleteSu)
{
//std::cout << "删除成功";
}
else
{
//std::cout << "删除失败";
}
}
}
代码的例子git上的代码是:https://github.com/IOSYUNYUN/ChangIcon.git,代码需要管理员权限才能实现删除和创建图标。如果修改自己的桌面图标先修改代码GetRegeditValueOfKey的传值。