很多公司的软件安装包不再使用InstallShied、InnoSetup等打包工具,他们自己编写代码去制作安装包,这样就能灵活地控制打包的流程、实现酷炫的UI界面。自制安装包的一个重要的一项就是创建桌面和开始菜单的快捷方式,这其中可能会产生一些bug,我们都遇到过,在此将这块的内容分享给大家。
创建快捷方式的代码是通用的,不管是创建桌面快捷方式,还是创建开始菜单快捷方式,都要调用下面封装的接口CreateFileShortcut:(该接口中主要使用前两个参数lpszTargetFilePath和lpszLnkPath,后面几个参数基本不用的)
/**
* 创建快捷方式
*
* @param lpszTargetFilePath 指定快捷方式指向的实际文件路径
* @param lpszLnkPath 快捷方式文件本身的完整路径
* @param wHotkey 为0表示不设置快捷键
* @param lpszDescription 备注
* @param iShowCmd 显示方式
* @return TRUE:成功,FALSE:失败
*/
BOOL CreateFileShortcut( LPCTSTR lpszTargetFilePath, LPCTSTR lpszLnkPath, WORD wHotkey,
LPCTSTR lpszDescription, int iShowCmd )
{
if ( lpszTargetFilePath == NULL || lpszLnkPath == NULL )
{
return FALSE;
}
HRESULT hr;
IShellLink *pLink; // IShellLink对象指针
IPersistFile *ppf; // IPersisFil对象指针
//::CoInitialize( NULL );
// 创建IShellLink对象
hr = CoCreateInstance( CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)&pLink );
if ( FAILED( hr ) )
{
//::CoUninitialize()
return FALSE;
}
// 从IShellLink对象中获取IPersistFile接口
hr = pLink->QueryInterface( IID_IPersistFile, (void**)&ppf );
if ( FAILED( hr ) )
{
pLink->Release();
//::CoUninitialize()
return FALSE;
}
// 快捷方式指向的实际文件路径
if ( lpszTargetFilePath != NULL )
{
CString strWorkDirectry = lpszTargetFilePath;
strWorkDirectry = strWorkDirectry.Left( strWorkDirectry.ReverseFind( _T('\\') ) );
pLink->SetPath( lpszTargetFilePath );
pLink->SetWorkingDirectory( strWorkDirectry );
// 解决快捷图标不刷新、更改图标时找不到文件的问题
// 快捷方式文件的图标就是其指向的exe文件的图标
pLink->SetIconLocation( lpszTargetFilePath, 0 );
IShellLinkDataList *pShellLinkDataList = NULL;
hr = pLink->QueryInterface( IID_IShellLinkDataList, (void**)&pShellLinkDataList );
if ( FAILED(hr) )
{
ppf->Release();
pLink->Release();
return FALSE;
}
// 避免64系统中图标路径被解析成环境变量,导致快捷方式图标异常
// 这个地方的代码是用API Monitor工具探测互联网大厂软件的函数调用记录添加上来的
DWORD dwFlags = SLDF_DEFAULT;
pShellLinkDataList->GetFlags( &dwFlags );
if ( (dwFlags & SLDF_HAS_EXP_ICON_SZ) == SLDF_HAS_EXP_ICON_SZ )
{
pShellLinkDataList->SetFlags( SLDF_DEFAULT );
pShellLinkDataList->RemoveDataBlock( EXP_SZ_ICON_SIG );
}
pShellLinkDataList->Release();
}
// 设置快捷键
if ( wHotkey != 0 )
{
pLink->SetHotkey( wHotkey );
}
// 设置备注
if ( lpszDescription != NULL )
{
pLink->SetDescription( lpszDescription );
}
// 显示方式
pLink->SetShowCmd( iShowCmd );
// 保存快捷方式到指定目录下
// 要将路径字符串转化成宽字节字符串,COM接口ppf->Save要传入宽字符
BOOL bUnicode = FALSE;
WCHAR* pwTemp = NULL;
#ifdef _UNICODE
pwTemp = (LPTSTR)lpszLnkPath;
bUnicode = TRUE;
#else
int nLen = MultiByteToWideChar( CP_ACP, 0, lpszLnkPath, -1, NULL, 0 );
pwTemp = new WCHAR[nLen+1];
memset( pwTemp, 0, (nLen+1)*sizeof(WCHAR) );
MultiByteToWideChar( CP_ACP, 0, lpszLnkPath, -1, pwTemp, nLen+1, NULL, NULL );
#endif
hr = ppf->Save( pwTemp, TRUE );
ppf->Release();
pLink->Release();
if ( !bUnicode && pwTemp != NULL )
{
delete []pwTemp;
pwTemp = NULL;
}
//::CoUninitialize();
return SUCCEEDED( hr );
}
下面是在公共桌面中创建快捷方式:
// 得到公共用户桌面
BOOL GetPublicUserDesktopPath( TCHAR *pszDesktopPath )
{
LPITEMIDLIST ppidl = NULL;
if ( SHGetSpecialFolderLocation( NULL, CSIDL_COMMON_DESKTOPDIRECTORY, &ppidl ) == S_OK )
{
BOOL flag = SHGetPathFromIDList( ppidl, pszDesktopPath );
CoTaskMemFree( ppidl );
return flag;
}
return FALSE;
}
// 在公共用户桌面上创建快捷方式
void CreateDesktopShortCut()
{
::CoInitialize( NULL );
TCHAR achDesktopDir[MAX_PATH] = { 0 };
BOOL bRet = GetPublicUserDesktopPath( achDesktopDir );
if ( !bRet )
{
::CoUninitialize();
return;
}
CString strTargetExePath; // 快捷方式指向的exe路径
CString strLnkPath; // 快捷方式文件本省的路径
// ... // 路径获取部分省略
CreateFileShortcut( strTargetExePath, strLnkPath, 0, NULL );
::CoUninitialize();
}
// 得到 开始->程序组 的路径
BOOL CProcessLogic::GetProgramsPath( TCHAR *pszProgramsPath )
{
LPITEMIDLIST ppidl;
if ( SHGetSpecialFolderLocation( NULL, CSIDL_COMMON_PROGRAMS, &ppidl ) == S_OK )
{
BOOL flag = SHGetPathFromIDList( ppidl, pszProgramsPath );
CoTaskMemFree( ppidl );
return flag;
}
return FALSE;
}
void CreateStartMenuShortCut()
{
::CoInitialize( NULL );
TCHAR achProgramsDir[MAX_PATH] = { 0 };
BOOL bRet = GetProgramsPath( achProgramsDir );
if ( !bRet )
{
::CoUninitialize();
return;
}
// 创建文件夹:C:\Documents and Settings\All Users\「开始」菜单\程序1\
CString strTLLnkDir = _T("程序1");
CreateDirectory( strTLLnkDir, NULL ); // 创建程序1文件夹
// 创建 程序1\程序1.lnk
CString strTargetPath; // 快捷方式指向的exe路径
CString strLnkPath;// 快捷方式文件本省的路径
// ... // 路径获取部分省略
CreateFileShortcut( strTargetPath, strLnkPath, 0, NULL );
::CoUninitialize();
}