Newer
Older
JumpListUtil / ShortcutUtil / MainForm.cs
using Entries;
using NativeHelpers;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Core;
using Windows.Management.Deployment;

namespace ShortcutUtil
{
    public partial class MainForm : Form
    {
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                if (!designMode)
                {
                    cp.ExStyle |= 0x02000000;  // Turn on WS_EX_COMPOSITED
                }
                return cp;
            }
        }

        private EntryList entryList;

        private Font defaultFont;
        private Font nameFont;
        private Brush fontBrush;

        private AppResolver appResolver;

        private EntryListItem listLayout;

        private HelperLocation helperLocation;

        private bool designMode;

        public MainForm(EntryList entryList, AppResolver appResolver, HelperLocation helperLocation)
        {
            designMode = System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime;

            this.helperLocation = helperLocation;

            InitializeComponent();

            typeof(ListBox).InvokeMember("DoubleBuffered",
                BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
                null, entryListBox, new object[] { true });

            DoubleBuffered = true;

            Text = entryList.FilePath == null ? "New shortcut file" : entryList.FilePath;

            defaultFont = defaultShortcutLabel.Font;
            nameFont = new Font(defaultFont, FontStyle.Bold);
            fontBrush = new SolidBrush(defaultShortcutLabel.ForeColor);

            this.entryList = entryList;
            this.appResolver = appResolver;

            entryListBox.Items.AddRange(this.entryList.Entries.ToArray());

            if (this.entryList.Entries.Count > 0)
            {
                entryListBox.SelectedIndex = 0;
            }

            

            listLayout = new EntryListItem(entryListBox);
            listLayout.TestButton.OnClick += ClickedTestButton;

            helperLocationText.Text = helperLocation.Location;

            UpdateHelperValidity();
            
        }

        private void UpdateHelperValidity()
        {
            bool valid = helperLocation.Valid;
            helperLocationText.ForeColor = valid ? SystemColors.ControlText : Color.Red;
            createButton.Enabled = valid && entryList.Entries.Count > 0;
        }

        private void ClickedTestButton(int index)
        {
            try
            {
                Entry entry = entryList.Entries[index];
                ProcessStartInfo startInfo = new ProcessStartInfo
                {

                    FileName = entry.TargetExpanded,
                    Arguments = entry.ArgumentsExpanded,
                    UseShellExecute = true,
                };
                Process.Start(startInfo);
            }
            catch (Exception e)
            {
                MessageBox.Show("Couldn't start entry: " + e.ToString());
            }
            
        }

        private Dictionary<Tuple<string, int, Size>, Icon> iconsByFileIndex = new Dictionary<Tuple<string, int, Size>, Icon>();
        private Icon GetIcon(string file, int index, Size preferred)
        {
            Tuple<string, int, Size> key = new Tuple<string, int, Size>(file, index, preferred);

            
            
            if (!iconsByFileIndex.ContainsKey(key))
            {
                Icon icon = null;

                try
                {
                    icon = new Icon(file, preferred);
                }
                catch (Exception)
                {
                    icon = null;
                }
                if (icon == null)
                {
                    try
                    {
                        icon = IconExtractor.Extract(file, index, true);
                    }
                    catch (Exception)
                    {
                        icon = null;
                    }
                }

                iconsByFileIndex.Add(key, icon);

                /*
                if (icon == null)
                {
                    MessageBox.Show("Couldn't load icon " + index + " from " + file);
                }
                */
            }

            return iconsByFileIndex[key];
        }

        private static readonly Size IconSize = new Size(64, 64);
        private static readonly Size ItemPadding = new Size(6, 6);

        private void entryListBox_DrawItem(object sender, DrawItemEventArgs e)
        {
            int index = e.Index;

            //RectangleF baseRectF = new RectangleF(new PointF(), e.Bounds.Size);
            Rectangle baseRect = new Rectangle(new Point(), e.Bounds.Size);

            using (Bitmap b = new Bitmap(baseRect.Width, baseRect.Height))
            {
                Graphics g = Graphics.FromImage(b);

                Brush backBrush = new SolidBrush(e.BackColor);
                g.FillRectangle(backBrush, baseRect);
                backBrush.Dispose();

                if ((e.State & DrawItemState.Focus) == DrawItemState.Focus
                    && (e.State & DrawItemState.NoFocusRect) != DrawItemState.NoFocusRect)
                    ControlPaint.DrawFocusRectangle(g, baseRect, ForeColor, BackColor);

                listLayout.LayoutFromList();

                if (index >= 0 && index < entryList.Entries.Count)
                {
                    Rectangle iconBounds = new Rectangle(new Point(ItemPadding), IconSize);

                    Size textSize = new Size(0, nameFont.Height + 2);

                    Entry entry = entryList.Entries[index];

                    Icon icon = GetIcon(entry.Icon, entry.IconIndex, IconSize);
                    if (icon != null)
                    {
                        g.DrawIcon(icon, iconBounds);
                    }

                    PointF textLocation = new PointF(IconSize.Width + ItemPadding.Width * 2, ItemPadding.Width);

                    g.DrawString(index + ": " + entry.Name, nameFont, fontBrush, textLocation);
                    textLocation = PointF.Add(textLocation, textSize);

                    g.DrawString(entry.Target + " " + entry.Arguments, defaultFont, fontBrush, textLocation);
                    textLocation = PointF.Add(textLocation, textSize);

                    g.DrawString(entry.Description, defaultFont, fontBrush, textLocation);
                    textLocation = PointF.Add(textLocation, textSize);

                    listLayout.TestButton.RenderToItem(index, g);
                }


                e.Graphics.DrawImage(b, e.Bounds.Location);
            }
        }

        private void entryListBox_MeasureItem(object sender, MeasureItemEventArgs e)
        {
            e.ItemHeight = GetItemSize().Height;
        }

        public Size GetItemSize()
        {
            return Size.Add(IconSize, new Size(0, ItemPadding.Height * 2));
        }

        private SearchForm search = new SearchForm();
        private async void appButton_Click(object sender, EventArgs e)
        {
            appButton.Enabled = false;

            PackageManager pm = new PackageManager();

            List<Tuple<string, string>> options = new List<Tuple<string, string>>();

            foreach (Package p in pm.FindPackagesForUser(string.Empty))
            {
                var apps = await p.GetAppListEntriesAsync().AsTask();
                foreach (AppListEntry app in apps)
                {
                    options.Add(new Tuple<string, string>(app.AppUserModelId, app.DisplayInfo.DisplayName + ": " + app.AppUserModelId));
                }
            }

            string selectedAppUserModelId = search.GetSelection("Select UWP App", "Available Apps:", options);

            if (selectedAppUserModelId != null)
            {
                appUserModelIdBox.Text = selectedAppUserModelId;
            }

            appButton.Enabled = true;
        }

        private static bool GetWindowHandle(IntPtr windowHandle, ref object windowHandles)
        {
            ((ArrayList)windowHandles).Add(windowHandle);
            return true;
        }

        private void hwndButton_Click(object sender, EventArgs e)
        {
            hwndButton.Enabled = false;

            object handles = new ArrayList();
            NativeCalls.EnumWindows(GetWindowHandle, ref handles);

            List<Tuple<IntPtr, string, string>> titles = new List<Tuple<IntPtr, string, string>>();
            foreach (object ptrObj in (ArrayList)handles)
            {
                IntPtr ptr = (IntPtr)ptrObj;
                if (NativeCalls.IsAltTabWindow(ptr))
                {
                    string title = NativeCalls.GetWindowTextManaged(ptr);
                    if (title != "")
                    {
                        /*
                        IPropertyStore store;
                        Guid guid = NativeValues.IID_IPropertyStore;
                        NativeCalls.VerifySucceeded((uint)NativeCalls.SHGetPropertyStoreForWindow(ptr, ref guid, out store));

                        using (PropVariant pv = new PropVariant())
                        {
                            NativeCalls.VerifySucceeded(store.GetValue(NativeValues.AppUserModelIDKey, pv));

                            string appUserModelId = pv.Value;
                            if (appUserModelId == null)
                            {
                                uint processId;
                                NativeCalls.GetWindowThreadProcessId(ptr, out processId);

                                IntPtr handle = NativeCalls.OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, (int)processId);

                                try
                                {
                                    appUserModelId = NativeCalls.GetApplicationUserModelIdManaged(handle);
                                }
                                catch (Exception) { }
                            }

                            
                        }      
                        */

                        string appUserModelId = appResolver.GetAppIDForWindow(ptr);
                        if (appUserModelId == null)
                        {
                            uint processId;
                            NativeCalls.GetWindowThreadProcessId(ptr, out processId);

                            appUserModelId = appResolver.GetAppIDForProcess(processId);
                        }

                        if (appUserModelId != null)
                        {
                            titles.Add(new Tuple<IntPtr, string, string>(ptr, title, appUserModelId == null ? "NULL" : appUserModelId));
                        }                            
                    }

                }
            }

            string selectedAppUserModelId = search.GetSelection("Select Window", "Available Windows:", titles.Select(t => new Tuple<string, string>(t.Item3, t.Item2 + ": " + t.Item3)).ToList());

            if (selectedAppUserModelId != null)
            {
                appUserModelIdBox.Text = selectedAppUserModelId;
            }

            hwndButton.Enabled = true;
        }

        public static string EncodeParameterArgument(string original)
        {
            if (string.IsNullOrEmpty(original))
                return original;
            string value = Regex.Replace(original, @"(\\*)" + "\"", @"$1\$0");
            value = Regex.Replace(value, @"^(.*\s.*?)(\\*)$", "\"$1$2$2\"", RegexOptions.Singleline);

            return value;
        }

        private void createButton_Click(object sender, EventArgs e)
        {
            if (helperLocation.Valid && entryList.Entries.Count > 0)
            {
                int index = entryListBox.SelectedIndex;
                if (index < 0 || index >= entryList.Entries.Count) index = 0;

                SaveFileDialog sfd = new SaveFileDialog();
                sfd.InitialDirectory = Environment.CurrentDirectory;
                sfd.Filter = "Shortcut (*.lnk)|*.lnk";
                sfd.Title = "Select output file";
                sfd.DefaultExt = "lnk";
                sfd.CheckPathExists = true;
                sfd.CreatePrompt = false;
                sfd.OverwritePrompt = true;

                DialogResult res = sfd.ShowDialog();

                if (res == DialogResult.OK)
                {
                    Entry selected = entryList.Entries[index];

                    ShellLink shellLink = new ShellLink();

                    shellLink.TargetPath = helperLocation.Location;
                    shellLink.Arguments = String.Join(" ",
                        new string[]
                        {
                            "-s", entryList.FilePath, "-i", "" + index, "-a", appUserModelIdBox.Text
                        }.Select(a => EncodeParameterArgument(a)));
                    shellLink.Description = selected.Description;
                    shellLink.IconLocation = new Tuple<string, int>(selected.Icon, selected.IconIndex);
                    shellLink.WorkingDirectory = Path.GetDirectoryName(entryList.FilePath);
                    shellLink.AppUserModelID = appUserModelIdBox.Text;

                    shellLink.Save(sfd.FileName);
                }
            }
        }

        private void helperLocationText_TextChanged(object sender, EventArgs e)
        {
            helperLocation.Location = helperLocationText.Text;
            UpdateHelperValidity();
        }

        private void helperLocationButton_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "Executables (*.exe)|*.exe";
            ofd.InitialDirectory = Path.GetDirectoryName(helperLocation.Location);
            ofd.Title = "Select helper executable";
            ofd.DefaultExt = "exe";
            ofd.Multiselect = false;
            ofd.CheckFileExists = true;
            DialogResult result = ofd.ShowDialog();
            if (result == DialogResult.OK)
            {
                helperLocation.Location = ofd.FileName;
                helperLocationText.Text = helperLocation.Location;
                UpdateHelperValidity();
            }
        }
    }
}