Search code examples
c++winapishortcut-file

How do I manipulate the icon for an existing desktop shortcut using Win32?


I want to be able to get the current .ico file being used for a shortcut and then change it to a different .ico file temporarily. I was planning on parsing the .lnk files manually, but I thought I might ask for an easier way here first.


Solution

  • Use the IShellLink interface. Here are examples from MSDN:

    Shell Links

    // CreateLink - Uses the Shell's IShellLink and IPersistFile interfaces 
    //              to create and store a shortcut to the specified object. 
    //
    // Returns the result of calling the member functions of the interfaces. 
    //
    // Parameters:
    // lpszPathObj  - Address of a buffer that contains the path of the object,
    //                including the file name.
    // lpszPathLink - Address of a buffer that contains the path where the 
    //                Shell link is to be stored, including the file name.
    // lpszDesc     - Address of a buffer that contains a description of the 
    //                Shell link, stored in the Comment field of the link
    //                properties.
    
    #include "stdafx.h"
    #include "windows.h"
    #include "winnls.h"
    #include "shobjidl.h"
    #include "objbase.h"
    #include "objidl.h"
    #include "shlguid.h"
    
    HRESULT CreateLink(LPCWSTR lpszPathObj, LPCSTR lpszPathLink, LPCWSTR lpszDesc) 
    { 
        HRESULT hres; 
        IShellLink* psl; 
    
        // Get a pointer to the IShellLink interface. It is assumed that CoInitialize
        // has already been called.
        hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); 
        if (SUCCEEDED(hres)) 
        { 
            IPersistFile* ppf; 
    
            // Set the path to the shortcut target and add the description. 
            psl->SetPath(lpszPathObj); 
            psl->SetDescription(lpszDesc); 
    
            // Query IShellLink for the IPersistFile interface, used for saving the 
            // shortcut in persistent storage. 
            hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); 
    
            if (SUCCEEDED(hres)) 
            { 
                WCHAR wsz[MAX_PATH]; 
    
                // Ensure that the string is Unicode. 
                MultiByteToWideChar(CP_ACP, 0, lpszPathLink, -1, wsz, MAX_PATH); 
    
                // Add code here to check return value from MultiByteWideChar 
                // for success.
    
                // Save the link by calling IPersistFile::Save. 
                hres = ppf->Save(wsz, TRUE); 
                ppf->Release(); 
            } 
            psl->Release(); 
        } 
        return hres; 
    

    // ResolveIt - Uses the Shell's IShellLink and IPersistFile interfaces 
    //             to retrieve the path and description from an existing shortcut. 
    //
    // Returns the result of calling the member functions of the interfaces. 
    //
    // Parameters:
    // hwnd         - A handle to the parent window. The Shell uses this window to 
    //                display a dialog box if it needs to prompt the user for more 
    //                information while resolving the link.
    // lpszLinkFile - Address of a buffer that contains the path of the link,
    //                including the file name.
    // lpszPath     - Address of a buffer that receives the path of the link
                      target, including the file name.
    // lpszDesc     - Address of a buffer that receives the description of the 
    //                Shell link, stored in the Comment field of the link
    //                properties.
    
    #include "stdafx.h"
    #include "windows.h"
    #include "shobjidl.h"
    #include "shlguid.h"
    #include "strsafe.h"
    
    HRESULT ResolveIt(HWND hwnd, LPCSTR lpszLinkFile, LPWSTR lpszPath, int iPathBufferSize) 
    { 
        HRESULT hres; 
        IShellLink* psl; 
        WCHAR szGotPath[MAX_PATH]; 
        WCHAR szDescription[MAX_PATH]; 
        WIN32_FIND_DATA wfd; 
    
        *lpszPath = 0; // Assume failure 
    
        // Get a pointer to the IShellLink interface. It is assumed that CoInitialize
        // has already been called. 
        hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); 
        if (SUCCEEDED(hres)) 
        { 
            IPersistFile* ppf; 
    
            // Get a pointer to the IPersistFile interface. 
            hres = psl->QueryInterface(IID_IPersistFile, (void**)&ppf); 
    
            if (SUCCEEDED(hres)) 
            { 
                WCHAR wsz[MAX_PATH]; 
    
                // Ensure that the string is Unicode. 
                MultiByteToWideChar(CP_ACP, 0, lpszLinkFile, -1, wsz, MAX_PATH); 
    
                // Add code here to check return value from MultiByteWideChar 
                // for success.
    
                // Load the shortcut. 
                hres = ppf->Load(wsz, STGM_READ); 
    
                if (SUCCEEDED(hres)) 
                { 
                    // Resolve the link. 
                    hres = psl->Resolve(hwnd, 0); 
    
                    if (SUCCEEDED(hres)) 
                    { 
                        // Get the path to the link target. 
                        hres = psl->GetPath(szGotPath, MAX_PATH, (WIN32_FIND_DATA*)&wfd, SLGP_SHORTPATH); 
    
                        if (SUCCEEDED(hres)) 
                        { 
                            // Get the description of the target. 
                            hres = psl->GetDescription(szDescription, MAX_PATH); 
    
                            if (SUCCEEDED(hres)) 
                            {
                                hres = StringCbCopy(lpszPath, iPathBufferSize, szGotPath);
                                if (SUCCEEDED(hres))
                                {
                                    // Handle success
                                }
                                else
                                {
                                    // Handle the error
                                }
                            }
                        }
                    } 
                } 
    
                // Release the pointer to the IPersistFile interface. 
                ppf->Release(); 
            } 
    
            // Release the pointer to the IShellLink interface. 
            psl->Release(); 
        } 
        return hres; 
    }
    

    In your case, you would:

    1. Create an instance of IShellLink

    2. query it for IPersistFile()

    3. call IPersistFile.Load() to set the .lnk filename

    4. call IShellLink.Resolve() to load the file

    5. call IShellLink.SetIconLocation() to set a new .ico filename

    6. Call IPersistFile.Save() to save the new .lnk file.