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.
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);
}
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:
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:
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:///" });
}
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.
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
Related
I'm making a application for steam games. Because shortcuts steal zone from desktop ,so i make this project for this. I want add a "drop file here to add list" when he put to shortcut on program i need get the app id, then it will automatically add to list.I added the drop file script!! But i dont know how to get URL title from assembly..There is a screenshot. I want get url from shortcut like this.
SCREENSHOT 1
SCREENSHOT 2
!! I made the drop file script.. I just have problems at get url script.
Opening steam applications is very simple, if you know the location of your steam.exe.
System.Diagnostics.Process.Start(#"C:\Program Files (x86)\Steam\steam.exe","steam://rungameid/730");
In my case (i think its default as well) you can start an application just by giving the path of the steam.exe and passing the content of the shortcut as argument.
Edit:
If you want to get the argument value you can just read the File using
File.ReadAllLines(path);
And get the argument from the string, which will then look like this:
string[] lines = File.ReadAllLines(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "/Golf With Your Friends.url");
string argument = lines.ToList().FirstOrDefault(item => { return item.Contains("steam://"); }).Replace("URL=", "");
System.Diagnostics.Process.Start(#"C:\Program Files (x86)\Steam\steam.exe", argument);
I am writing a WPF / C# application, and would like to enable a user to select one (or multiple) files, or one (or multiple) folders, without having to select which option they use initially, but intentionally. In my opinion, the best way to acchieve this goal would be to have a standard FolderBrowserDialog, and as long as the user does not seelct a file, but browses to a path, clicking the open button should select that path.
Practically, this solution does not work, because OpenFileDialog does not allow empty selections, you can hit "open", but nothing will happen. There is one workaround descriped here which allows to enter a fake name like "Selected Folder." as filename. which can afterwards be filtered out, which is a workaround, but not a nice one:
http://www.codeproject.com/Articles/44914/Select-file-or-folder-from-the-same-dialog
This solution has two important weaknesses:
1.) you will have to filter for the fake name
2.) if you paste a filename manually, or select a file first, and then switch the selection to a folder instead, the fake name is not inserted automatically.
of course I am aware there is something like FolderBrowserDialog, which I omit using even if I only wanted to select folders and not files. The reason: this dialog has no possibility to paste paths from clipboard, and I find it annoying to navigate all the way, I rather copy paths from somewhere and paste them, which works perfectly fine in OpenFileDialog, but not in FolderBrowserDialog. Besides, FolderBrowserDialog does not allow to select files and folders.
I have googled a lot, but do not find satisfying solutions, although I am sure many people must obviously face this problem.
As mentioned, the most elegant way for me would be to make the OpenFileDialog simply allow empty Filename boxes when clicking Open - any way to acchieve this?
Thanks alot.
Letting a user select a directory OR a file using the same dialog is not practical nor intuitively possible.
However, if you want a solution for selecting a Folder, here it is :
If you don't want to create a custom dialog but still prefer a 100% WPF way and don't want to use separate DDLs, additional dependencies or outdated APIs, I came up with a very simple hack using WPF's Save As dialog for actually selecting a directory.
No using directive needed, you may simply copy-paste the code below !
It should still be very user-friendly and most people will never notice.
The idea comes from the fact that we can change the title of that dialog, hide files, and work around the resulting filename quite easily.
It is a big hack for sure, but maybe it will do the job just fine for your usage...
In this example I have a textbox object to contain the resulting path, but you may remove the related lines and use a return value if you wish...
// Create a "Save As" dialog for selecting a directory (HACK)
var dialog = new Microsoft.Win32.SaveFileDialog();
dialog.InitialDirectory = textbox.Text; // Use current value for initial dir
dialog.Title = "Select a Directory"; // instead of default "Save As"
dialog.Filter = "Directory|*.this.directory"; // Prevents displaying files
dialog.FileName = "select"; // Filename will then be "select.this.directory"
if (dialog.ShowDialog() == true) {
string path = dialog.FileName;
// Remove fake filename from resulting path
path = path.Replace("\\select.this.directory", "");
path = path.Replace(".this.directory", "");
// If user has changed the filename, create the new directory
if (!System.IO.Directory.Exists(path)) {
System.IO.Directory.CreateDirectory(path);
}
// Our final value is in path
textbox.Text = path;
}
The only issues with this hack are :
Acknowledge button still says "Save" instead of something like "Select directory", but in a case like mines I "Save" the directory selection so it still works...
Input field still says "File name" instead of "Directory name", but we can say that a directory is a type of file...
There is still a "Save as type" dropdown, but its value says "Directory (*.this.directory)", and the user cannot change it for something else, works for me...
Most people won't notice these, although I would definitely prefer using an official WPF way if microsoft would get their heads out of their asses, but until they do, that's my temporary fix.
I have a C# .NET 3.5 program that uses an OpenFileDialog to retrieve a file for importing. It has two filters (*Domain*.* and *.*) and the *Domain*.* is chosen as the FilterIndex. I'm seeing a case of a disappearing file from the filters when it is modified within the OpenFileDialog.
Here are the steps the user will take to reproduce the problem:
In the program, open the OpenFileDialog. Ensure that the filter is set to *Domain*.*.
Find a file in the directory that matches the filter and change the file name so that it no longer matches the filter. Note: the file will disappear.
Change the filter to show all files *.*. Note: the file is not listed even though you're supposedly showing *.*.
At this point, you can open the folder in Windows Explorer and see that the file with the changed name exists. However, in the OpenFileDialog, it is no longer visible regardless of the filter that is used. The only way to see that file again is to close the OpenFileDialog and then reopen it.
Here is my code that creates the OpenFileDialog.
using(OpenFileDialog domainFileDialog = new OpenFileDialog())
{
domainFileDialog.CheckFileExists = true;
domainFileDialog.CheckPathExists = true;
domainFileDialog.Filter = "Domain Files (*Domain*.*)|*Domain*.*|All files (*.*)|*.*";
domainFileDialog.FilterIndex = 1;
domainFileDialog.Multiselect = false;
domainFileDialog.RestoreDirectory = true;
domainFileDialog.ShowReadOnly = true;
domainFileDialog.SupportMultiDottedExtensions = true;
domainFileDialog.Title = "Choose the Domain File to Import...";
domainFileDialog.ValidateNames = true;
\\ Perform Import functionality...
}
It seems as though when you open the OpenFileDialog and modify an existing file that matches the more restrictive filter so that it no longer matches the filter, the file disappears from visibility even when all files *.* are displayed.
Since I have no idea why this is happening, (and I'm open for suggestions as to how to fix it) is there any way to restrict a user from creating or modifying any of the files in the OpenFileDialog in hopes of preventing this problem?
First, I'd suggest you consider whether it is worth caring that the user might do this. If the FOD does this, every windows application has this feature, but you don't hear users complaining about it. So is it really an issue that needs fixing?
It wouldn't surprise me if the problem's related to the filter being for more than just a file extension - it's rather unconventional to do this. Maybe try it with an extension only and see if this is the cause.
Have you tried pressing F5 to refresh its cached data? I don't know if this would work in a FOD as it does in an explorer window, but it might be interesting to give it a try.
Having said all that, it would be trivial to filter keypresses (F2) and mouse clicks (right button) )(with some forms you could derive off it, or you could drop in a MessageFilter) to make it impossible for a user to access any UI that allows them to rename a file in the FOD. It would piss me off, as a user, if you broke the FOD in this waym though - much more than a renamed file "disappearing" under rare circumstances, anyway.
http://msdn.microsoft.com/en-us/library/microsoft.win32.openfiledialog.aspx
The OpenFileDialog doesn't itself support any such functionality. I think your best bet at this point is writing your own open-file dialog window. You would gain a great deal of control that way and it wouldn't take too long.
In FileOpen or FileSave dialogbox when I call them, they automatically go to the last opened path. This happens even if I close my application and open it. But how to get that path/file name to a textbox or variable?
it seems a bit weired but under Windows 7 it works with the folling:
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.InitialDirectory = System.Environment.GetFolderPath(Environment.SpecialFolder.MyComputer);
Try it and tell me if you need further help.
Presumably the information is stored somewhere in the depths of the registry (it's done by the unmanaged control to which OpenFileDialog is only a wrapper). The easiest would probably be to persist the path the last time the dialog was closed in your application somewhere where you can access it.
As mentioned here: What does the FileDialog.RestoreDirectory Property actually do?
They use the path from Environment.CurrentDirectory.
I am having a similar problem to Vicky, which goes as follows. I am developing in Visual Basic 2008 Express Edition under Vista Business SP2.
I have an application with an OpenFileDialog and a SaveFileDialog. When I call the OpenFileDialog on first running the application, it defaults to the directory from which the Application last opened/saved a file. However, this directory IS NOT "Environment.CurrentDirectory" which is set to "C:\Users\Brian\Documents\Visual Studio 2008\Projects\IFPM Analysis\IFPM Analysis\bin\Debug" and is not changed by either the OpenFileDialog or SaveFileDialog.
Later on in the Application, I call the SaveFileDialog, with the initial directory property (.InitialDirectory) set in the code to a default directory. When I subsequently call the OpenFileDialog, it defaults to the directory used by the SaveFileDialog. All the time, the value of "Environment.CurrentDirectory" is unchanged.
So, my question is, where is the directory that is being used by the OpenFileDialog and SaveFileDialog being stored? I assume it is something to do with the underlying FileDialog class, and I know persists even after the Application has been closed and restarted.
Ideally I want to be able to store the directory selected by the user from the OpenFileDialog and reset it after I have used the SaveFileDialog. While I can use the InitialDirectory property of the OpenFileDialog within the Application, this doesn't help me when I close the Application and restart it. Sadly, the typical user actions are:
start Application
open file with OpenFileDialog
save file with SaveFileDialog
leave Application
This means that when the user comes back to the Application, the default directory is the "wrong" one. I know that I can save the last used directory from the OpenFileDialog in my own configuration file so that it will persist outside of the Application, but that seems a little silly when Windows provides me with the same functionality, if only I knew where to look!
Any help gratefully received!
Thanks,
Brian.
The recent opened files list is stored in 2 places:
Recent Folder: The recent folder is usually located under C:\Documents and Settings[Your Profile]\Recent (The path is different under Windows Vista), and it contains shortcuts to the recently opened files.
Registry: Each time that a file is selected in save/open dialog-box, the filename is added to the files list under HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\OpenSaveMRU
This method can help you to get the list:
public static string GetLastOpenSaveFile(string extention)
{
RegistryKey regKey = Registry.CurrentUser;
string lastUsedFolder = string.Empty;
regKey = regKey.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\OpenSaveMRU");
if (string.IsNullOrEmpty(extention))
extention = "html";
RegistryKey myKey = regKey.OpenSubKey(extention);
if (myKey == null && regKey.GetSubKeyNames().Length > 0)
myKey = regKey.OpenSubKey(regKey.GetSubKeyNames()[regKey.GetSubKeyNames().Length - 2]);
if (myKey != null)
{
string[] names = myKey.GetValueNames();
if (names != null && names.Length > 0)
{
lastUsedFolder = (string)myKey.GetValue(names[names.Length - 2]);
}
}
return lastUsedFolder;
}
Success!
Iordan
I have the following code:
p.StartInfo.FileName = "notepad.exe";
p.StartInfo.Arguments = "somefile.txt";
p.Start();
When "somefile.txt" is opened in notepad, its text will be modified. I want to save the new text in a temp file with the C# app automatically.
Is it possible to access this text and save it with C#?
It is sorta possible, you could use P/Invoke to call the SendMessage() method and generate the WM_COMMAND messages for the Edit + Select-All and Edit + Copy menu commands. That puts all text on the clip board, readily accessible to your program. You'll need to use the Spy++ utility to find out what the command identifiers are. Looks like 25 and 769 when I look at it on Win7.
There's a more direct way to discover the commands. In Visual Studio choose File + Open + File and select c:\windows\notepad.exe. Open the Menu node and double-click the "1" resource. That opens the menu editor, select Edit + Select-All and look at the Properties window for the ID.
Of course, this technique is specific to Notepad. The command IDs will be different for another program. As will be the code you need to find the proper window handle. Using Process.MainWindowHandle is treacherous at best, you don't know what specific instance of the process you need to select.
It would be very difficult to look at the text in-memory in the notepad process. It could be done, but it depends on the internal memory layout of notepad and your process may need administrative privileges depending on how the memory pages are protected.
Why not use a FileSystemWatcher to detect when the file has been modified, and then read the file in directly using c#?
http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.aspx
Copy the somefile.txt before you start notepad to a temporary file/folder. Start notepad using this temporary file. And wait for Notepad to exit.
Then you can examine if the file is changed (checksum?).
Probably delete the file after the processing, or your system will be full with temp files.
You can bring notepad window to front and send CTRL+S. You will need to use pinvoke for that.
I really don't understand why you would want to use notepad?
If you need a user to edit a text file, and then your app take some action on the text itself, why not just give the user the ability to edit the text right in your app?
What does notepad give you that the TextBox control doesn't have?
Try something like this
using System.Windows.Forms;
using System.IO;
TextBox myEditor = new TextBox();
myEditor.Multiline = true;
myEditor.ScrollBars = ScrollBars.Vertical;
myEditor.AcceptsReturn = true;
myEditor.AcceptsTab = true;
myEditor.WordWrap = true;
// Get the text from the file.
StreamReader sr = new StreamReader("SomeFile.txt"))
myEditor.Text = sr.ReadToEnd();