Luccas Clezar
Luccas Clezar

Reputation: 1074

Shortcut of universal app

I'm creating a Xamarin.Forms app and I want to have an option to create a Desktop shortcut (I already implemented on Android).

I have a protocol that I can use to launch my app, so this is not a concern, and I already saved another type of file in the Desktop to test, my only problem is that I can't create a .lnk file.

In my researches I saw a lot of explanations as to how to create a .lnk file programmatically, but all tutorials were related to WindowsForms or PowerShell scripts (as far as I know, UWP doesn't allow executing PS scripts).

So, is there a way of creating Desktop Shortcuts?


Edit after answer

When I create the shortcut using the Dialog, everything is fine, but when I create it in the app, the shortcut's icon doesn't change to my app's icon, it displays a confirmation when I run it and after I confirm, it displays an error message saying The target "" of this Internet Shortcut is not valid.

Shortcut with default icon

But if I change the file in any way (rename it, copy and paste it or change it's contents), it changes the icon to the correct one and when I confirm the dialog, it opens my app as expected. This proves that nothing is wrong with the file or its contents, but I don't know how to workaround this.


Edit

StorageFile shortcutFile = null;

try
{
    shortcutFile = await ApplicationData.Current.LocalFolder.GetFileAsync("shortcut.url");
}
catch (Exception) { }

if (shortcutFile == null)
{
    shortcutFile = await ApplicationData.Current.LocalFolder.CreateFileAsync("shortcut.tmp", CreationCollisionOption.ReplaceExisting);
    await FileIO.WriteLinesAsync(shortcutFile, new string[] { "[InternetShortcut]", "URL=yourapp:///" });

    string newShortcutName = shortcutFile.Path.Substring(0, shortcutFile.Path.Length - 4) + ".url";
    File.Move(shortcutFile.Path, newShortcutName);

    shortcutFile = await ApplicationData.Current.LocalFolder.GetFileAsync(newShortcutName.SplitWithoutEmpty('\\').Last());
}

FolderPicker savePicker = new FolderPicker
{
    SuggestedStartLocation = PickerLocationId.Desktop
};
savePicker.FileTypeFilter.Add(".url");
StorageFolder folder = await savePicker.PickSingleFolderAsync();

if (folder != null)
{
    // Works fine until here, then it throws an exception (Permission Denied)
    await shortcutFile.CopyAsync(folder, shortcutFile.Name);
}

Upvotes: 2

Views: 2170

Answers (3)

Nathan Smith
Nathan Smith

Reputation: 1813

Another option might be to add an ExecutionAlias and create the shortcut directly to the alias.

In Package.appxmanifest

xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"

<Extensions>
  <uap5:Extension Category="windows.appExecutionAlias" EntryPoint="Windows.FullTrustApplication" Executable="MyApp\MyApp.exe">
    <uap5:AppExecutionAlias>
      <uap5:ExecutionAlias Alias="MyApp.exe" />
    </uap5:AppExecutionAlias>
  </uap5:Extension>
</Extensions>

Then make the shortcut for MyApp.exe

Upvotes: 0

user3190036
user3190036

Reputation: 533

I have found a much simpler way.

You just need to create a normal shortcut with the target C:\Windows\explorer.exe shell:AppsFolder{PackageFamilyName}!App

I use the following code to generate the shortcut the first time the application runs (You'll need to add a COM reference to Windows Scripting Host):

string shortcutLocation = Path.Combine(Environment.SpecialFolder.Desktop, shortcutName + ".lnk");
WshShell shell = new();
IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(shortcutLocation);

shortcut.IconLocation = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "icon.ico");
shortcut.Arguments = $"shell:AppsFolder\\{insert your PackageFamilyName}!App";
shortcut.TargetPath = Path.Combine(Environment.GetEnvironmentVariable("windir"), "explorer.exe");
shortcut.Save();

PackageFamilyName: Your app family name should be a giant alphanumeric hash, the easiest way to get that is to install it somewhere and use the powershell command Get-AppxPackage https://learn.microsoft.com/en-us/powershell/module/appx/get-appxpackage?view=windowsserver2019-ps. It can also be obtained in code with Package.Current.Id.FamilyName.

Icon location: This is not necessary it should use the default icon for your app. I changed this because it used a version with an ugly blue background. Adding an icon to your build output folder and linking it as above lets you choose the icon.

It should also be noted that the mouseover text for the shortcut is windows explorer. Please let me know if anyone finds a way around this.

Upvotes: 4

Martin Zikmund
Martin Zikmund

Reputation: 39072

The Windows 10 alternative to desktop shortcut is a Live Tile, which is basically a live version of an Android icon. You can create a desktop shortuct by dragging the Live Tile to the desktop manually.

If you want a programmatic way and have already a registered URI protocol, you should be able to create a url shortcut instead of classic one. Let's see this step by step:

You can create a URL shortuct manually using the dialog:

manual shortcut creation

Now this will create a .url file on your desktop, which behaves as a shortcut. You can see it in the listing of dir command:

dir command output

Now try to rename the file to have a different extension using the ren command, for example:

ren YourApp.url YourApp.urltext

You can now open this file normally in a text editor to see its contents:

[{000352A0-0000-0000-C000-000000000012}]
Prop3=19,0
[InternetShortcut]
IDList=
URL=yourapp:///

From my testing it seems that the first two rows are unnecessary and the IDList property is also redundant. This means you can just do with the following:

[InternetShortcut]
URL=yourapp:///

If you rename the file back to have .url extension and double-click it, it will launch the app associated with the yourapp protocol, which is exactly what you need.

So to do this programmatically, you have to save a .url file with the contents we just came up with and save it where the user wants to have the shortcut created.

There is one disadvantage, that you have to keep in mind - UWP apps are sandboxed, so you cannot directly create files on desktop without the user's consent. The best approach I can see now is to use a FileSavePicker to let the user choose a folder where she wants to save the shortcut. It is still quite user friendly and it even gives her the flexibility to have the shortcut elsewhere than on the desktop.

Update

I have found this SO answer with an extremely detailed analysis of this problem. It thoroughly explains why we are hitting these problems - the URL files are interpreted by Internet Explorer and once interpreted, the URL info is stored as NTFS metadata. I would suggest creating the file with a temporary extension first and only then rename it to .url. I am not at my computer right now, but I will try when I get there :-)

Update 2

After some playing I have come up with the following and it "partially works on my PC". The shortcut is successfully created and even has the right icon, BUT each time it is opened it shows the "file from different computer" confirmation dialog, which can be turned off only through Properties.

FileSavePicker savePicker = new FileSavePicker
{
    SuggestedStartLocation = PickerLocationId.Desktop
};
savePicker.FileTypeChoices.Add("Shortcut", new [] { ".url" });
var saveFile = await savePicker.PickSaveFileAsync();

if (saveFile != null)
{
    await FileIO.WriteLinesAsync(
       saveFile, 
       new string[] { "[InternetShortcut]", "URL=yourapp:///" });
}

Upvotes: 2

Related Questions