9

I'm trying to create a shortcut (.lnk) on my windows filesystem. The code I have is working fine. However, when I run the same console application on a Windows 2008R2 Server, it acts differently.

So I have my console application and here is what happens:
My program simply creates a shortcut on the desktop with a .docx extension and all goes fine on my local machine. When I run the same console application on my Server, it creates the same shortcut but the target got modified... it changed the target to a .doc file.

In other words, when I run the console app:

LocalMachine

Creates MyWordFile.lnk pointing to U:\test.docx

Server

Creates MyWordFile.lnk pointing to U:\test.doc


This is strange behaviour. Here is the code

Code

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;

namespace TestShortcut
{
    class Program
    {
        static void Main(string[] args)
        {
            //ShortcutTarget
            var sTarget = @"U:\test.docx";

            //ShortCutName
            var sName = @"MyWordFile.lnk";

            IShellLink link = (IShellLink)new ShellLink();
            link.SetPath(sTarget);

            IPersistFile file = (IPersistFile)link;
            string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
            file.Save(Path.Combine(desktopPath, sName), false);
        }
    }

    [ComImport]
    [Guid("00021401-0000-0000-C000-000000000046")]
    internal class ShellLink
    {
    }

    [ComImport]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("000214F9-0000-0000-C000-000000000046")]
    internal interface IShellLink
    {
        void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out IntPtr pfd, int fFlags);
        void GetIDList(out IntPtr ppidl);
        void SetIDList(IntPtr pidl);
        void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
        void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
        void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
        void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
        void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
        void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
        void GetHotkey(out short pwHotkey);
        void SetHotkey(short wHotkey);
        void GetShowCmd(out int piShowCmd);
        void SetShowCmd(int iShowCmd);
        void GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon);
        void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
        void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
        void Resolve(IntPtr hwnd, int fFlags);
        void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
    }
}



Update

I noticed that the 4th character gets removed from the target's extension. So, when i have a file "file.abcdef", the link points at "file.abc". Also spaces get replaced for underscores, so "my file.abcd" pointer becomes "my_file.abc"

1 Answer 1

5
+50

Creating a shortcut does not modify the name of the target.

You are seeing something else, "my_file.abc" is the short name of "my file.abcd". Such names are always created on a file system that still has the short name generation feature turned on. Almost all of them still do, even though it is very rarely necessary anymore today. Keeping "my file.abcd" compatible with programs that can only handle MS-Dos names does require that the space is replaced and the extension shortened to 3 letters. You also often see "~1" in such names, used to truncate the filename to 8 letters.

You can see the short names with DIR /x.

Exactly how you got to see the short name for this file is not obvious from the question. But the IShellLink interface declaration is certainly wrong, the methods are not listed in the correct order. That has pretty grave consequences for an ComInterfaceType.InterfaceIsIUnknown interface, you will call the wrong method at runtime. The Path property setter is actually the 2nd method. You are now calling the 18th method, it is not declared so very hard to guess what it might do. Well, something very surprising if it doesn't throw an exception, this qualifies :) It is not entirely implausible btw, the 17th method that was added was the getter for the Target property, added in a later version of the interface. You might accidentally hit the undocumented setter. Intentionally undocumented.

Just don't declare the interface yourself. Best way to do it is to use Project > Add Reference > Browse button > select C:\Windows\System32\Shell32.dll. Described in more detail in this post.

0

Not the answer you're looking for? Browse other questions tagged or ask your own question.