这两天想做一下windows系统下图标的修改,让程序有更新的时候能够更新图标的外观,达到提醒的作用,360,QQ经常采用这种方式进行更新的提示,也有采用弹框的方式来提示,用新版QVOD的同事可能见到过类似的。废话不说,我的思路是:
(1)检测程序更新
(2)检测windows桌面图标中是够含有需要的,如果没有则需要创建,如果有则准备更新图标
(3)根据跟新的内容,程序自己创建新的图像,修改快捷方式的图标
这种方法需要开机之后,存在一个系统进程来检测这种更新,360就是这样干的~~还有一个弊端是程序需要检测桌面快捷方式的信息来判断是不是自己想去修改的,OK ,这个问题其实可以通过LNK文件的属性,应用信息,名字等问题来解决。 下面是实现过程
1 ICON文件的操作
ICON文件的格式和BMP文件的格式的类似,不过增加了层数的东西,一个ICON文件中可能有几份数据,不同的分辨率,ICON文件的格式主要定义如下:
//ICON文件存储格式
typedef struct
{
byte bwidth; // width, in pixels, of the image
byte bheight; // height, in pixels, of the image
byte bcolorcount; // number of colors in image (0 if >=8bpp)
byte breserved; // reserved ( must be 0)
WORD wplanes; // color planes
WORD wbitcount; // bits per pixel
DWORD dwbytesinres; // how many bytes in this resource?
DWORD dwimageoffset; // where in the file is this image?
} ICONDIRENTRY, *LPICONDIRENTRY;
//ICON文件目录
typedef struct
{
WORD idReserved; // Reserved
WORD idType; // resource type (1 for icons)
WORD idCount; // how many images?
ICONDIRENTRY idEntries[1]; // the entries for each image
} ICONDIR, *LPICONDIR;
//ICON文件数据存储格式
typedef struct
{
BITMAPINFOHEADER icheader; // dib header
RGBQUAD iccolors[1]; // color table
byte icxor[1]; // dib bits for xor mask
byte icand[1]; // dib bits for and mask
} iconimage, *lpiconimage;
,
//图像数据存储
struct stImageData
{
BITMAPINFOHEADER icheader; // ptr to header
byte* icxor ;
byte* icand ;
stImageData()
{
icand = NULL ;
icxor = NULL ;
}
};
//ICON图像整体组成
struct stIconData
{
int num ;
stImageData data[1];
stIconData()
{
num = 0 ;
}
};
struct stIcon
{
ICONDIR *dir ;
stIconData *icon ;
stIcon()
{
dir = NULL ;
icon = NULL ;
}
};
bool CIconOperate::ReadIcon(const string _str)
{
fstream fin ;
fin.open(_str.c_str(),ios_base::binary | ios_base::in);
if (!fin.is_open())
{
return false ;
}
ICONDIR dirTemp ;
//reserved
fin.read((char*)(&dirTemp.idReserved),sizeof(WORD));
//type
fin.read((char*)(&dirTemp.idType),sizeof(WORD));
//num
fin.read((char*)(&dirTemp.idCount),sizeof(WORD));
//image data header
byte *pBuffer = new byte[3 * sizeof(WORD) + dirTemp.idCount * sizeof(ICONDIRENTRY)] ; // 变长数据操作
m_IconDir = (ICONDIR *)(pBuffer);
m_IconDir->idCount = dirTemp.idCount ;
m_IconDir->idReserved = dirTemp.idReserved ;
m_IconDir->idType = dirTemp.idType ;
for (int i = 0 ; i < dirTemp.idCount ; ++i)
{
fin.read((char*)(&m_IconDir->idEntries[i]),sizeof(ICONDIRENTRY));
}
//img data
byte *pBufferData = new byte[sizeof(int) + dirTemp.idCount * sizeof(stImageData)];
m_IconData = (stIconData*)pBufferData ;
m_IconData->num = m_IconDir->idCount ;
for (int i = 0 ; i < m_IconDir->idCount ; ++ i)
{
//SEEK
fin.seekg(m_IconDir->idEntries[i].dwimageoffset,ios_base::beg) ;
//READ BITMAPINFOHEADER
fin.read((char*)(&m_IconData->data[i].icheader),sizeof(BITMAPINFOHEADER));
//READ XOR DATA
int xornum = WIDTHBYTES(m_IconDir->idEntries[i].bwidth,m_IconDir->idEntries[i].wbitcount)
* m_IconDir->idEntries[i].bheight;
int andnum = WIDTHBYTES(m_IconDir->idEntries[i].bwidth,1)
* m_IconDir->idEntries[i].bheight;
m_IconData->data[i].icxor = new byte[xornum];
fin.read((char*)(m_IconData->data[i].icxor),xornum);
//READ AND DATA
m_IconData->data[i].icand = new byte[andnum];
fin.read((char*)(m_IconData->data[i].icand),andnum);
}
fin.close();
return true ;
}
写数据
bool CIconOperate::SaveIcon(const string _str)
{
if (m_IconDir == NULL || m_IconData == NULL)
return false ;
//int width = 64 ;
//int heigth = 64 ;
//int ppx = 32 ;
fstream fout;
fout.open(_str.c_str(),ios_base::out | ios_base::binary);
//reserved
WORD wData = 0 ;
fout.write((char*)(&wData),sizeof(WORD));
//type
wData = 1 ;
fout.write((char*)(&wData),sizeof(WORD));
//num
wData = m_IconDir->idCount ;
fout.write((char*)(&wData),sizeof(WORD));
//write ICONDIRENTRY数据
for (int i = 0 ; i < m_IconDir->idCount ; ++i)
{
//ICONDIRENTRY结构
ICONDIRENTRY iconData ;
iconData.bwidth = m_IconDir->idEntries[i].bwidth ;
iconData.bheight = m_IconDir->idEntries[i].bheight ;
iconData.bcolorcount = 0;
iconData.breserved = 0 ;
iconData.wplanes = 1 ;
iconData.wbitcount = m_IconDir->idEntries[i].wbitcount ;
iconData.dwbytesinres = sizeof(BITMAPINFOHEADER)
+ iconData.bheight * WIDTHBYTES(iconData.bwidth,iconData.wbitcount)
+ iconData.bheight * WIDTHBYTES(iconData.bwidth,1);
iconData.dwimageoffset = CalculateImageOffset(i) ;
fout.write((char*)(&iconData),sizeof(ICONDIRENTRY));
}
for (int i = 0 ; i < m_IconDir->idCount ; ++i)
{
//BITMAPINFOHEADER结构
BITMAPINFOHEADER tBitHeader;
tBitHeader.biSize = sizeof(BITMAPINFOHEADER);
tBitHeader.biWidth = m_IconDir->idEntries[i].bwidth ;
tBitHeader.biHeight = m_IconDir->idEntries[i].bheight*2 ;
tBitHeader.biPlanes = 1 ;
tBitHeader.biBitCount = m_IconDir->idEntries[i].wbitcount ;
tBitHeader.biCompression = 0 ;
tBitHeader.biSizeImage = 0;
tBitHeader.biXPelsPerMeter = 0 ;
tBitHeader.biYPelsPerMeter = 0 ;
tBitHeader.biClrUsed = 0 ;
tBitHeader.biClrImportant = 0 ;
fout.write((char*)(&tBitHeader),sizeof(BITMAPINFOHEADER));
fout.write((char*)m_IconData->data[i].icxor,tBitHeader.biHeight/2 * WIDTHBYTES(tBitHeader.biWidth,tBitHeader.biBitCount));
fout.write((char*)m_IconData->data[i].icand,tBitHeader.biHeight/2 * WIDTHBYTES(tBitHeader.biWidth,1));
}
fout.close();
return true ;
}
,
vector<string>* CFileSearch::SearchLinkByName(const CString& _str)
{
m_FindList.clear();
m_strFind = _str ;
GetPath(m_strDeskTopPath,m_strQuickLanchPath);
if(GetDesktopIShellFolder())
{
GetIEunmIDList(m_pIShellFolder,FALSE,m_mode);
}
fprintf(fp,"\n\n\n\n");
for (int i = 0 ; i < m_FindList.size() ; i++)
{
fprintf(fp,"%s\n",m_FindList[i].c_str());
}
return &m_FindList ;
}
//获取桌面文件夹和快速启动文件夹的路径
int CFileSearch::GetPath(char *DeskTop, char* AppData)
{
CRegKey m_reg;
if(ERROR_SUCCESS==m_reg.Open(HKEY_CURRENT_USER,"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",KEY_READ))
{
ULONG m_ulsize1=1000;
ULONG m_ulsize2=1000;
m_reg.QueryStringValue("Desktop",DeskTop,&m_ulsize1);
m_reg.QueryStringValue("AppData",AppData,&m_ulsize2);
lstrcat(AppData,"\\Microsoft\\Internet Explorer\\Quick Launch");
}
return 0;
}
//获取桌面文件夹的IShellFolder接口指针
BOOL CFileSearch::GetDesktopIShellFolder()
{
m_pIShellFolder=GetIShellFolderByPath(m_strDeskTopPath);
//m_pAppData=GetIShellFolderByPath(m_strQuickLanchPath);
return TRUE;
}
//获取桌面文件夹的IEnumIDList接口
//isQuickLanch---是否搜索第二层文件夹
//isRecur---是否是文件夹
BOOL CFileSearch::GetIEunmIDList(IShellFolder * pShellFolder,BOOL isRecur,BOOL isQuickLanch)
{
IEnumIDList *pIEnumFile = NULL ;
IEnumIDList *pIEnumFolder = NULL ;
if(!pShellFolder)
return FALSE;
HRESULT hr=pShellFolder->EnumObjects(0,SHCONTF_NONFOLDERS,&pIEnumFile); //不是文件夹
if(FAILED(hr))
{
return FALSE;
}
if(!isRecur)
{
m_pFirstLayerFile = pIEnumFile;
}
EnumAllItems(pIEnumFile,FALSE,isQuickLanch);
if(!isQuickLanch)
{
HRESULT hr=pShellFolder->EnumObjects(0,SHCONTF_FOLDERS,&pIEnumFolder); //文件夹
if(FAILED(hr))
{
return FALSE;
}
if(!isRecur)
{
m_pFirstLayerFolder = pIEnumFolder;
}
EnumAllItems(pIEnumFolder,TRUE,isQuickLanch);
}
return TRUE;
}
BOOL CFileSearch::EnumAllItems(IEnumIDList *m_pEnum,BOOL isFolder,BOOL isQuickLanch)
{
LPITEMIDLIST m_pItem=NULL;
ULONG m_ulwork= 0;
while(m_pEnum->Next(1,&m_pItem,&m_ulwork)==S_OK)
{
//如果是第一层,重置路径
if(!isQuickLanch)
{
if((m_pFirstLayerFolder==m_pEnum) && (isFolder))
{
lstrcpy(m_strParentPath,m_strDeskTopPath);
}
if((m_pFirstLayerFile==m_pEnum) && (!isFolder))
{
lstrcpy(m_strParentPath,m_strDeskTopPath);
}
}
else
{
// if((m_pFirstLayerFile==m_pEnum) && (!isFolder))
// {
// lstrcpy(m_strParentPath,m_strQuickLanchPath);
// }
if((m_pFirstLayerFolder==m_pEnum) && (isFolder))
{
lstrcpy(m_strParentPath,m_strDeskTopPath);
}
if((m_pFirstLayerFile==m_pEnum) && (!isFolder))
{
lstrcpy(m_strParentPath,m_strDeskTopPath);
}
}
WIN32_FIND_DATA ffd;
SHGetDataFromIDList(m_pIShellFolder,m_pItem,SHGDFIL_FINDDATA,&ffd,sizeof(WIN32_FIND_DATA));
if(!isFolder)
{
CString m_strTempPath=m_strParentPath;
m_strTempPath+="\\";
m_strTempPath += ffd.cFileName;
CString m_strCmp=".lnk";
fprintf(fp,"%s\n",m_strTempPath.GetBuffer());
m_strTempPath.MakeUpper();
m_strCmp.MakeUpper();
if(m_strTempPath.Right(4)==m_strCmp) //判断是否是链接文件
{
if (ReadShortCut(m_strTempPath.GetBuffer()))
{
string str = m_strTempPath.GetBuffer();
m_FindList.push_back(str);
}
m_strTempPath.ReleaseBuffer();
}
}
else
{
lstrcat(m_strParentPath,"\\");
lstrcat(m_strParentPath,ffd.cFileName);
IShellFolder *m_pITemp=GetIShellFolderByPath(m_strParentPath);
GetIEunmIDList(m_pITemp,TRUE,isQuickLanch);
}
}
return TRUE;
}
//获取指定目录的IShellFolder接口
IShellFolder *CFileSearch::GetIShellFolderByPath(LPTSTR path)
{
IShellFolder *m_ShellFolderTopMost=NULL;
HRESULT hr=SHGetDesktopFolder(&m_ShellFolderTopMost);
if(FAILED(hr))
{
return NULL;
}
IShellFolder *m_pFolder;
LPITEMIDLIST pidlWorkDir=NULL;
OLECHAR strOleFilePath[MAX_PATH];
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, path, -1, strOleFilePath, MAX_PATH );
hr = m_ShellFolderTopMost->ParseDisplayName(NULL,NULL,strOleFilePath,NULL,&pidlWorkDir,NULL);
if(FAILED(hr))
{
return NULL;
}
hr=m_ShellFolderTopMost->BindToObject(pidlWorkDir,NULL,IID_IShellFolder,(LPVOID*)&m_pFolder);
if(m_ShellFolderTopMost)m_ShellFolderTopMost->Release();
return m_pFolder;
}
//读取快捷方式的信息
BOOL CFileSearch::ReadShortCut(LPTSTR linkName)
{
::CoInitialize(NULL);
IShellLink *m_pIShellLink=NULL;
IPersistFile *m_pIPersistFile=NULL;
HRESULT hr=::CoCreateInstance(CLSID_ShellLink,NULL,CLSCTX_INPROC_SERVER,IID_IShellLink,(LPVOID*)&m_pIShellLink);
if(hr==S_OK)
{
hr=m_pIShellLink->QueryInterface(IID_IPersistFile,(void **)&m_pIPersistFile);
if(hr==S_OK)
{
USES_CONVERSION;
m_pIPersistFile->Load(T2COLE(linkName),STGM_READWRITE);
char m_strPath[MAX_PATH]={0};
m_pIShellLink->GetPath(m_strPath,MAX_PATH,NULL,SLGP_UNCPRIORITY);
CString temp = m_strPath;
temp.MakeUpper();
m_strFind.MakeUpper();
if (strstr(temp.GetBuffer(),m_strFind.GetBuffer())) //判断应用程序名
{
//AfxMessageBox(temp);
if(m_pIShellLink)
m_pIShellLink->Release();
if(m_pIPersistFile)
m_pIPersistFile->Release();
::CoUninitialize();
return TRUE;
}
}
}
if(m_pIShellLink)
m_pIShellLink->Release();
if(m_pIPersistFile)
m_pIPersistFile->Release();
::CoUninitialize();
return FALSE;
bool CModifyLinkICon::ChangeLinkIcon(const string& strLnkName,const string& strIconPath)
{
if (strLnkName.empty() || strIconPath.empty())
{
return false;
}
HRESULT hres;
IShellLink *psl = NULL;
IPersistFile *pPf = NULL;
int id;
LPITEMIDLIST pidl;
bool bRet = false;
do
{
hres = CoInitialize(NULL);
if (FAILED(hres))
{
break;
}
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (FAILED(hres))
{
break;
}
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&pPf);
if (FAILED(hres))
{
break;
}
wchar_t wsz[256];
MultiByteToWideChar(CP_ACP, 0, strLnkName.c_str(), -1, wsz, MAX_PATH);
hres = pPf->Load(wsz, STGM_READWRITE);
if (FAILED(hres))
{
break;
}
hres = psl->SetIconLocation(strIconPath.c_str(), 0);
if (FAILED(hres))
{
break;
}
pPf->Save(wsz, TRUE);
if (FAILED(hres))
{
break;
}
bRet = true;
} while (0);
if (pPf != NULL)
{
pPf->Release();
}
if (psl != NULL)
{
psl->Release();
}
CoUninitialize();
AfxMessageBox("替换完成");
return bRet;
}
4结论
其实这些操作估计很早之前就有,网上的信息页很多,我就算做一下汇总吧,最后给出我测试用的界面,很简答,可以实现快捷的搜索,快捷图标的更换,图像的读取,写入以及简单的修改。