通过手工操作建立这些菜单项或快捷方式并不复杂,在一般Windows使用手册中都有介绍,相信大家都很熟悉,在此不再赘述。笔者在有关资料的基础上,通过实践摸索,找到了在应用程序中完成上述工作的方法。这正是开发安装程序所必需的。
Windows的快捷方式实际上是一个带有扩展名LNK的数据文件,其中包含有用于访问Windows某一对象(即在资源管理器中所能浏览的所有对象,包括文件,文件夹,驱动器及打印机等)的有关信息,如目标对象的路径和名称,工作目录,要传递的命令行参数,运行时的初始显示状态,图标及其快捷键等。通过在快捷方式上单击鼠标右键并在弹出菜单中选择“属性”可以观察该快捷方式的这些性质。
快捷方式的数据文件如果存放在C:/Windows/Desktop子目录下,这个快捷方式就会显示在桌面上,而如果存放在C:/Windows/”Start Menu”/Programs子目录下,这个快捷方式就会作为“开始”菜单的一个菜单项出现。而桌面上的文件夹和“开始”菜单的菜单组则是上述两个子目录下的子目录的表现。
Windows外壳(Shell)的快捷方式是以OLE技术的组件对象模型COM(Component ObjectModal)为基础而设计的。利用COM模型,一个应用程序可以调用另一应用程序的某些功能。这方面的技术细节请参阅有关文献。
创建Windows的快捷方式比较容易。首先利用OLE通过调用CoCreateInstance()函数建立一个IID_IShellLink实例,并同时得到其接口指针。利用这个接口指针可以对其各项属性进行设置。为了使这些信息以快捷方式的数据文件(*.lnk)格式保存起来,还需要从IID_IShellLink对象取得其IID_IPersistFile接口指针,以便于调用其成员函数Save()保存前面设置的信息。
至于如何删除快捷方式以及创建和删除文件夹,则只需要简单地调用文件操作函数SHFileOperation()就可以了。另外应该注意,在完成上述操作之后,都要调用SHChangeNotify()函数通知Windows外壳有关变化以使之及时更新其显示状态。
该示例程序为一个基于对话框的应用程序,两个圆形按钮用于设置要创建/删除的文件夹或快捷方式的位置,下面的四个按钮则用于执行不同的操作。另外,该程序还需要一个简单的对话框,用于输入要创建的文件夹或快捷方式的名称。
// SortCut.cpp :
BOOL CSortCutApp::InitInstance()
{
......
CoInitialize (NULL);
CSortCutDlg dlg;
m_pMainWnd = &dlg;
......
CoUninitialize ();
return FALSE;
}
// SortCutDlg.cpp :
#include "stdafx.h"
#include "SortCut.h"
#include "SortCutDlg.h"
#include "NameDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//起始文件夹的PIDL
int nBeginAt=CSIDL_DESKTOPDIRECTORY;
......
//浏览文件夹
BOOL BrowseForFolder(
LPITEMIDLIST pidlRoot,//浏览开始处的PIDL
LPITEMIDLIST *ppidlDestination,
//浏览结束时所选择的PIDL
LPCSTR lpszTitle)//浏览对话框中的提示文字
{
BROWSEINFO BrInfo ;
ZeroMemory( &BrInfo, sizeof(BrInfo)) ;
BrInfo.hwndOwner = HWND_DESKTOP ;
BrInfo.pidlRoot = pidlRoot ;
BrInfo.lpszTitle = lpszTitle ;
//浏览文件夹
*ppidlDestination= SHBrowseForFolder(&BrInfo);
//用户选择了取消按钮
if(NULL == *ppidlDestination) return FALSE ;
return TRUE ;
}
//取得快捷方式的目标应用程序名
SelectMenuItem( LPSTR szFileName)
{
OPENFILENAME ofn ;
static CHAR szFilter[] = "Programs/0*.exe/0" ;
ZeroMemory(&ofn, sizeof( OPENFILENAME)) ;
ofn.lStructSize = sizeof( OPENFILENAME) ;
ofn.hwndOwner = HWND_DESKTOP;
ofn.lpstrFilter = szFilter ;
ofn.nFilterIndex = 0 ;
ofn.nMaxFile = MAX_PATH ;
ofn.lpstrTitle = "请选择目标应用程序:" ;
ofn.lpstrFile = szFileName ;
ofn.Flags = OFN_FILEMUSTEXIST |
OFN_PATHMUSTEXIST | OFN_EXPLORER ;
//文件浏览
if(!GetOpenFileName( &ofn))//选择了取消按钮
return FALSE ;
return TRUE ;
}
//取得要创建的快捷方式的名字
BOOL GetShortcutCrt(LPSTR szPath)
{
LPITEMIDLIST pidlBeginAt, pidlDestination ;
// 取得开始菜单或桌面的PIDL
SHGetSpecialFolderLocation( HWND_DESKTOP,
nBeginAt, &pidlBeginAt) ;
// 取得要创建的快捷方式所在的位置
if( !BrowseForFolder(pidlBeginAt,&pidlDestination,"请选择快捷方式所在的位置:"))
return FALSE ;
// 把PIDL转换为路径名
SHGetPathFromIDList( pidlDestination, szPath) ;
// 取得快捷方式名称
CNameDlg name_dlg;
if(name_dlg.DoModal() == IDCANCEL)
return FALSE ;
//把快捷方式名和扩展名.LNK添加到路径名后
//形成完整的快捷方式数据文件名
wsprintf(szPath+lstrlen(szPath),"//%s.lnk",
name_dlg.m_strName) ;
return TRUE ;
}
//创建快捷方式
BOOL CreateLink (
LPSTR szPath,//快捷方式的目标应用程序名
LPSTR szLink)//快捷方式的数据文件名(*.lnk)
{
HRESULT hres ;
IShellLink * psl ;
IPersistFile* ppf ;
WORD wsz[ MAX_PATH] ;
//创建一个IShellLink实例
hres = CoCreateInstance( CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
if( FAILED( hres)) return FALSE ;
//设置目标应用程序
psl -> SetPath( szPath) ;
//设置快捷键(此处设为Shift+Ctrl+'R')
psl -> SetHotkey( MAKEWORD( 'R',
HOTKEYF_SHIFT |HOTKEYF_CONTROL)) ;
//从IShellLink获取其IPersistFile接口
//用于保存快捷方式的数据文件 (*.lnk)
hres = psl -> QueryInterface( IID_IPersistFile,
(void**)&ppf) ;
if( FAILED( hres)) return FALSE ;
// 确保数据文件名为ANSI格式
MultiByteToWideChar( CP_ACP, 0, szLink, -1,
wsz, MAX_PATH) ;
//调用IPersist:Save
//保存快捷方式的数据文件 (*.lnk)
hres = ppf -> Save( wsz, STGM_READWRITE) ;
//释放IPersistFile和IShellLink接口
ppf -> Release( ) ;
psl -> Release( ) ;
return TRUE;
}
//删除文件夹
BOOL DeleteFolder( LPSTR pszFolder)
{
SHFILEOPSTRUCT fos ;
ZeroMemory( &fos, sizeof( fos)) ;
fos.hwnd = HWND_DESKTOP;
fos.wFunc = FO_DELETE ;
fos.fFlags = FOF_SILENT | FOF_ALLOWUNDO ;
fos.pFrom = pszFolder ;
// 删除文件夹及其内容
if( 0 != SHFileOperation( &fos)) return FALSE ;
} // 获取当前路径
TCHAR szPath [MAX_PATH] = _T("") ;
GetModuleFileName( NULL, szPath, MAX_PATH ) ;
PathRemoveFileSpec(szPath) ;
m_strCurPath = szPath ;TCHAR szDirFile[MAX_PATH]= _T("") ;
TCHAR szDirDesktop[MAX_PATH]= _T("") ;
SHGetSpecialFolderPath(NULL,szDirFile,CSIDL_PERSONAL,false); //得到我的文件的路径
m_SpecialFolderPath=szDirFile;
m_DirFlag=GetFileAttributes(m_SpecialFolderPath+"//MTDS_IMAGES");//文件夹是否存在
if(m_DirFlag ==0xFFFFFFFF)//文件夹不存在
CreateDirectory(m_SpecialFolderPath+"//MTDS_IMAGES",NULL);//创建快捷方式------少了个判断文件是否创建成功,判断创建实例是否成功
CString m_szDirDesktop;
WORD wsz[MAX_PATH];
SHGetSpecialFolderPath(NULL,szDirDesktop,CSIDL_DESKTOPDIRECTORY,false);//获得Desktop的目录
m_szDirDesktop=szDirDesktop;
IShellLink* psl ;
IPersistFile* ppf;
//创建一个IShellLink实例
// if(SUCCEEDED(CoCreateInstance(CLSID_ShellLink, NULL,CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl)));
CoCreateInstance(CLSID_ShellLink, NULL,CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
//设置目标应用程序
psl->SetPath(m_SpecialFolderPath+"//MTDS_IMAGES");
//从IShellLink获取其IPersistFile接口
//用于保存快捷方式的数据文件 (*.lnk)
// if(SUCCEEDED(psl -> QueryInterface( IID_IPersistFile,(void**)&ppf) ));
psl -> QueryInterface( IID_IPersistFile,(void**)&ppf);
// 确保数据文件名为ANSI格式
MultiByteToWideChar( CP_ACP, 0, m_szDirDesktop+"//MTDS_IMAGE.lnk", -1,wsz, MAX_PATH);
//调用IPersist:Save
//保存快捷方式的数据文件 (*.lnk)
ppf -> Save( wsz, STGM_READWRITE) ;
ppf -> Release( ) ;
psl -> Release( ) ;
m_bDefaultSkin = !PathFileExists( GetFilePath(FILE_SKIN)) ;
}
使用API函数SHGetSpecialFolderLocation。shlobj.h里有SHGetSpecialFolderLocation的原型声明。这个函数可以帮我们找到Windows的桌面目录、启动目录、我的文档目录等。
SHGetSpecialFolder需要三个参数。 第一个参数是HWND,它指定了"所有者窗口":在调用这个函数时可能出现的对话框或消息框。第二个参数是一个整数id,决定哪个目录是待查找目录,它的取值可能是:
CSIDL_BITBUCKET 回收站
CSIDL_CONTROLS 控制面板
CSIDL_DESKTOP Windows 桌面Desktop
CSIDL_DESKTOPDIRECTORY Desktop的目录
CSIDL_DRIVES 我的电脑
CSIDL_FONTS 字体目录
CSIDL_NETHOOD 网上邻居
CSIDL_NETWORK 网上邻居虚拟目录
CSIDL_PERSONAL 我的文档
CSIDL_PRINTERS 打印机
CSIDL_PROGRAMS 程序组
CSIDL_RECENT 最近打开的文档
CSIDL_SENDTO “发送到”菜单项
CSIDL_STARTMENU 任务条启动菜单项
CSIDL_STARTUP 启动目录
CSIDL_TEMPLATES 文档模板 Following sample code creates a shortcut and sets the shortcut's icon to an icon contained in shell32.dll:
/*PARAMETERS
fname_to_create_link = (e.g.) "c://mytextfile.txt"
lnk_fname = (e.g.) "yourname.lnk"
*/
void CreateLinkThenChangeIcon(LPTSTR fname_to_create_link,
LPTSTR lnk_fname)
{
HRESULT hres;
IShellLink *psl = NULL;
IPersistFile *pPf = NULL;
WORD wsz[256];
TCHAR buf[256];
int id;
LPITEMIDLIST pidl;
hres = CoCreateInstance( CLSID_ShellLink,
NULL,
CLSCTX_INPROC_SERVER,
IID_IShellLink,
(LPVOID*)&psl);
if(FAILED(hres))
goto cleanup;
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&pPf);
if(FAILED(hres))
goto cleanup;
hres = psl->SetPath(fname_to_create_link);
if(FAILED(hres))
goto cleanup;
//place the shortcut on the desktop
SHGetSpecialFolderLocation(hwnd, CSIDL_DESKTOP, >pidl);
SHGetPathFromIDList(pidl, buf);
lstrcat(buf,"//");
lstrcat(buf,lnk_fname);
MultiByteToWideChar(CP_ACP, 0, buf, -1, wsz, MAX_PATH);
hres = pPf->Save(wsz, TRUE);
if(FAILED(hres))
goto cleanup;
GetSystemDirectory(buf, 256);
lstrcat(buf,"//shell32.dll");
hres = psl->SetIconLocation(buf, 1);
if(FAILED(hres))
goto cleanup;
hres = psl->GetIconLocation(buf, 256, &id);
if(FAILED(hres))
goto cleanup;
pPf-&Save(wsz, TRUE);
cleanup:
if(pPf)
pPf->Release();
if(psl)
psl->Release();
}
It works! //
1、快捷方式的实质
Windows的快捷方式实际上是一个带有扩展名LNK的数据文件,其中包含有用于访问Windows某一对象(即在资源管理器中所能浏览的所有对象,包括文件,文件夹,驱动器及打印机等)的有关信息,如目标对象的路径和名称,工作目录,要传递的命令行参数,运行时的初始显示状态,图标及其快捷键等。通过在快捷方式上单击鼠标右键并在弹出菜单中选择“属性”可以观察该快捷方式的这些性质。
2、编程思想
Windows外壳(Shell)的快捷方式是以OLE技术的组件对象模型COM(Component ObjectModal)为基础而设计的。利用COM模型,一个应用程序可以调用另一应用程序的某些功能。
Windows提供了一个COM接口IshellLink,它能够帮助我们创建、修改、删除快捷方式。要使用COM接口提供的功能,必须首先调用CoCreateInstance()函数建立一个IID_IShellLink实例,并同时得到其接口指针。利用这个接口指针可以对其各项属性进行设置。创建实例后,通过调用SetPath()方法设置快捷方式指向的源程序的路径,快捷方式的存储位置应该是:“程序”的位置+程序组名称+快捷方式名称+扩展名.lnk;通过SetDescription()方法设置快捷方式的描述,当然你还可以调用比如SetHotKey()等方法设置热键、显示的图标(默认为实际程序的图标)等。为了使这些信息以快捷方式的数据文件(*.lnk)格式保存起来,还需要从IID_IShellLink对象取得其IID_IPersistFile接口指针,以便于调用其成员函数Save()保存前面设置的信息。
需要注意的是:快捷方式应该使用Unicode存储,所以必须调用wcscpy()函数将其转换。
//Create
BOOL CMainDlg::CreateLink(LPCSTR pszShortcutFile,/*源*/
LPCSTR pszLink, /*lnk名*/
LPCSTR pszDesc/*描述*/)
{
HRESULT hres;
CComPtr<IShellLink> psl;
CComPtr<IPersistFile> ppf;hres = CoCreateInstance (CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
IID_IShellLink, (void **)&psl);
if (FAILED(hres))
{
return FALSE;
}
hres = psl->QueryInterface (IID_IPersistFile, (void **)&ppf);
if (FAILED(hres))
{
return FALSE;
}
WORD wsz [MAX_PATH];
hres = psl->SetPath (pszShortcutFile);
if (FAILED(hres))
{
return FALSE;
}hres = psl->SetDescription (pszDesc);
if (FAILED(hres))
{
return FALSE;
}
#ifndef _UNICODE
MultiByteToWideChar (CP_ACP, 0, pszLink, -1, wsz, MAX_PATH);
#else
wcscpy(wsz,szLink);
#endif
hres = ppf->Save (wsz, TRUE);
if (FAILED(hres))
{
return FALSE;
}
return TRUE;
}string CMainDlg::GetShortcutTarget(LPCSTR pszLinkFileName)
{
HRESULT hres;CString Link, Temp = pszLinkFileName;
Temp.MakeLower();
if (Temp.Find(".lnk")==-1) //Check if the name ends with .lnk
{
Link = pszLinkFileName;
Link +=_T(".lnk"); //if not, append it
}
else
{
Link = pszLinkFileName;
}string Info;
CComPtr<IShellLink> psl;hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
IID_IShellLink, (LPVOID*) &psl);if (SUCCEEDED(hres))
{
CComPtr<IPersistFile> ppf;
hres = psl->QueryInterface( IID_IPersistFile, (LPVOID *) &ppf);
if (SUCCEEDED(hres))
{
WORD wsz[MAX_PATH];
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Link, -1, wsz,MAX_PATH);
hres = ppf->Load(wsz, 0);
if (SUCCEEDED(hres))
{
hres = psl->Resolve(NULL,SLR_ANY_MATCH | SLR_NO_UI);
if(SUCCEEDED(hres))
{
psl->GetPath(Temp.GetBuffer(1024), 1024, NULL,SLGP_UNCPRIORITY);
Temp.ReleaseBuffer();
Info = Temp;
return Info;
} }
}
}
return NULL;