VC中快捷方式的使用和原理

Windows 7程序开发系列之二(JumpList篇1 - User Task)       相对于上一篇中任务栏特性的开发,JumpList的开发显得稍微麻烦一些。JumpList将分为两次讲解,这次先讲解如何添加用户任务(User Task)。同样以foobar2000为例,当右键点击任务栏按钮时,显示程序的JumpList。       最下方3个项目为系统任务,一般不需要我们去操作。上方的两个任务:播放、参数选项,即为自定义的用户任务。用 阅读详情

第六章 快捷方式的最短路径

         Windows Shell允许存储任何对象的引用到系统范围内的任何地点。例如,当你从一个文件夹拖拽可执行程序到另一个文件夹时,鼠标自动改变形状给出除拷贝和移动文件之外的第三种选择。

                                          

         除非你确定,否则可执行文件是不能拷贝或移动的,相反,每次你做这样的操作时,实际拷贝或移动的是对它的物理位置的一个引用,实际所建立的不是文件的拷贝,而是它的初始位置的连接。

         所有这些都是快捷方式的示例,这种东西在老版本的Windows中就已经存在—例如,程序管理器的图标就是早期的快捷方式。然而,不要弄混了,它们不是相同的,主要差别在于快捷方式具有可以指向文件对象这个更普遍的机理:不仅是可执行文件,也不仅仅是文件。在Windows9x和WindowsNT的Shell中,快捷方式是无处不在的。可以在任何文件夹中找到它们,而最多的是在系统的特殊文件夹中。如果你希望应用程序具有印象深刻的功能,比如,添加项到‘Favorites’或‘发送到’文件夹中,甚至是到‘开始’菜单中,则建立快捷方式是一个可行的方法。快捷方式是Shell的重要组成部分,也是我们在这一章里要彻底讨论的内容。在这一章中我们打算讨论:

         快捷方式确切地是什么

         系统怎样存储和装入快捷方式

         怎样建立和删除快捷方式

         可以编码处理快捷方式的函数举例

我们将要给出的例子假设你对Shell编程外围知识有一定了解,但是例子将进一步清晰地说明快捷方式的灵活性。例如,在这一章中,我们将使用热键控件和拖拽功能作为示例应用的内建功能。

什么是快捷方式

         快捷方式表示一个特定文件对象的连接,并是一个具有.lnk扩展名的微小二进制文件。这里的‘微小’意思是快捷方式文件的尺寸很少达到1KB。并不是所有的快捷方式都确切地有相同的尺寸,但是它们却拥有固定的属性集:目标文件对象,描述,热键,图标等。我们将简短地检测这些内容。

         快捷方式遍及整个Windows Shell,可以作为Shell提供的服务。从软件的观点分析,快捷方式是通过暴露IShellLink接口的COM服务器实现的,其接口标识是CLSID_ShellLink。通过这个接口,你可以设置快捷方式的各种属性,和调用接口方法在磁盘上保存或装入它们。

 

快捷方式文件类型

         正如前面所说的,快捷方式是一个文件,但是,它是一种Shell以特殊方法处理的文件。Shell当然知道一个类行为‘快捷方式’的文件是一个对某件东西的引用,所以当你双击它的时候(或单击它—依赖于活动桌面的设置)返回一个被指向的对象,而不是你点击的文件。

 

建立快捷方式

         尽管快捷方式通常都与可执行文件相关联,但这并不是规定—你可以建立目录或非可执行文件的快捷方式。就编程而言绝对没有不同。同样也能建立非文件系统对象的快捷方式(如打印机)。而此时,就有一个小的差别了,你应该使用不同的方法做这个工作。

         建立一个新的.lnk文件有两个选择,头一个依赖于Shell DDE接口,它是直接从旧的程序管理器继承过来的。我们不考虑这种情况,详情请参看Shell DDE相关资料(Internet 客户端SDK)和MSDN库。如果你使用DDE编程而不是下面将要看到的技术,这些资料可以使你知道Windows3.x以来都发生了哪些改变,以及DDE接口中相对较新的特征。

 

使用IShellLink接口

         第二个建立快捷方式的方法(也是推荐的方法)是使用IShellLink COM接口,这是一个十分容易的过程,相关的步骤是:

    建立适当的COM服务器

    获得IShellLink接口指针

    通过IShellLink的方法设置属性

    获得IPersistFile接口指针

    使用IPersistFile的方法保存一个文件的快捷方式

建立服务器就是调用CoCreateInstance(),一定要保证在处理之前已经适当地初始化了COM库(使用CoInitialize()):

IShellLink* pShellLink = NULL;

HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,

IID_IShellLink, reinterpret_cast<LPVOID*>(&pShellLink));

if(FAILED(hr))

return hr;

CLSID在shlobj.h头文件中定义,上面的调用返回指向IShellLink接口的指针,这是处理快捷方式的关键。下面表中列出接口的所有方法,和主要的描述,我们将在随后的代码例程中指出某些可能存在的缺陷。

 

方法

描述

GetArguments()

SetArguments()

返回/设置命令行变量

GetDescription()

SetDescription()

返回/设置描述串

GetHotkey()

SetHotkey()

返回/设置快捷方式的热键

GetIconLocation()

SetIconLocation()

返回/设置路径和图标索引

GetIDList()

SetIDList()

返回/设置链接对象的PIDL。如果操作非文件系统对象,应该使用这两个方法代替GetPath() 和SetPath()

GetPath()

SetPath()

返回/设置链接对象的路径和文件名

GetShowCmd()

SetShowCmd()

返回/设置链接对象的SW_XXX标志

GetWorkingDirectory()

SetWorkingDirectory()

返回/设置工作目录

SetRelativePath()

设置连接对象的相对路径

Resolve()

恢复有快捷方式指向的文件对象

 

一旦获得了IShellLink接口的指针,你就可以开始通过设置目标对象(文件,目录,或非文件对象的PIDL),和选项属性列表,构造快捷方式了。你也可以设置描述文字,快速访问热键,特殊的图标,工作目录,命令行参数以及表示窗口(如果有)建立行为的值。下面是典型的代码段:

pShellLink->SetPath(pszTarget);

pShellLink->SetDescription(pszDesc);

pShellLink->SetHotkey(wHotKey);

pShellLink->SetIconLocation(pszIconPath, wIconIndex);

此时,对象仅仅存在于内存之中,为了使它永久存在,需要把它存进文件。就是为了这个原因,我们使用的COM服务器(标志为CLSID_ShellLink)才实现了IPersistFile接口。这是一个包含读写磁盘方法的接口,因此可以为调用者提供通常编程接口意义上的文件装入与保存服务。

IPersistFile* pPF;

pShellLink->QueryInterface(IID_IPersistFile, reinterpret_cast<LPVOID*>(&pPF));

MultiByteToWideChar(CP_ACP, 0, szLnkFile, -1, wszLnkFile, MAX_PATH);

pPF->Save(wszLnkFile, TRUE);

IPersistFile接口的两个最重要的方法Load()和Save(),二者都要求Unicode串,因而需要转换包含文件名的缓冲为宽字符串格式。

 

快捷方式的全程函数

    我们已经给出了形成新Shell辅助函数的信息,用于建立快捷方式—显然,Windows Shell API并不提供简单而直接的函数来建立(或处理)快捷方式。另一个想法是,我们打算给函数取名为SHCreateShortcutEx()。

事实上,尽管Win32API没有,但是WindowsCE SDK中却包含了这样一个函数SHCreateShortcut(),具有下面的原型:

BOOL SHCreateShortcut(LPTSTR szShortcut, LPTSTR szTarget);

 

我们的函数接受.lnk文件作为目标名,和一个包含这个快捷方式所有属性的结构:

struct SHORTCUTSTRUCT

{

LPTSTR pszTarget;

LPTSTR pszDesc;

WORD wHotKey;

LPTSTR pszIconPath;

WORD wIconIndex;

};

typedef SHORTCUTSTRUCT* LPSHORTCUTSTRUCT;

下面是这个函数的源代码,这个函数我们将在后面的示例程序中使用:

HRESULT SHCreateShortcutEx(LPCTSTR szLnkFile, LPSHORTCUTSTRUCT lpss)

{

WCHAR wszLnkFile[MAX_PATH] = {0};

IShellLink* pShellLink = NULL;

IPersistFile* pPF = NULL;

// 验证SHORTCUTSTRUCT指针

if(lpss == NULL)

return E_FAIL;

// 建立COM服务器,假设CoInitialize()已经被调用

HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL,

CLSCTX_INPROC_SERVER, IID_IShellLink,

reinterpret_cast<LPVOID*>(&pShellLink));

if(FAILED(hr))

return hr;

//设置属性

pShellLink->SetPath(lpss->pszTarget);

pShellLink->SetDescription(lpss->pszDesc);

pShellLink->SetHotkey(lpss->wHotKey);

pShellLink->SetIconLocation(lpss->pszIconPath, lpss->wIconIndex);

//取得IPersistFile接口

hr = pShellLink->QueryInterface(

IID_IPersistFile, reinterpret_cast<LPVOID*>(&pPF));

if(FAILED(hr))

{

pShellLink->Release();

return hr;

}

// 保存LNK(Unicode名)

MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,

szLnkFile, -1, wszLnkFile, MAX_PATH);

hr = pPF->Save(wszLnkFile, TRUE);

// 清理

pPF->Release();

pShellLink->Release();

return hr;

}

 

Shell脚本对象

         Shell脚本对象提出了一种操作快捷方式更好的方法。这在IE4.0中引进,并且已经成为Windows98 的标准部件。在坚固的外壳下,他作为自动服务器,给出了建立和解决快捷方式的编程接口(它们还做许多其它有趣的事情…)

         最有趣的是这些部件都可以用于桌面应用,HTML页面,和整个Windows脚本环境。 在第十二章中我们将详细讨论它们。

 

快捷方式正确的命名

         在Shell的4.71版本以后,一个称之为SHGetNewLinkInfo()的新函数对程序员是可用的。然而与你所希望的不同,这个函数不能建立快捷方式。相反,它的用途在于为快捷方式安排一个正确的名字:

BOOL SHGetNewLinkInfo(LPCTSTR pszLinkTo,

LPCTSTR pszDir,

LPTSTR pszName,

BOOL* pfMustCopy,

UINT uFlags);

这个函数接受路径名的指针或者目标对象的PIDL,这个参数存储在pszLinkTo之中。uFlags值指明它是PIDL还是路径名。目标文件夹是pszDir。

    这个例程将给出正在建立的快捷方式文件的名字。这个名字由pszName参量返回,并假设其缓冲长度为MAX_PATH字符数。当你对已经存在的快捷方式建立快捷方式时,Shell并不建立新的连接,而是,简单地拷贝和修改这个目标。pfMustCopy就用于这个目的,它返回一个布尔值来表示Shell是建立了一个快捷方式文件还是处理了一个拷贝,TRUE表示pszLinkTo是一个已存在的快捷方式,此时Shell只拷贝和适当地修改它,FALSE则是建立一个全新的快捷方式。最后的可用标志是:

 

标志

描述

SHGNLI_PIDL

如果设置,pszLinkTo变量将作为PIDL而不是串来考虑

SHGNLI_NOUNIQUE

如果设置,Shell将首先确定快捷方式的名字,而后检查可能的冲突,如果名字与同文件夹中的另一个发生冲突,就重复操作,直到找出唯一的名字为止。

SHGNLI_PREFIXNAME

如果设置,名字将总是有一个‘快捷方式到’的前缀

 

事实上,SHGetNewLinkInfo()函数努力为快捷方式提供与给定目标一致的名字。例如,对于指向DOS可执行文件,将给出.pif扩展名,否则将给出.lnk扩展名。这个函数所执行的另一个检查是关于目标驱动器是否支持长文件名。如果不支持,则函数返回8.3格式的名字。

 

删除快捷方式

         删除快捷方式与删除文件一样容易。更重要的是不必考虑它所指向文件的命运,因为你仅仅删除了它的引用。被指向的目标完全不受影响。

 

解析快捷方式

         建立快捷方式仅仅完成了工作的一半,很快你就会被读出快捷方式文件的内容弄糊涂。解析快捷方式并不是不同于读文件,但是这个操作一般称之为‘解析’而不是‘读’。有理由说明它们在概念上的差异,快捷方式指向一个文件对象,但是这只是一个连接—不是嵌入的。在建立快捷方式的时候,假定对象是存在的,但是在读它的时候并没有这个假设。当需要访问被引用对象时,没有东西来保证它不被删除,移动,或重命名。

         读一个快捷方式简单地说明你试图访问这个.lnk文件指定的对象。解析快捷方式则说明系统将试图了解被引用对象已经移动到了什么地方,或它是怎样被重命名的。

探测器怎样解析快捷方式

         我们说,解析快捷方式是从读开始的。然而,如果探测器在.lnk文件指定的位置找不到有效的文件对象,则它将在所有驱动器和磁盘目录上执行递归搜索,直到找到具有相同尺寸,建立日期以及与快捷方式指向的文件一样属性的文件为止。如果搜索失败,探测器将显示如下对话框:

                        

 

这个对话框可以通过适当设置IShellLink::Resolve()的标志加以抑制。当然,如果你已经删除了引用对象,探测器也就不可能找到它们,即使它们仍然在‘回收站’中,也不能。

 

解析快捷方式的函数

         Shell API 也缺少解析快捷方式的函数,所以还是需要我们自己写。相关的步骤是:

                   建立必要的COM服务器

                   取得IPersistFile接口指针

        使用IPersistFile的方法从.lnk文件装入快捷方式

        取得IShellLink接口指针

        解析这个快捷方式

整个操作的核心是Resolve()。语法如下:

HRESULT IShellLink::Resolve(HWND hwnd, DWORD fFlags);

头一个参数是一个父窗口的Handle,函数利用它来显示任何需要显示的对话框。另一个是dwFlags变量,它可以是下列值得组合:

 

标志

描述

SLR_NO_UI

函数不显示任何对话框,即使查找指向文件失败。此时函数在默认的3秒钟之后返回。超时时间可以通过这个变量的高位字客户化地指定所期望的毫秒数。

SLR_ANY_MATCH

试着解析这个连接,在失败时显示对话框

SLR_UPDATE

如果设置这个标志,并且引用的对象已经被移动或重命名,则快捷方式被更新到指向新的位置,这个行为不是默认的。

 

注意,更新快捷方式,使其指向新位置上的文件对象(如果有)的行为不是自动的,必须通过传递SLR_UPDATE标志到IShellLink::Resolve()函数显式地请求。下面是SHResolveShortcut()函数的源代码,与它的姊妹例程SHCreateShortcutEx()一样,它在我们说明快捷方式编程的示例程序中被广泛地使用。

HRESULT SHResolveShortcut(LPCTSTR szLnkFile, LPSHORTCUTSTRUCT lpss)

{

WCHAR wszLnkFile[MAX_PATH] = {0};

IShellLink* pShellLink = NULL;

IPersistFile* pPF = NULL;

// 建立合适的COM服务器

HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL,CLSCTX_INPROC_SERVER,

 IID_IShellLink,reinterpret_cast<LPVOID*>(&pShellLink));

if(FAILED(hr))

return hr;

// 取得装入.LNK 文件的IPersistFile接口

hr = pShellLink->QueryInterface(IID_IPersistFile,

 reinterpret_cast<LPVOID*>(&pPF));

if(FAILED(hr))

{

pShellLink->Release();

return hr;

}

// 装入快捷方式(Unicode 名)

MultiByteToWideChar(CP_ACP, 0, szLnkFile, -1, wszLnkFile, MAX_PATH);

hr = pPF->Load(wszLnkFile, STGM_READ);

if(FAILED(hr))

{

pPF->Release();

pShellLink->Release();

return hr;

}

// 解析连接

hr = pShellLink->Resolve(NULL, SLR_ANY_MATCH);

if(FAILED(hr))

{

pPF->Release();

pShellLink->Release();

return hr;

}

// 抽取信息,充填到lpss

if(lpss != NULL)

{

TCHAR szPath[MAX_PATH] = {0};

TCHAR szDesc[MAX_PATH] = {0};

TCHAR szIcon[MAX_PATH] = {0};

WORD w = 0;

WORD wIcon = 0;

WIN32_FIND_DATA wfd;

pShellLink->GetPath(szPath, MAX_PATH, &wfd, SLGP_SHORTPATH);

pShellLink->GetDescription(szDesc, MAX_PATH);

pShellLink->GetHotkey(&w);

pShellLink->GetIconLocation(szIcon, MAX_PATH,

 reinterpret_cast<int*>(&wIcon));

lpss->pszTarget = szPath;

lpss->pszDesc = szDesc;

lpss->pszIconPath = szIcon;

lpss->wHotKey = w;

lpss->wIconIndex = wIcon;

}

pPF->Release();

pShellLink->Release();

return hr;

}

在装入文件时,我们使用了IPersistFile接口的Load()方法,它有两个变量,Unicode格式的.Lnk文件名,和表示要打开文件的访问模式的参数。

 

快捷方式与特殊文件夹

         在绝大多数情况下,如果需要编程建立快捷方式,你都需要在一个特殊文件夹中建立它。然而,这并不复杂—仅仅是要指定这个文件夹的正确路径而已。在下一节将要讨论的程序中,就允许你在很多这种通常的特殊文件夹:‘我的文档’,‘桌面’,‘开始菜单’,‘程序’,‘发送到’和‘Favorites’中建立快捷方式。考虑第五章中的SHGetSpecialFolderPath()函数,它正好能发现非虚拟文件夹的路径。

 

示例程序:快捷方式管理

         下图所看到的应用是一个建立和解析快捷方式的控制板管理器。它的对话框窗口分成两个部分:上面部分是解析快捷方式,下面的则是建立新的快捷方式。

           

这个用户界面使你能够选择打开.Lnk文件,并且可以拉动目标—即,支持拖拽快捷方式,和解析快捷方式。在这个程序中解析的每一个快捷方式都将在观察中列出报告。这里开发的例子将仅仅显示目标,描述和热键信息,要进一步增强它的功能,对于你不应该是太大的问题。

 

选择一个快捷方式

         头一个必须考虑的问题是安排一个‘打开’对话框来选择要解析的快捷方式。麻烦是,在默认情况下,‘打开’对话框不能处理快捷方式,因此没有任何.lnk文件的名字被返回。为了在这种环境下工作,你必须在GetOpenFileName()函数中指定OFN_NODEREFERENCELINKS标志。就象下面显示的处理器函数那样,它在应用对话框上安排两个浏览按钮:

void OnBrowse(HWND hDlg, WPARAM wID)

{

TCHAR szFile[MAX_PATH] = {0};

OPENFILENAME ofn;

ZeroMemory(&ofn, sizeof(OPENFILENAME));

ofn.lStructSize = sizeof(OPENFILENAME);

if(wID == IDC_SHORTCUT)

{

ofn.lpstrFilter = __TEXT("Shortcuts/0*.lnk/0");

ofn.Flags = OFN_NODEREFERENCELINKS;

}

else

ofn.lpstrFilter = __TEXT("All files/0*.*/0");

ofn.nMaxFile = MAX_PATH;

ofn.lpstrInitialDir = __TEXT("c://");

ofn.lpstrFile = szFile;

if(!GetOpenFileName(&ofn))

return;

else

SetDlgItemText(hDlg, wID, ofn.lpstrFile);

return;

}

使用这个技术,如果你双击一个.lnk文件,探测器将停在那儿,并返回一个指向文件的名,而不是进入引用的文件。

 

Shell拖拽

         我承认,即使我们是对Shell进行编程,拖拽也不是一个有重要关系的科目,但是在我们看到它操作的过程之后,这就显得有价值了。VC++资源编辑器就有拉动目标的特征(通过打开WS_EX_ACCEPTFILES位)。当然,它也能使你来规划怎样或什么时候可以处理拉动事件。我们想要限制列表观察的拖拽操作,但是如果你指派了这个特征,则我们所要面对的是必须子类化这个窗口,以便感知相关的拉动事件。反过来,我们希望使用较简单的方法:整个对话框都将可以拉动,但是当它捕捉到消息WM_DROPFILES后,应该校验列表观察中所发生的事件,否则,忽略这个事件。Shell处理拖拽的函数都定义在shellapi.h中,有DragQueryPoint(),DragQueryFile()和DragFinish(),后面我们还将继续讨论这个课题。

 

显示结果

         这个程序在用户界面上有一个报告风格的列表观察,为了较容易的使用它,我们建立了两个辅助函数来帮助在观察中增加列和串。记住以后我们还会使用它们。

         第一个函数是MakeReportView(),它转换列表观察窗口到具有指定列的报告风格的列表观察。函数原型要求传递一个列表观察的Handle,一个带有字符串名和列宽度值的数组,以及一个列数值。为了使函数尽量简洁,我们假定,数组中偶位置为名字串,奇位置为数字。

    数组实际是一个串指针数组—即,一个32位值的数组,理解了这个假设之后,你就可以如下使用数组了:

LPTSTR psz[] = {"Target", reinterpret_cast<TCHAR*>(170),

"Description", reinterpret_cast<TCHAR*>(170),

"Hotkey", reinterpret_cast<TCHAR*>(100)};

MakeReportView(hwndList, psz, 3);

MakeReportView()总是以名字/宽度对方式处理数组元素,所以,列数总是等于数组尺寸的一半。

void MakeReportView(HWND hwndList, LPTSTR* psz, int iNumOfCols)

{

RECT rc;

DWORD dwStyle = GetWindowStyle(hwndList);

SetWindowLong(hwndList, GWL_STYLE, dwStyle | LVS_REPORT);

GetClientRect(hwndList, &rc);

// 处理元素对,数组尺寸假设为 2 * iNumOfCols

for(int i = 0 ; i < 2 * iNumOfCols ; i = i + 2)

{

LV_COLUMN lvc;

ZeroMemory(&lvc, sizeof(LV_COLUMN));

lvc.mask = LVCF_TEXT | LVCF_WIDTH;

lvc.pszText = psz[i];

if(reinterpret_cast<int>(psz[i + 1]) == 0)

lvc.cx = rc.right / iNumOfCols;

else

lvc.cx = reinterpret_cast<int>(psz[i + 1]);

ListView_InsertColumn(hwndList, i, &lvc);

}

return;

}

MakeReportView()的伴随例程是AddStringToReportView(),它添加新行到指定的列表观察。由于底层编程接口的原因,充填报告风格列表观察所有列要求分几个步骤。你应该为这个新项的第一列(主列)添加指定的文字,然后依次在其他列上设置文字。所有这些步骤都由AddStringToReportView()执行,你只需要传递一个包含所有子串的NULL分隔串和在iNumOfCols指出有多少列就可以了。

void AddStringToReportView(HWND hwndList, LPTSTR psz, int iNumOfCols)

{

LV_ITEM lvi;

ZeroMemory(&lvi, sizeof(LV_ITEM));

lvi.mask = LVIF_TEXT;

lvi.pszText = psz;

lvi.cchTextMax = lstrlen(psz);

lvi.iItem = 0;

ListView_InsertItem(hwndList, &lvi);

// 其它列

for(int i = 1 ; i < iNumOfCols ; i++)

{

psz += lstrlen(psz) + 1;

ListView_SetItemText(hwndList, 0, i, psz);

}

return;

}

在本例中列表观察有三列:‘目标’,‘描述’和‘热键’。前两列是直接的,而第三列使用了以前没用过的通用控件的一个用法。这是需要进一步说明的。

 

热键通用控制

         Windows95引进了一个新的控件,可以使你能够图像方式选择一个键的组合(如图):

                       

使用这个控件的方法是,敲击按键组合,它解释键盘码,并转换成相应文字,显然,在建立快捷方式环境下,这个控件有更友善的用户界面。

         反过来,在解析快捷方式时,你所有的全部仅是一个由IShellLink::GetHotkey()返回的数(精确地:DWORD),这需要把它转换成一个友善格式的串。

         这个字分解成两个字节,表示一个热键,高字节是修改符(Alt,Ctrl,Shift,或三者的组合),低字节是你所敲击的键码。注意,如果你按了A,这个码是65 (大写字符),不是97(小写字符)。要使用HotkeyToString()例程,你需要相对于某些已知常量检查高字节的位。下面这个函数对此进行了处理:

void HotkeyToString(WORD wHotKey, LPTSTR pszBuf)

{

BYTE bKey = LOBYTE(wHotKey);

BYTE bMod = HIBYTE(wHotKey);

if(bMod & HOTKEYF_CONTROL)

lstrcpy(pszBuf, __TEXT("Ctrl"));

if(bMod & HOTKEYF_SHIFT)

if(lstrlen(pszBuf))

lstrcat(pszBuf, __TEXT(" + Shift"));

else

lstrcpy(pszBuf, __TEXT("Shift"));

if(bMod & HOTKEYF_ALT)

if(lstrlen(pszBuf))

lstrcat(pszBuf, __TEXT(" + Alt"));

else

lstrcpy(pszBuf, __TEXT("Alt"));

TCHAR s[2] = {0};

wsprintf(s, __TEXT("%c"), bKey);

if(lstrlen(pszBuf))

{

lstrcat(pszBuf, __TEXT(" + "));

lstrcat(pszBuf, s);

}

else

lstrcpy(pszBuf, s);

}

HotkeyToString()函数接受热键值和一个要充填返回结构的串缓冲。它检查修改器和建立串的第一部分—如,Ctrl+Alt,然后通过关联的按键字符完成这个操作—如,Ctrl+Alt+X。下图中显示了在解析快捷方式时应用的结果:

                                      

收集建立变量

         如果不是有点微妙的话,建立快捷方式对话框的这一部分没有什么可值得注意的。打开一个已存在的快捷方式(使用在桌面上的一个是比较好的),试图给它分配一个新的热键,此时,你将发现热键控件校正你的按键。A将变成Ctrl+Alt+A。

         正是这个特征,而且是个重要特征,因为,如果你试图编程地分配一个非Ctrl+Alt+…形式的热键,这个热键将永远不被识别。稍微考虑一下,你就会明白,这种行为不是串—Ctrl+Alt+ … 与可能的加速器冲突。这也使我明白了,在前几个例子中为什么Alt+A是错误的了。

 

给出热键规则

         指令热键控件自动置换某些错误或无效的按键组合,你需要使用按键规则。不管它的名字如何,这其实就是简单地发送消息到热键窗口。为了强制使它接受仅仅Ctrl+Alt前缀的按键,你必须:

SendMessage(hwndHotkey, HKM_SETRULES,

HKCOMB_NONE | HKCOMB_S | HKCOMB_A | HKCOMB_C,

HOTKEYF_CONTROL | HOTKEYF_ALT);

这个‘规则’可以重新解释为:

         无效的按键组合总是那些有一个修改符在wParam中列出的按键。

         用在lParam中指定的组合键置换每一个无效的按键。

如果不是从空(HKCOMB_NONE),Shift(HKCOMB_S), Alt (HKCOMB_A) 或 Ctrl(HKCOMB_C)开始,则忽略它们,并用Ctrl+Alt代替之。下面图像显示了建立快捷方式时的这个过程:

                 

源代码

         现在看一下这个示例程序的剩余源代码。要正确地编译它必须确保包含shlobj.h, resource.h 和commdlg.h,以及链接comdlg32.lib和ole32.lib。另外,由于我们使用了COM,所以还需要用CoInitialize(NULL)和CoUninitialize()把WinMain()中的DialogBox()调用括起来。

 

DoCreateShortcut()函数

         这个函数在点击‘建立’按钮时被调用。它从控件中收集参数和安排调用SHCreateShortcutEx()函数,在combo框中有一些特殊文件夹的名字。

void DoCreateShortcut(HWND hDlg)

{

SHORTCUTSTRUCT ss;

ZeroMemory(&ss, sizeof(SHORTCUTSTRUCT));

TCHAR szTarget[MAX_PATH] = {0};

TCHAR szDesc[MAX_PATH] = {0};

// 取得热键

ss.wHotKey = static_cast<WORD>(SendDlgItemMessage(

hDlg, IDC_HOTKEY, HKM_GETHOTKEY, 0, 0));

// 取得目标和描述

GetDlgItemText(hDlg, IDC_TARGET, szTarget, MAX_PATH);

GetDlgItemText(hDlg, IDC_DESCRIPTION, szDesc, MAX_PATH);

ss.pszTarget = szTarget;

ss.pszDesc = szDesc;

// 确定快捷方式文件名

// 取得目标文件夹和最后的反斜杠

HWND hwndCbo = GetDlgItem(hDlg, IDC_SPECIAL);

int i = ComboBox_GetCurSel(hwndCbo);

DWORD nFolder = ComboBox_GetItemData(hwndCbo, i);

TCHAR szPath[MAX_PATH] = {0};

SHGetSpecialFolderPath(hDlg, szPath, nFolder, FALSE);

if(szPath[lstrlen(szPath) - 1] != '//')

lstrcat(szPath, __TEXT("//"));

TCHAR szLnkFile[MAX_PATH] = {0};

GetDlgItemText(hDlg, IDC_LNKFILE, szLnkFile, MAX_PATH);

lstrcat(szPath, szLnkFile);

lstrcat(szPath, __TEXT(".lnk"));

// 建立

SHCreateShortcutEx(szPath, &ss);

// 更新UI

SetDlgItemText(hDlg, IDC_SHORTCUT, szPath);

return;

}

DoResolveShortcut()函数

         这个函数在响应‘解析’按钮的点击时调用。尽管它也接受一个附加的参数pszFile,用于表示要解析的文件。如果这个参数为NULL,则函数使用‘快捷方式’编辑框中的内容。这个变量存在的原因是使这个函数更容易解析拖拽到程序窗口上的任何文件。DoResolveShortcut()首先调用我们的函数SHResolveShortcut()解析这个快捷方式,然后更新用户界面,附加一个新行到报告列表观察中。

void DoResolveShortcut(HWND hDlg, LPTSTR pszFile)

{

TCHAR szLnkFile[MAX_PATH] = {0};

if(pszFile == NULL)

GetDlgItemText(hDlg, IDC_SHORTCUT, szLnkFile, MAX_PATH);

else

lstrcpy(szLnkFile, pszFile);

// 解析快捷方式

SHORTCUTSTRUCT ss;

HRESULT hr = SHResolveShortcut(szLnkFile, &ss);

if(FAILED(hr))

return;

//////////////////////////////////////////////

// 更新UI

// 建立列表观察串

TCHAR pszBuf[1024] = {0};

LPTSTR psz = pszBuf;

lstrcpy(psz, ss.pszTarget);

lstrcat(psz, __TEXT("/0"));

psz += lstrlen(psz) + 1;

lstrcpy(psz, ss.pszDesc);

lstrcat(psz, __TEXT("/0"));

psz += lstrlen(psz) + 1;

// Try to get the text version of the hotkey

TCHAR szKey[30] = {0};

HotkeyToString(ss.wHotKey, szKey);

lstrcpy(psz, szKey);

lstrcat(psz, __TEXT("/0"));

// 加一个新项到报告列表观察(3 列)

HWND hwndList = GetDlgItem(hDlg, IDC_VIEW);

AddStringToReportView(hwndList, pszBuf, 3);

return;

}

HandleFileDrop()函数

         在响应WM_DROPFILES消息时调用此函数,这个函数定义了当用户拖拽文件到窗口的客户区域时所要求的操作。接受的数据是CF_HDROP类型的,这是一种从探测器窗口或从桌面拖拽文件操作时用于Shell移动文件环境下的交互格式。任何具有WS_EX_ACCEPTFILES风格设置的窗口都只对拖拽操作敏感,并以这种格式封装数据。换句话说,当源是Windows Shell,或其它以CF_HDROP格式传递参数的程序时,我们的程序也接受拖拽操作。

         CF_HDROP是一种剪裁板格式,用于交换基本为文件名的数据项的格式—更多关于剪裁板格式信息和CF_HDROP数据的内部结构可以参看VC++的帮助文件。对于我们而言,重要的是,虽然它的内存Handle称为CF_HDROP,还是有一定数量的函数能够读出这种格式的数据。

         当你从Shell拉动文件的时候,目标窗口接收到消息WM_DROPFILES,其中一个变量是HDROP型的Handle。我们的HandleFileDrop()函数首先检查拉动发生的窗口,如果这个窗口是列表观察,则进一步抽取和解析各种文件名。你可以拉动任何文件到这个列表观察上,但是仅仅快捷方式被正确地处理。

void HandleFileDrop(HWND hDlg, HDROP hDrop)

{

// 检查拉动到的窗口

POINT pt;

DragQueryPoint(hDrop, &pt);

ClientToScreen(hDlg, &pt);

HWND hwndDrop = WindowFromPoint(pt);

if(hwndDrop != GetDlgItem(hDlg, IDC_VIEW))

{

Msg(__TEXT("抱歉,你必须拉动到列表观察控件上!"));

return;

}

// 检查文件

int iNumOfFiles = DragQueryFile(hDrop, -1, NULL, 0);

for(int i = 0 ; i < iNumOfFiles; i++)

{

TCHAR szFileName[MAX_PATH] = {0};

DragQueryFile(hDrop, i, szFileName, MAX_PATH);

DoResolveShortcut(hDlg, szFileName);

}

DragFinish(hDrop);

}

DragQueryPoint()告知拉动发生时点的客户区域坐标,而DragQueryFile()则依次抽取所有包装在HDROP Handle中的文件。你也可以使用这个函数获得拉动的文件数。最后,必须调用DragFinish()函数来结束拉动操作。

APP_DlgProc()函数

         这是应用主窗口过程,由于涉及到我们前面给出的几个例子,所以看一下这个处理器是有价值的:

BOOL CALLBACK APP_DlgProc(HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)

{

switch(uiMsg)

{

case WM_INITDIALOG:

OnInitDialog(hDlg);

break;

case WM_DROPFILES:

HandleFileDrop(hDlg, reinterpret_cast<HDROP>(wParam));

break;

case WM_COMMAND:

switch(wParam)

{

case IDC_RESOLVE:

DoResolveShortcut(hDlg, NULL);

return FALSE;

case IDC_CREATE:

DoCreateShortcut(hDlg);

return FALSE;

case IDC_BROWSE:

OnBrowse(hDlg, IDC_SHORTCUT);

return FALSE;

case IDC_BROWSETARGET:

OnBrowse(hDlg, IDC_TARGET);

return FALSE;

case IDCANCEL:

EndDialog(hDlg, FALSE);

return FALSE;

}

break;

}

return FALSE;

}

OnInitDialog()函数

         在这个工程(project)中有几个东西要初始化。在处理combo框时,应该有一个熟知的过程,我们还需要设置列表观察控件,以及编程热键控件,使其使用Ctrl+Alt…的格式形式。

void OnInitDialog(HWND hDlg)

{

// 设置图标(T/F 大/小图标)

SendMessage(hDlg, WM_SETICON, FALSE, reinterpret_cast<LPARAM>(g_hIconSmall));

SendMessage(hDlg, WM_SETICON, TRUE, reinterpret_cast<LPARAM>(g_hIconLarge));

// 初始化报告观察

HWND hwndList = GetDlgItem(hDlg, IDC_VIEW);

LPTSTR psz[] = {"Target", reinterpret_cast<TCHAR*>(170),

"Description", reinterpret_cast<TCHAR*>(170),

"Hotkey", reinterpret_cast<TCHAR*>(100)};

MakeReportView(hwndList, psz, 3);

// 可用的特殊文件夹

HWND hwndCbo = GetDlgItem(hDlg, IDC_SPECIAL);

int i = ComboBox_AddString(hwndCbo, "Desktop");

ComboBox_SetItemData(hwndCbo, i, CSIDL_DESKTOP);

i = ComboBox_AddString(hwndCbo, "Favorites");

ComboBox_SetItemData(hwndCbo, i, CSIDL_FAVORITES);

i = ComboBox_AddString(hwndCbo, "Programs");

ComboBox_SetItemData(hwndCbo, i, CSIDL_PROGRAMS);

i = ComboBox_AddString(hwndCbo, "My Documents");

ComboBox_SetItemData(hwndCbo, i, CSIDL_PERSONAL);

i = ComboBox_AddString(hwndCbo, "SendTo");

ComboBox_SetItemData(hwndCbo, i, CSIDL_SENDTO);

i = ComboBox_AddString(hwndCbo, "Start Menu");

ComboBox_SetItemData(hwndCbo, i, CSIDL_STARTMENU);

ComboBox_SetCurSel(hwndCbo, 0);

// 初始化热键控件,每一件东西都有Ctrl+Alt前缀

SendDlgItemMessage(hDlg, IDC_HOTKEY, HKM_SETRULES,

HKCOMB_NONE | HKCOMB_S | HKCOMB_A | HKCOMB_C,

HOTKEYF_CONTROL | HOTKEYF_ALT);

SetDlgItemText(hDlg, IDC_TARGET, __TEXT("C://"));

}

在系统文件夹中建立快捷方式

         现在可以编译和运行的这个示例程序可以使你很容易在系统文件夹中建立快捷方式—所需要做的全部操作就是从combo框中选择一个文件夹名。如果你希望在你自己的程序中静默地做这项工作,只要知道所涉及的文件夹名,剩下的就是用这个全路径名格式化一个串。

下面是做这项工作的一个简单的函数。作为变量,它接受要建立的.lnk文件名,特殊文件夹的ID(有CSIDL_XXX格式的常量),以及指向的文件名。代码是SHCreateShortcutEx()的一个封装:

HRESULT SHCreateSystemShortcut(LPCTSTR szLnkFile, int nFolder, LPCTSTR szFile)

{

WCHAR wszLnkFile[MAX_PATH] = {0};

TCHAR szPath[MAX_PATH] = {0};

IShellLink* pShellLink = NULL;

IPersistFile* pPF = NULL;

// 建立适当的COM服务器

HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL,

CLSCTX_INPROC_SERVER, IID_IShellLink,

reinterpret_cast<LPVOID*>(&pShellLink));

if(FAILED(hr))

return hr;

// 设置属性

pShellLink->SetPath(szFile);

// 取得IPersistFile接口,用于保存

hr = pShellLink->QueryInterface(

IID_IPersistFile, reinterpret_cast<LPVOID*>(&pPF));

if(FAILED(hr))

{

pShellLink->Release();

return hr;

}

// 准备快捷方式名

SHGetSpecialFolderPath(NULL, szPath, nFolder, FALSE);

if(szPath[lstrlen(szPath) - 1] != '//')

lstrcat(szPath, __TEXT("//"));

lstrcat(szPath, szLnkFile);

// 存储LNK(Unicode 名)

MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szPath, -1, wszLnkFile, MAX_PATH);

hr = pPF->Save(wszLnkFile, TRUE);

// 清理

pPF->Release();

pShellLink->Release();

return hr;

}

使用上面的函数,在‘桌面’上,在‘开始’菜单中,在‘程序文件’中,或在‘Favorites’中建立快捷方式就非常容易了。要证明这一点,要求在‘开始’菜单中添加一个新项指向‘记事本’,则只需要:

SHCreateSystemShortcut(__TEXT("Notepad.lnk"), CSIDL_STARTMENU,

                                        __TEXT("c://windows//notepad.exe"));

就可以了,显然,c:/windows/路径应该置换成你机器上的实际Windows的目录。还要注意,在Windows NT下,‘notepad.exe’存储在‘System’目录下。

    你也可以建立指向目录和非可执行文件的快捷方式。事实上,引用任何系统文件对象,仅仅需要传递路径到IShellLink::SetPath(),或传递PIDL调用IShellLink::SetIDList()。

 

‘发送到’文件夹

‘发送到’文件夹是包含两个非快捷方式对象的文件夹,对于快捷方式是不值得注意的。如果在Windows上安装了IE4.0以上版,或在Windows98以上版系统上,‘发送到’文件夹可能包含对Email容器或桌面的引用。使用这个机理,你也可以从Shell把给定的文件作为新消息的附件直接发送到你自己的发件箱,或作为快捷方式发送到桌面。

                 

                

这个截图显示有两个没有典型的快捷标记的项,它们是‘桌面快捷方式’,是一个空的.DeskLink文件,其长度为0字节。如果你搜索这个扩展名的注册表信息,就会发现,在其后有一个COM对象。

       

         了解到它由一个COM对象支持文件是一大进步,但是,是哪一种COM对象在支持它,COM对象实现了什么接口,都还需要进一步探讨。事实上,这是一个Shell扩展,更确切地讲是一个拖动处理器。我们将在第十五章中讨论Shell扩展。现在,仅说明‘发送到’文件夹不仅包含快捷方式还有别的就可以了。使用.DeskLink串纯粹是一种表示,你也可以使用其它串来表示。

 

‘最近文档’文件夹

         ‘最近文档’文件夹收集最近打开的文档。这个目录下的内容可以通过单击‘开始’菜单的‘文档’项来查看,其物理位置在Windows目录下。然而,奇怪的是在它包含的快捷方式和通过菜单显示的项之间并不是1:1对应的。

         Shell API给出了一个称之为SHAddToRecentDocs()的函数,使程序员能够把文档的链接存储到这个文件夹下。

void SHAddToRecentDocs(UINT uFlags, LPCVOID pv);

第一个变量指出了第二个变量的类型:PIDL或路径名,可以取SHARD_PATH或SHARD_PIDL值。使用这个函数,能够成功地把你的文档引用加进菜单中。但是如果简单地在这个文件夹上建立一个快捷方式,就不对了—也就是说,建立快捷方式是必要的但不充分。SHAddToRecentDocs()显然做了更多的事情。

         最终,SHAddToRecentDocs()添加项目到由‘开始菜单’使用的MRU(最近使用的)列表中,并且简单地加这个文件到‘最近文档’文件夹中。这个函数还复制这个文件夹中的快捷方式,以及处理菜单的顺序。因此,你应该坚定地使用函数,而不是其他方法,以兼容未来在某些方面实现方式的变化。

 

小结

这一章中探讨了快捷方式,这个在Windows的任何书和文章中都讨论过的课题。快捷方式是相对简单的,但是没有建立和解析它的单一函数,这一章中讨论并写出了这样的函数,还查看了:

         快捷方式的作用

         怎样建立和解析它们

         某些与快捷方式一道工作的有用的函数

         拖拽和热键控件

         快捷方式和系统文件夹之间的关系

 

记录一次在PowerShell下进入VC编译环境的探索过程 前言 为了实现 gitlab CI 自动化构建功能,需要用到 gitlab-runner,在 windows + vc 的场景下需要用到 gitlab-runner for windows。其最终原理是在 powershell 环境下执行 powershell 脚本。 众所周知,大部分通过命令行编译VC项目的过程都需要先初始化VC的工作环境(用cmake构建的除外),通常是通过开始菜单中的 “... 阅读详情

相关推荐

MFC Windows 程序设计[158]之创建快捷方式

MFC是微软公司提供的一个类库(class libraries),用以减少应用程序开发人员的工作量。其中包含大量Windows句柄封装类和很多Windows的内建控件和组件的封装类。有很多初学MFC的小伙伴们都觉得MFC比较难学,看书吧,看了就忘了,网上的一些教程又是写的含糊不清,故而学习起来很吃力...... 故此,本专栏亲自研究,实践,由易到难,层层深入,将调试过程中所遭所遇进行详细讲解,注意事项进行一一列举,希望能够帮助到各位初学MFC的小伙伴,避免走弯路,费时费力。......

hnjzfwy的博客 360

Windows Shell编程-第六章.快捷方式的最短路径

第六章 快捷方式的最短路径          Windows Shell允许存储任何对象的引用到系统范围内的任何地点。例如,当你从一个文件夹拖拽可执行程序到另一个文件夹时,鼠标自动改变形状给出除拷贝和移动文件之外的第三种选择。                                                     除非你确定,否则可执行文件是不能拷贝或移动的,相反,每

ljmwork的专栏 2006

Windows桌面图标隐藏与恢复VC教程

Visual C++(VC)的界面主要分为菜单栏、工具栏、编辑区、输出窗口等部分。菜单栏包含了文件、编辑、视图、项目、构建、调试、工具、窗口、帮助等选项,是进行各种操作的主要入口。工具栏包含了常用的命令按钮,如新建、打开、保存、编译、运行等。编辑区是编写代码的主要区域,支持多种语言的语法高亮显示。输出窗口用于显示程序的编译和运行结果。Windows API(应用程序编程接口)是一系列预定义的函数、结构和常数,供程序员用来构建Windows应用程序。

weixin_42124497的博客 1482

Windows Shell命令

一、Windows下Shell命令概述 Windows的Shell命令又是Windows的CMD命令。而cmd命令又是原来MS-DOS系统保留下来。 Shell来源路径与配置 对应的命令资源配置在windows的环境变量中: Windows Shell命令是基于配置好的Path环境变量,对Shell命令在Path路径中依次从前至后搜寻到对应命名的可执行入口。 也就是可以自行编写一些Shell程序,...

SXWZ的博客 2407

VC6.0:深入详解与实践

MFC(Microsoft Foundation Classes)库是Microsoft为简化Windows应用程序开发而提供的一套C++类库。在MFC中,文档/视图架构是一种典型的模式,它将应用程序的数据(文档)与数据显示(视图)分离。这种架构鼓励开发者采用模块化设计,使得代码更容易维护和扩展。文档/视图架构包含三个主要组件:文档(Document)、视图(View)和框架窗口(Frame Window)。文档是数据存储的中心,视图负责展示文档数据,框架窗口则提供了一个用户交互的环境。

weixin_34640289的博客 956

京东技术产品经理面试全记录:从简历投递到一面45分钟实战复盘

本文深度复盘京东技术产品经理面试全流程,从策略准备、思维题拆解、项目阐述到反问谈判,提供了一套可演练的求职策略体系。文章强调该岗位需具备横跨技术与商业的枢纽能力,并通过“QQ分组vs微信标签”等案例,剖析了产品思维与结构化表达在面试中的关键作用,助力候选人将紧张转化为掌控感。

weixin_29275257的博客 630

APIT INTRODUCT

一、什么是Windows API?   1.什么是Windows API?   Windows 这个多作业系统除了协调应用程式的执行、分配内存、管理系统资源…之外, 她同时也是一个很大的服务中心,调用这个服务中心的各种服务(每一种服务就是一个函数),可以帮应用程式达到开启视窗、描绘图形、使用周边设备…等目的,由於这些函数服务的对象是应用程式(Application), 所以便称之为 Applica

cxzhq2002的杂记 1441

最新测算源码PHP算命源码-风水八字-八字起名-塔罗牌占卜

未来,随着技术的发展和用户需求的变化,我们可以继续对系统进行升级和优化,以满足更多用户的需求和期望。数据库设计:创建一个名为bazi的数据库表,包含以下字段:id(自增ID)、name(姓名)、sex(性别)、birth_date(出生日期)、birth_time(出生时间)、bazi(八字结果)。数据库设计:创建一个包含塔罗牌信息的数据库表,如tarot_cards,包含字段:id(塔罗牌ID)、name(塔罗牌名称)、meaning(塔罗牌含义)等。根据操作系统的不同,PHP的安装方法会有所不同。

shanbeizi的博客 2418

windows shell 编程(转载)

  <INPUT id="__VIEWSTATE" type=hidden name=__VIEWSTATE> //<![CDATA[var theForm = document.forms[Form1];if (!theForm) { theForm = document.Form1;}function __doPostBack(eventTa

flyhigh_tbw的专栏 2483

DETRs with Collaborative Hybrid Assignments Training论文阅读与代码

在这篇论文中,作者观察到在DETR中将过少的 Query 分配为正样本,采用一对一的集合匹配,会导致对编码器输出的监督稀疏,严重损害编码器的区分特征学习,反之亦然,也会影响解码器中的注意力学习。为了缓解这个问题,作者提出了一种新颖的协同混合分配训练方案,名为Co-DETR,以从多样的标签分配方式中学习更高效、更有效的基于DETR的检测器。这种新的训练方案可以通过训练多个并行辅助 Head ,以一对多的标签分配方式(如ATSS和Faster RCNN)进行监督,轻松增强端到端检测器中编码器的学习能力。

qingjing0413的博客 1155

峰岹FU6832L双核电机控制芯片实战:从FOC算法到BLDC/PMSM驱动开发

在嵌入式系统和电机控制领域,磁场定向控制(FOC)是实现无刷直流电机(BLDC)和永磁同步电机(PMSM)高效、平稳运行的核心算法。其原理是通过坐标变换将三相交流量分解为直流量,实现类似直流电机的转矩与磁场解耦控制,从而提升效率、降低噪音与振动。这项技术的工程价值在于能以更低的处理器开销实现高性能驱动,广泛应用于消费电子、工业自动化与智能家电。本文以峰岹FU6832L芯片为例,深入解析其独特的双核架构——将实时性要求极高的FOC运算交由专用硬件引擎处理,解放主控CPU处理应用逻辑,并结合PID参数整定、电流

weixin_29570795的博客 512

给应用程序创建快捷方式VC实现

 1 引言 在Windows 3.x中,软件安装程序通常使用动态数据交换(DDE)的方法在程序管理器(Program Manager)中创建一个程序组(Program Group)。随着Windows95/98/NT这些32操作系统在系统内核和用户界面上的巨大改进,这种方法在已经不再适用了。现在,几乎所有的安装 程序都是在“开始”菜单中添加新的菜单项,或者在桌面上建立新的快捷方式(Shortcut

yxyhack's blog 5076

JWT实战:5分钟搞定Spring Boot集成JWT认证(含完整代码示例)

本文详细介绍了如何在Spring Boot中快速集成JWT认证,提供完整的代码示例和配置指南。通过JWT实现无状态认证,解决分布式系统中的会话管理问题,提升系统扩展性和安全性。涵盖令牌生成、验证、Spring Security整合及生产级优化方案。

weixin_29327525的博客 456

VC创建快捷方式

快捷方式: 现在,几乎所有的安装 程序都是在“开始”菜单中添加新的菜单项,或者在桌面上建立新的快捷方式(Shortcut)(有时还要建立相应的菜单组或文件夹来包含多个菜单项或快捷 方式),以便于用户使用时方便快捷地启动软件。当启动一个安装程序后,程序安装完成后会自动创建相应的快捷方式。 Windows 的快捷方式实际上是一个带有扩展名LNK的数据文件,其中包含有用于访问Windows

cp32212116的专栏 1356

详解Xilinx FPGA高速串行收发器GTX/GTP(6)--Pattern Generator/Pattern Checker

详解Xilinx FPGA高速串行收发器GTX/GTP(6)--Pattern Generator/Pattern Checker

孤独的单刀 1625

Ubuntu\linux,加载mcp2515的驱动,链接mcp2515,mcp2515的这个驱动默认在linux内核中。jetson nao、香橙派、树莓派

3. 其他文章,还要进行驱动编译就比较复杂了(不推荐)1.树莓派+MCP2515+

pvmsmfchcs的博客 1564

一分钟学会创建开始菜单程序组和桌面快捷方式

Windows下的开始菜单程序组和桌面快捷方式对普通用户来讲可以说是整个系统的入口,因此一般的安装程序在运行时都会创建这两种链接。那么我们普通的绿色程序(我说的是那种没有依靠打包向导生成的程序)有没有机会创建呢,当然是有的。在VC下创建这两类链接有几个要点,第一是获取链接所在目录,比如桌面的目录通常是:C:/Documents and Settings/UserName/桌面。 程序组的目录通

陈敏的博客 8688

Sigrity Power Tree如何进行直流压降仿真分析操作指导

Sigrity Power Tree模式可以用于前仿真,在没有layout文件的情况下对电源进行直流压降仿真分析,用于评估原理图设计以及器件选型是否合理。Model:设置为Equal Current,设置为等电源模型,意思该负载每个电源pin都是等电流的。Current:设置每个负载的电流,U30为3A,U3-U6各为1A。Nominal Current:设置能够承载的最大电流,设置为10A。Upper/ Lower Tol:设置为压降范围为5%Output Current:设置输出电流为10A。

weixin_54787054的博客 732

VC6.0(VC++6.0)下载安装和使用教程(超级肝,新手必看)

VC6.0可以在 XP 下完美运行,不用进行额外的设置。在Win7和Win10下,设置兼容模式才可以运行。Win8相对麻烦一些,要先修改 MSDEV.EXE 的名字,再设置兼容模式。通过以上的设置如果还不能使用,那么请更换其它编译器,或者安装 Windows XP 吧,实在是没办法了。VC 6.0 太老了,在高版本的 Windows 系统中有时候就是死活都用不了,谁也没辙,微软早就不维护它了,也不再对它进行升级或者打补丁了,只能说明你的教学资料或者教学体制太落后了。

数据结构和算法教程(C语言版) 1万+

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

VC6.0(VC++6.0)下载安装使用教程(建议新手收藏)

微软原版的 VC6.0 已经不容易找到,网上提供的都是经过第三方修改的版本,删除了一些使用不到的功能,增强了兼容性。这里我们使用 VC6.0 完整绿色版,它能够支持一般的 C/C++ 应用程序开发以及计算机二级考试。该软件仅 31M,下载快速,安装简单,无需设置各种组件,还能够卸载干净。VC6.0 能够在 XP 下很好的运行,无需进行额外的设置,但在 Win7、Win8 和 Win10 下,安装完成后还要修改兼容模式才可以。

zdoeo的博客 1万+

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

VC6.0(VC++6.0)下载地址和安装教程(图解)

截止到 2016年07月06日,C 语言中文网提供的 VC6.0安装包,下载量已超过150万次,收到反馈超过300条。 微软原版的 VC6.0 已经不容易找到,网上提供的都是经过第三方修改的版本,删除了一些使用不到的功能,增强了兼容性。这里我们使用 VC6.0 完整绿色版,它能够支持一般的 C/C++ 应用程序开发以及计算机二级考试。 VC6.0 完整绿色版下载地址公众号后台回复:VC,即可获取安装包。 该软件仅 31M,下载快速,安装简单,无需设置各种组件,还能够卸载干净。 VC6.0 能够在 XP 下

AI悦创·编程私教1v1 1万+

编程实现向开始菜单和桌面上添加快捷方式

 编程实现向开始菜单和桌面上添加快捷方式  通过InstallShield for Microsoft Visual C++ 6等应用程序创建一个安装程序来建立菜单项或快捷方式并不复杂,但是如果希望做一个无须安装的“绿色软件”,那么就只有自己编程实现了。 一、快捷方式的实质  Windows的快捷方式实际上是一个带有扩展名LNK的数据文件,其中包含有用于访问Windows某一对象(即在

bqw2008的专栏 4578

vc6.0安装

VC6.0(VC++6.0)下载地址和安装教程(图解) <使用Dev C++编写C语言程序使用VC6.0编写C语言程序> C语言中文网推出辅导班啦,包括「C语言辅导班、C++辅导班、算法/数据结构辅导班」,全部都是一对一教学:一对一辅导 + 一对一答疑 + 布置作业 + 项目实践 + 永久学习。QQ在线,随时响应! 截止到2016年07月06日,C语言中文网提供的V...

xinlongkun的博客 798

VC++ 操作Windows快捷方式

  声明:本文是参考网友博文,然后自己实践整理所得,转载请注明出处!   Windows的快捷方式实际上是一个带有扩展名LNK的数据文件,其中包含有用于访问Windows某一对象(即在资源管理器中所能浏览的所有对象,包括文件,文件夹,驱动器及打印机等)的有关信息,如目标对象的路径和名称,工作目录,要传递的命令行参数,运行时的初始显示状态,图标及其快捷键等。通过在快捷方式上单击鼠标右键并在弹出菜单中...

weixin_34146410的博客 272
上一篇: 基于Richedit的简单IM实现之一:界面部分
下一篇: 打包软件的思路
小人物2014
博客等级 码龄19年 66粉丝 99原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值