Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IShellLink::SetIconLocation translates my icon path into %Program Files% which is WRONG

Does anyone know how to correct for this behavior?

Currently, when our installer installs our application, it obtains an IShellLink, then loads it up with the data necessary for our shortcut icon (in the start menu & desktop), and then uses IPersistFile::Save to write out the shortcut.

The problem is that the path specified for the icon, via IShellLink::SetIconLocation, is transformed into using %ProgramFiles%... which... for x64, is WRONG.

I've noticed that lots of other 32 bit software has this failing under x64 - but then I assumed that they were using %ProgamFiles% themselves, as a literal element in their .lnk creation code. However, it appears to be that IShellLink is forcing this bug into existence, and I don't have a work-around (or perhaps that the link property editor in the shell is responsible for the problem and the underlying link is okay).

A few Google searches have turned up nothing... has anyone else encountered this or know of an article / example of how to force x64 windows to not muck this up?

Clarifying example:

hr = m_shell_link->SetIconLocation("C:\\Program Files (x86)\\Acme\\Prog.exe", 0);

Will result in a shortcut which has the correct icon, but when you press "Change icon" in the shortcut properties page, will report "Windows can't find the file %ProgramFiles%\Acme\Prog.exe.")

like image 212
Mordachai Avatar asked Jun 04 '10 17:06

Mordachai


3 Answers

Convert the name into a short filename, and it will only convert the drive letter, yet keep the correct path.

        PWCHAR pIcon = L"C:\\Program Files (x86)\\Myfoo\\Bar.exe";
        DWORD dwLen = GetShortPathName(pIcon, NULL, 0);
        PWCHAR pShort = NULL; 
        if (dwLen) {
            pShort = new WCHAR[dwLen];
            dwLen = GetShortPathName(pIcon, pShort, dwLen);
            if (!dwLen) {
                delete [] pShort;
                pShort = NULL;
            }
        }

        if (NULL == pShort) {
            psl->SetIconLocation(pIcon,iTmp);
        } else {
            psl->SetIconLocation(pShort,iTmp);
        }
        delete [] pShort;
like image 138
JustSomeDude Avatar answered Nov 06 '22 17:11

JustSomeDude


As user "pointoforder" points out in this issue report on GitHub, another fix is to unset the SLDF_HAS_EXP_ICON_SZ flag and remove the EXP_SZ_ICON_SIG data block from the IShellLinkDataList object. This comment has the according Delphi code snippet.

like image 31
sschuberth Avatar answered Nov 06 '22 17:11

sschuberth


When I was looking for a solution to create shortcuts in C# I found this post on StackOverflow.

Now I came across the problem described in this thread. I'm unsure where to post my solution but I guess this is the right place.

I added IShellLinkDataList and changed the Save() method as below:

#region IShellLinkDataList Interface
[ComImportAttribute()]
[GuidAttribute("45e2b4ae-b1c3-11d0-b92f-00a0c90312e1")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
private interface IShellLinkDataList
{
    void AddDataBlock(IntPtr pDataBlock);
    void CopyDataBlock(uint dwSig, out IntPtr ppDataBlock);
    void RemoveDataBlock(uint dwSig);
    void GetFlags(out uint pdwFlags);
    void SetFlags(uint dwFlags);
}
#endregion

private const uint SLDF_HAS_EXP_ICON_SZ = 0x00004000;
private const uint EXP_SZ_ICON_SIG = 0xA0000007;

public void Save(string linkFile)
{   
    // Save the object to disk
    uint flags;
    if (linkA == null)
    {
        ((IShellLinkDataList)linkW).GetFlags(out flags);
        flags = flags & ~SLDF_HAS_EXP_ICON_SZ;
        ((IShellLinkDataList)linkW).SetFlags(flags);
        ((IShellLinkDataList)linkW).RemoveDataBlock(EXP_SZ_ICON_SIG);
        ((IPersistFile)linkW).Save(linkFile, true);
        shortcutFile = linkFile;
    }
    else
    {
        ((IShellLinkDataList)linkA).GetFlags(out flags);
        flags = flags & ~SLDF_HAS_EXP_ICON_SZ;
        ((IShellLinkDataList)linkA).SetFlags(flags);
        ((IShellLinkDataList)linkA).RemoveDataBlock(EXP_SZ_ICON_SIG);
        ((IPersistFile)linkA).Save(linkFile, true);
        shortcutFile = linkFile;
    }
}
like image 35
ThielHater Avatar answered Nov 06 '22 18:11

ThielHater