# encoding: ascii
# api: csharp
# title: Get-Link
# description: Sorry, but for now this is an all-in-one link creating/editing function: GET-Link and then call methods on it … feel free to polish it up and paste back. All credit goes to Ian Clegg for writing 90% of this code.
# version: 0.1
# type: class
# license: CC0
# x-poshcode-id: 2493
# x-archived: 2011-02-09T06:00:45
#
#
param([Parameter(ValueFromPipelineByPropertyName=$true)][Alias("PSPath")][string]$Path) 
begin {

Add-Type -TypeDef @"
using System;
using System.IO;
using System.Text;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace Huddled.Interop
{
   [Flags()] // SHELL_LINK_DATA_FLAGS
   public enum ShellLinkFlags : uint {
     Default                            = 0x00000000,
     HasIdList                          = 0x00000001,
     HasLinkInfo                        = 0x00000002,
     HasName                            = 0x00000004,
     HasRelpath                         = 0x00000008,
     HasWorkingdir                      = 0x00000010,
     HasArgs                            = 0x00000020,
     HasIconLocation                    = 0x00000040,
     Unicode                            = 0x00000080,
     ForceNoLinkInfo                    = 0x00000100,
     HasExpSz                           = 0x00000200,
     RunInSeparate                      = 0x00000400,
     HasLogo3Id                         = 0x00000800,
     HasDarwinId                        = 0x00001000,
     RunasUser                          = 0x00002000,
     HasExpIconSz                       = 0x00004000,
     NoPidlAlias                        = 0x00008000,
     ForceUncname                       = 0x00010000,
     RunWithShimlayer                   = 0x00020000,
     ForceNoLinktrack                   = 0x00040000,
     EnableTargetMetadata               = 0x00080000,
     DisableLinkPathTracking            = 0x00100000,
     DisableKnownfolderRelativeTracking = 0x00200000,
     NoKFAlias                          = 0x00400000,
     AllowLinkToLink                    = 0x00800000,
     UnaliasOnSave                      = 0x01000000,
     PreferEnvironmentPath              = 0x02000000,
     KeepLocalIdListForUncTarget        = 0x04000000,
     Valid                              = 0x07fff7ff,
     Reserved                           = (uint)0x80000000
   }


   // IShellLink.ShowCmd fFlags
   [Flags()]
   public enum ShowCmd
   {
      Normal = 1,
      Maximized = 3,
      Minnoactive = 7
   }

   // IShellLink.Resolve fFlags SLR_FLAGS
   [Flags()]
   public enum ResolveFlags
   {
      NoUI = 0x1,
      AnyMatch = 0x2,
      Update = 0x4,
      NoUpdate = 0x8,
      NoSearch = 0x10,
      NoTrack = 0x20,
      NoLinkInfo = 0x40,
      InvokeMsi = 0x80
   }

   // IShellLink.GetPath fFlags SLGP_FLAGS
   [Flags()]
   public enum GetPathFlags
   {
      ShortPath = 0x1,
      UncPriority = 0x2,
      RawPath = 0x4
   }

   // Win32 COORD
   [StructLayout(LayoutKind.Sequential)]
   public struct COORD
   {
      public short X;
      public short Y;
   }

   // IShellDataLink NT_CONSOLE_PROPS
   [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
   public struct NT_CONSOLE_PROPS
   {
      public int cbSize;             // Size of this extra data block
      public uint dwSignature;         // signature of this extra data block
      public ushort wFillAttribute;       // fill attribute for console
      public ushort wPopupFillAttribute;   // fill attribute for console popups
      public COORD dwScreenBufferSize;    // screen buffer size for console
      public COORD dwWindowSize;         // window size for console
      public COORD dwWindowOrigin;       // window origin for console
      public int nFont;
      public int nInputBufferSize;
      public COORD dwFontSize;
      public uint uFontFamily;
      public uint uFontWeight;
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
      public string FaceName;
      public uint uCursorSize;
      public bool bFullScreen;
      public bool bQuickEdit;
      public bool bInsertMode;
      public bool bAutoPosition;
      public uint uHistoryBufferSize;
      public uint uNumberOfHistoryBuffers;
      public bool bHistoryNoDup;
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
      public uint[] ColorTable;
   }

   // The CharSet must match the CharSet of the corresponding PInvoke signature
   [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
   public struct WIN32_FIND_DATA
   {
       public uint dwFileAttributes;
       public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
       public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
       public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
       public uint nFileSizeHigh;
       public uint nFileSizeLow;
       public uint dwReserved0;
       public uint dwReserved1;
       [MarshalAs(UnmanagedType.ByValTStr, SizeConst=260)]
       public string cFileName;
       [MarshalAs(UnmanagedType.ByValTStr, SizeConst=14)]
       public string cAlternateFileName;
   }


   [ComImport(),
   InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
   Guid("0000010B-0000-0000-C000-000000000046")]
   public interface IPersistFile
   {
      #region Methods inherited from IPersist

      void GetClassID(
         out Guid pClassID);

      #endregion

      [PreserveSig()]
      int IsDirty();

      void Load(
         [MarshalAs(UnmanagedType.LPWStr)] string pszFileName,
         int dwMode);

      void Save(
         [MarshalAs(UnmanagedType.LPWStr)] string pszFileName,
         [MarshalAs(UnmanagedType.Bool)] bool fRemember);

      void SaveCompleted(
         [MarshalAs(UnmanagedType.LPWStr)] string pszFileName);

      void GetCurFile(
         out IntPtr ppszFileName);

   }

   [ComImport(),
   InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
   Guid("000214F9-0000-0000-C000-000000000046")]
   public interface IShellLinkW
   {
      void GetPath(
         [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile,
         int cchMaxPath,
         out WIN32_FIND_DATA pfd,
         GetPathFlags 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,
         ResolveFlags fFlags);

      void SetPath(
         [MarshalAs(UnmanagedType.LPWStr)] string pszFile);

   }

   [ComImport(),
   InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
   Guid("45E2b4AE-B1C3-11D0-B92F-00A0C90312E1")]
   public interface IShellLinkDataList
   {
      void AddDataBlock(IntPtr pDataBlock);

      void CopyDataBlock(uint dwSig, out IntPtr ppDataBlock);

      void RemoveDataBlock(uint dwSig);

      void GetFlags(out uint dwFlags);

      void SetFlags(uint dwFlags);
   }

   public class ShellLink
   {
      private IShellLinkW _ShellLink;
      private NT_CONSOLE_PROPS _ConsoleProperties;
      private ushort _ScreenFill;
      private ushort _PopUpFill;

      public Color[] ConsoleColors = new Color[16];

      public ShellLink(string Path) {
         _ShellLink = Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("00021401-0000-0000-C000-000000000046"))) as IShellLinkW;
         if (File.Exists(Path))
         {
            IntPtr pConsoleProperties = IntPtr.Zero;
            ((IPersistFile)_ShellLink).Load(Path, 0);

            try
            {
               // TODO: Fix memory leak here
               ((IShellLinkDataList)_ShellLink).CopyDataBlock(0xA0000002, out pConsoleProperties);
               _ConsoleProperties = (NT_CONSOLE_PROPS)Marshal.PtrToStructure(pConsoleProperties, typeof(NT_CONSOLE_PROPS));
               _ScreenFill = _ConsoleProperties.wFillAttribute;
               _PopUpFill = _ConsoleProperties.wPopupFillAttribute;

               for (int i = 0; i < 16; i++)
               {
                  ConsoleColors[i] = ColorTranslator.FromWin32((int)_ConsoleProperties.ColorTable[i]);
               }
            }
            catch (Exception)
            {
            }

         }
         else
         {
            ((IPersistFile)_ShellLink).Save(Path, true);
         }

         // Initialize default Console Properties (TODO: Fix this bug too)
         if (_ConsoleProperties.dwSignature != 0xA0000002)
         {
            _ConsoleProperties = new NT_CONSOLE_PROPS();
            _ConsoleProperties.cbSize = Marshal.SizeOf(_ConsoleProperties);
            _ConsoleProperties.dwSignature = 0xA0000002;
            _ConsoleProperties.ColorTable = new uint[16];
            for (int i = 0; i < 16; i++)
            {
               _ConsoleProperties.ColorTable[i] = 0xffffffff;
            }
         }
      }
      
      public ShellLinkFlags Flags {
         get {
            uint flags;
            ((IShellLinkDataList)_ShellLink).GetFlags( out flags );
            return (ShellLinkFlags)flags;
         }
         set {
            ((IShellLinkDataList)_ShellLink).SetFlags( (uint)value );
         }
      }

      public void Save(string Path)
      {
         SetConsoleColors();
         ((IPersistFile)_ShellLink).Save(Path, true);
      }

      public void Save()
      {
         SetConsoleColors();
         ((IPersistFile)_ShellLink).Save(null, true);
      }

      public IShellLinkW GetShellLink()
      {
         return _ShellLink;
      }

      public string Path
      {
         get
         {
            StringBuilder sb = new StringBuilder(260);
            WIN32_FIND_DATA pfd = new WIN32_FIND_DATA();
            _ShellLink.GetPath(sb, 259, out pfd, GetPathFlags.RawPath);
            return sb.ToString();
         }
         set
         {
            _ShellLink.SetPath(value);
         }
      }

      public string Description
      {
         get
         {
            StringBuilder sb = new StringBuilder(2048);
            _ShellLink.GetDescription(sb, 2048);
            return sb.ToString();
         }
         set { _ShellLink.SetDescription(value); }
      }

      public string WorkingDirectory
      {
         get
         {
            StringBuilder sb = new StringBuilder(260);
            _ShellLink.GetWorkingDirectory(sb, 260);
            return sb.ToString();
         }
         set { _ShellLink.SetWorkingDirectory(value); }
      }

      public ShowCmd ShowCmd
      {
         get
         {
            int nShowCmd;
            _ShellLink.GetShowCmd(out nShowCmd);
            return (ShowCmd)Enum.ToObject(typeof(ShowCmd), nShowCmd);
         }
         set
         {
            _ShellLink.SetShowCmd((int)value);
         }
      }

      public bool QuickEditMode
      {
         get { return _ConsoleProperties.bQuickEdit; }
         set { _ConsoleProperties.bQuickEdit = value; }
      }

      public bool InsertMode
      {
         get { return _ConsoleProperties.bInsertMode; }
         set { _ConsoleProperties.bInsertMode = value; }
      }

      public bool AutoPosition
      {
         get { return _ConsoleProperties.bAutoPosition; }
         set { _ConsoleProperties.bAutoPosition = value; }
      }

      public void SetScreenBufferSize(short x, short y)
      {
         COORD c = new COORD();
         c.X = x;
         c.Y = y;
         _ConsoleProperties.dwScreenBufferSize = c;
      }

      public void SetWindowSize(short x, short y)
      {
         COORD c = new COORD();
         c.X = x;
         c.Y = y;
         _ConsoleProperties.dwWindowSize = c;
      }

      public uint CommandHistoryBufferSize
      {
         get { return _ConsoleProperties.uHistoryBufferSize; }
         set { _ConsoleProperties.uHistoryBufferSize = value; }
      }

      public uint CommandHistoryBufferCount
      {
         get { return _ConsoleProperties.uNumberOfHistoryBuffers; }
         set { _ConsoleProperties.uNumberOfHistoryBuffers = value; }
      }

      public byte ScreenBackgroundColor
      {
         set
         {
            _ScreenFill &= 0x00ff;
            _ScreenFill += (ushort)(value << 4);
         }

      }

      public byte ScreenTextColor
      {
         set
         {
            _ScreenFill &= 0xff00;
            _ScreenFill += value;
         }

      }

      public byte PopUpBackgroundColor
      {
         set
         {
            _PopUpFill &= 0x00ff;
            _PopUpFill += (ushort)(value << 4);
         }
      }

      public byte PopUpTextColor
      {
         set
         {
            _PopUpFill &= 0xff00;
            _PopUpFill += value;
         }
      }

      // This does more than console colors
      private void SetConsoleColors()
      {
         for (int i = 0; i < 16; i++)
         {
            _ConsoleProperties.ColorTable[i] = (uint)ColorTranslator.ToWin32(ConsoleColors[i]);
         }
         _ConsoleProperties.wFillAttribute = _ScreenFill;
         _ConsoleProperties.wPopupFillAttribute = _PopUpFill; ;

         IntPtr pConsoleProperties = Marshal.AllocCoTaskMem(_ConsoleProperties.cbSize);
         Marshal.StructureToPtr(_ConsoleProperties, pConsoleProperties, true);
         ((IShellLinkDataList)_ShellLink).RemoveDataBlock(0xA0000002);
         ((IShellLinkDataList)_ShellLink).AddDataBlock(pConsoleProperties);
      }
   }
}
"@ -ReferencedAssemblies System.Drawing

function global:Get-Link { 
param([Parameter(ValueFromPipelineByPropertyName=$true)][Alias("PSPath")][string]$Path) 
process {
   New-Object Huddled.Interop.ShellLink (Convert-Path $Path)
}
}

}
process { 
   Get-Link $Path
}
