create_shortcut_ex() creates shortcuts to Windows applications or files
create_PIF() creates shortcuts to DOS applications.
#include "shlguid.h"
#include "shlobj.h"
static const char W95_SHORTCUT_TYPE[] = "LNK"; // File type for a Windows 95 shortcut.
static const char W95DOS_SHORTCUT_TYPE[] = "PIF"; // File type for a Windows 95 shortcut
// to DOS app.
#define CUST_ERR 0x20000000
#define SHIE_NOT_A_SHORTCUT (CUST_ERR+1) // Passed file name not a W95 shortcut
#define SHIE_SHELL_INTERFACE_ERROR (CUST_ERR+2) // Error calling W95 shell
#define SHIE_OUT_OF_MEMORY (CUST_ERR+3) // Not enough free memory for call
#define SHIE_CANT_FIND_FILE (CUST_ERR+4) // File (or shortcut target) not found
#define SHIE_NAME_TOO_LONG (CUST_ERR+5) // File name exceeds MAX_PATH
#define SHIE_AUTORUN_ERROR (CUST_ERR+6) // CD inserted with AUTORUN info
#define SHIE_NOT_ABSOLUTE_PATH (CUST_ERR+7) // Not full absolute path
#define SHIE_NO_CD_DRIVE (CUST_ERR+8) // No CD Drive configured
#define SHIE_USER_CANCEL (CUST_ERR+9) // Cancelled by user
DWORD pascal create_shortcut_ex(char *target_file, char *shortcut_name, char *description,
char *arguments, char *working_directory, char *iconfile,
int icon_no)
// Create a Windows 95 shortcut to a non-DOS application.
// Passed file name parameters must not exceed MAX_PATH in length. shortcut_name must have
// a .LNK extension. target_file parameter must be a full path to the target file. All
// parameters, other than the first two, are optional (ie. may be NULL or 0).
//
// If description is omitted, defaults to the link name less the .LNK extension.
// If working_directory is omitted, defaults to the directory of the target_file.
//
// Returns OK or error response.
{
IShellLink *pshell = NULL;
IPersistFile *ppersist = NULL;
WCHAR uni_name[MAX_PATH+1];
DWORD response;
HRESULT result;
char desc_buff[MAX_PATH+1];
char dir_buff[MAX_PATH+1];
// First check that the shortcut name has a .LNK extension
if (stricmp(get_filetype_ptr(shortcut_name, FALSE), W95_SHORTCUT_TYPE) != EQUAL)
return SHIE_NOT_A_SHORTCUT;
// Default the description, if necessary
if (description == NULL)
{
if (strlen(shortcut_name) > MAX_PATH)
return SHIE_NAME_TOO_LONG;
strcpy(desc_buff, shortcut_name);
desc_buff[strlen(shortcut_name) - strlen(W95_SHORTCUT_TYPE) - 1] = '\0';
description = desc_buff;
}
// If no working directory, default to the directory of the EXE file
if (working_directory == NULL)
{
char *ptr;
int len;
if ((len=strlen(target_file)) > MAX_PATH)
return SHIE_NAME_TOO_LONG;
if (len > 0)
{
strcpy(working_directory = dir_buff, target_file);
for (ptr = dir_buff+len-1; ptr>dir_buff; --ptr)
{
if (*ptr == '\\')
{
*ptr = '\0';
break;
}
if (*ptr == ':')
{
ptr[1] = '\0';
break;
}
}
}
if (ptr <= dir_buff)
{
return SHIE_NOT_ABSOLUTE_PATH;
}
}
// Before we start, we need a pointer to the IShellLink interface and thence to its
// PersistFile interface
if (!SUCCEEDED(CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
&IID_IShellLink, &pshell))
|| !SUCCEEDED(pshell->lpVtbl->QueryInterface(pshell, &IID_IPersistFile, &ppersist)) )
{
// Failed to get to the shell or its persist file interface
if (pshell != NULL)
pshell->lpVtbl->Release(pshell);
return SHIE_SHELL_INTERFACE_ERROR;
}
// Set the path to the target, description etc.
if (SUCCEEDED(result = pshell->lpVtbl->SetPath(pshell, target_file))
&& SUCCEEDED(result = pshell->lpVtbl->SetDescription(pshell, description))
&& SUCCEEDED(result = pshell->lpVtbl->SetWorkingDirectory(pshell, working_directory))
&& (arguments == NULL
|| SUCCEEDED(result = pshell->lpVtbl->SetArguments(pshell, arguments)))
&& (iconfile == NULL
|| SUCCEEDED(result = pshell->lpVtbl->SetIconLocation(pshell, iconfile, icon_no)))
)
{
// Convert file name to Unicode
MultiByteToWideChar(CP_ACP, 0, shortcut_name, -1, uni_name, sizeof uni_name);
// Save the shortcut
result=ppersist->lpVtbl->Save(ppersist, uni_name, TRUE);
}
if (SUCCEEDED(result))
response = OK;
else switch (result)
{
case E_OUTOFMEMORY:
response = SHIE_OUT_OF_MEMORY;
break;
case E_FAIL:
// Failed to load the shortcut
response = SHIE_CANT_FIND_FILE;
break;
default:
response = SHIE_SHELL_INTERFACE_ERROR;
break;
}
// All done and the response is in response. Free all the shell resources we grabbed
ppersist->lpVtbl->Release(ppersist);
pshell->lpVtbl->Release(pshell);
return response;
}
DWORD pascal create_PIF(char *target_file, char *shortcut_name, char *description)
// Create a PIF (Windows 95 shortcut to a DOS application).
// Passed parameters must not exceed MAX_PATH in length. shortcut_name must have a .PIF
// extension. The description is optional. If omitted, description defaults to the link
// name less the .PIF extension.
// Returns OK or error response
{
IShellLink *pshell = NULL;
IPersistFile *ppersist = NULL;
WCHAR uni_name[MAX_PATH+1];
DWORD response;
HRESULT result;
char desc_buff[MAX_PATH+1];
// First check that the shortcut name has a .LNK extension
if (stricmp(get_filetype_ptr(shortcut_name, FALSE), W95DOS_SHORTCUT_TYPE) != EQUAL)
return SHIE_NOT_A_SHORTCUT;
// Default the description, if necessary
if (description == NULL)
{
if (strlen(shortcut_name) > MAX_PATH)
return SHIE_NAME_TOO_LONG;
strcpy(desc_buff, shortcut_name);
desc_buff[strlen(shortcut_name) - strlen(W95DOS_SHORTCUT_TYPE) - 1] = '\0';
description = desc_buff;
}
// Before we start, we need a pointer to the IShellLink interface and thence to its
// PersistFile interface
if (!SUCCEEDED(CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
&IID_IShellLink, &pshell))
|| !SUCCEEDED(pshell->lpVtbl->QueryInterface(pshell, &IID_IPersistFile, &ppersist)) )
{
// Failed to get to the shell or its persist file interface
if (pshell != NULL)
pshell->lpVtbl->Release(pshell);
return SHIE_SHELL_INTERFACE_ERROR;
}
// Set the path to the target and the description
if (SUCCEEDED(result = pshell->lpVtbl->SetPath(pshell, target_file))
&& SUCCEEDED(result = pshell->lpVtbl->SetDescription(pshell, description)) )
{
// Convert file name to Unicode
MultiByteToWideChar(CP_ACP, 0, shortcut_name, -1, uni_name, sizeof uni_name);
// Save the shortcut
result=ppersist->lpVtbl->Save(ppersist, uni_name, TRUE);
}
if (SUCCEEDED(result))
response = OK;
else switch (result)
{
case E_OUTOFMEMORY:
response = SHIE_OUT_OF_MEMORY;
break;
case E_FAIL:
// Failed to load the shortcut
response = SHIE_CANT_FIND_FILE;
break;
default:
response = SHIE_SHELL_INTERFACE_ERROR;
break;
}
// All done and the response is in response. Free all the shell resources we grabbed
ppersist->lpVtbl->Release(ppersist);
pshell->lpVtbl->Release(pshell);
return response;
}