Creating W95 links

Sample code is given below for how to make shell calls to create Win95 shortcuts. I have not tried it on NT4 but I think the same principle applies.

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;
}

Return to my home page