SHORTCUT: A SampleThat Manipulates Shortcuts

SHORTCUT is a simple MFC-based application in which the user can create a shortcut for a file selected from the current directory. When the user chooses the Create Shortcut menu item, the dialog box shown in Figure 9-4 displays a list of the files in the current directory. I filled the list box with file options by using a call to DlgDirList in my handler for the WM_INITDIALOG message, OnInitDialog. To create a shortcut to a text file named README, for example, the user would choose README.TXT from the list.

Figure 9-4.

The Create A Shortcut dialog box in the SHORTCUT sample.

The user indicates whether the shortcut should appear on the desktop by checking the Place Shortcut On Desktop option in the dialog box. After the user selects a file from the list and clicks the OK button, the application checks to determine whether the shortcut should be located on the desktop or in the current directory. If it should be placed on the desktop, the shortcut's default location is in a subdirectory (called DESKTOP) of the directory that contains Windows 95. For example, if your installation of Windows 95 is in the C:\WINDOWS directory, the shortcut file is placed in C:\WINDOWS\DESKTOP. This subdirectory is hidden; you can find it by opening an MS-DOS command prompt and typing attrib desktop or by going into your Windows installation directory and typing dir desktop.

If your system is configured to use a different profile per user, the location of the shortcut is different. You can set up a different profile per user by using the Passwords application in Control Panel. Open Passwords, click the User Profiles tab, and then check the Include Desktop Icons And Network Neighborhood Contents In User Settings option. This stores the desktop icons (and your desktop shortcut) in the Registry's Desktop subdirectory under HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\-Explorer\Shell Folders. The Desktop key will contain the fully qualified path to the icons. (Refer to Chapter 10 for a discussion of the Registry and its keys.) For example, if I set up my computer to support per-user profiles, the Desktop key will be located under C:\WINDOWS\PROFILES\NANCYCL\DESKTOP. (The SHORTCUT sample does not support per-user profiles, so in this case all shortcuts targeted to the desktop are placed in the DESKTOP subfolder of the Windows 95 installation directory.)

The shortcut name is completed by retrieving the selected file through a call to DlgDirSelect, stripping off the filename extension, and replacing it with the LNK extension. All shortcuts have the LNK extension.

Then it's time to get down to the real work of creating the shortcut. The CreateShortCut::CreateIt function actually performs the task. It takes three parameters:

Because this function makes a call to CoCreateInstance, it is assumed that CoInitialize has already been called. As you can see in the following code, this function uses both the IPersistFile interface, for actually saving the shortcut in the system, and the IShellLink interface, for storing the path and the description of the shortcut target:

HRESULT CreateShortCut::CreateIt (LPCSTR pszShortcutFile,
LPSTR pszLink, 
LPSTR pszDesc)
{
HRESULT hres;
IShellLink *psl;

// Create an IShellLink object and get a pointer to the IShellLink
// interface (returned from CoCreateInstance).
hres = CoCreateInstance (CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
IID_IShellLink, (void **)&psl);
if (SUCCEEDED (hres))
{
IPersistFile *ppf;

// Query IShellLink for the IPersistFile interface for
// saving the shortcut in persistent storage.
hres = psl->QueryInterface (IID_IPersistFile, (void **)&ppf);
if (SUCCEEDED (hres))
{
WORD wsz [MAX_PATH]; // buffer for Unicode string

// Set the path to the shortcut target.
hres = psl->SetPath (pszShortcutFile);

if (! SUCCEEDED (hres))
AfxMessageBox ("SetPath failed!");

// Set the description of the shortcut.
hres = psl->SetDescription (pszDesc);

if (! SUCCEEDED (hres))
AfxMessageBox ("SetDescription failed!");

// Ensure that the string consists of ANSI characters.
MultiByteToWideChar (CP_ACP, 0, pszLink, -1, wsz, MAX_PATH);

// Save the shortcut via the IPersistFile::Save member function.
hres = ppf->Save (wsz, TRUE);

if (! SUCCEEDED (hres))
AfxMessageBox (“Save failed!”);

// Release the pointer to IPersistFile.
ppf->Release ();
}
// Release the pointer to IShellLink.
psl->Release ();
}
return hres;
}

Once you have created the shortcut, you might need to access and manipulate it programmatically. This is referred to as resolving the shortcut. I added a function to my sample that demonstrates how you can resolve a shortcut. I used the same type of dialog box that I used for creating the shortcut—and used almost exactly the same code to fill the dialog box with the names of the files in the current directory and prompt the user to choose a shortcut to resolve. The only difference was a simple check to ensure that the user actually picked a LNK file:

void ResolveShortCut::OnOK () 
{
char szFile [MAX_PATH];

// Get the selected item in the list box.
DlgDirSelect (szFile, IDC_LIST1);

// Find out whether it is a LNK file.
if (strstr (szFile, “.lnk”) != NULL)
// Make the call to ResolveShortcut::ResolveIt here.
ResolveIt (m_hWnd, szFile);

CDialog::OnOK ();
}

The ResolveShortCut::ResolveIt function resolves the shortcut. This function takes two parameters:

Like the function that created the shortcut, this function calls CoCreateInstance and assumes that CoInitialize has already been called. Notice that the following code needs to call into the IPersistFile interface. The IShellLink object implements this interface to store shortcut information. To get the path information requested later in the code, the shortcut information must be loaded first. Failing to load the shortcut information causes the calls to GetPath and GetDescription to fail.

HRESULT ResolveShortCut::ResolveIt (HWND hwnd, LPCSTR
pszShortcutFile)
{
HRESULT hres;
IShellLink *psl;
char szGotPath [MAX_PATH];
char szDescription [MAX_PATH];
WIN32_FIND_DATA wfd;

// Get a pointer to the IShellLink interface.
hres = CoCreateInstance (CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
IID_IShellLink, (void **)&psl);
if (SUCCEEDED (hres))
{
IPersistFile *ppf;

// Get a pointer to the IPersistFile interface.
hres = psl->QueryInterface (IID_IPersistFile, (void **)&ppf);

if (SUCCEEDED (hres))
{
WORD wsz [MAX_PATH]; // buffer for Unicode string

// Ensure that the string consists of Unicode characters.
MultiByteToWideChar (CP_ACP, 0, pszShortcutFile, -1, wsz,
MAX_PATH);

// Load the shortcut.
hres = ppf->Load (wsz, STGM_READ);

if (SUCCEEDED (hres))
{
// Resolve the shortcut.
hres = psl->Resolve (hwnd, SLR_ANY_MATCH);
if (SUCCEEDED (hres))
{
strcpy (szGotPath, pszShortcutFile);
// Get the path to the shortcut target.
hres = psl->GetPath (szGotPath, MAX_PATH,
(WIN32_FIND_DATA *)&wfd, SLGP_SHORTPATH);
if (! SUCCEEDED (hres))
AfxMessageBox ("GetPath failed!");
else
AfxMessageBox (szGotPath);
// Get the description of the target.
hres = psl->GetDescription (szDescription, MAX_PATH);
if (! SUCCEEDED (hres))
AfxMessageBox ("GetDescription failed!");
else
AfxMessageBox (szDescription);
}
}
// Release the pointer to IPersistFile.
ppf->Release ();
}
// Release the pointer to IShellLink.
psl->Release ();
}
return hres;
}