It is sometimes desirable to have your application open the default application for a file. For example, to open a PDF file you might use:
System.Diagnostics.Process.Start("Filename.pdf");
To open an image, you'd just use the same code with a different filename:
System.Diagnostics.Process.Start("Filename.gif");
Some extensions (.gif for example) just about always have a default handler, even in a base Windows installation. However, some extensions (.pdf for example) often don't have an application installed to handle them.
In these cases, it'd be desirable to determine if an application is associated with the extension of the file you wish to open before you make the call to Process.Start(fileName).
I'm wondering how you might best implement something like this:
static bool ApplicationAssociated(string extension)
{
var extensionHasAssociatedApplication = false;
var condition = // Determine if there is an application installed that is associated with the provided file extension.;
if (condition)
{
extensionHasAssociatedApplication = true;
}
return extensionHasAssociatedApplication;
}
I would recommend following the advice in David's answer BUT since you need to detect an association:
To check whether a file has an association you can use the native function FindExecutable which is basically what Windows Explorer uses internally... it gives a nice error code (SE_ERR_NOASSOC) if there is no association. Upon success it gives a path to the respective executable.
Thee DllImport for it is
[DllImport("shell32.dll")]
static extern int FindExecutable(string lpFile, string lpDirectory, [Out] StringBuilder lpResult);
Another option would be to walk the registry for example (not recommended since complex due to several aspets like WoW64 etc.):
The real association is stored in the key that HKEY_CLASSES_ROOT\.pdf points to - in my case AcroExch.Document, so we checkoutHKEY_CLASSES_ROOT\AcroExch.Document. There you can see (and change) what command is going to be used to launch that type of file:
HKEY_CLASSES_ROOT\AcroExch.Document\shell\open\command
#Yahia gets the nod. I'm posting my quick solution for posterity so you can see what I went with. There are lots of possible improvements to this code, but this will give you the idea:
public static bool HasExecutable(string path)
{
var executable = FindExecutable(path);
return !string.IsNullOrEmpty(executable);
}
private static string FindExecutable(string path)
{
var executable = new StringBuilder(1024);
FindExecutable(path, string.Empty, executable);
return executable.ToString();
}
[DllImport("shell32.dll", EntryPoint = "FindExecutable")]
private static extern long FindExecutable(string lpFile, string lpDirectory, StringBuilder lpResult);
In a situation like this the best approach is to try to open the document and detect failure. Trying to predict whether or not a file association is in place just leads to you reimplementing the shell execute APIs. It's very hard to get that exactly right and rather needless since they already exist!
You will have too look at the registry to get that information.
You can follow from:
HKEY_CLASSES_ROOT\.extension
and it usually leads to something like HKEY_CLASSES_ROOT\extfile\Shell\Open\Command
and you will come to the command to open the file type.
Depending on what you are doing, it may be ideal to just ask for forgiveness ( that is, just open the file and see)
All of that information lives in the registry.. you could navigate to HKEY_CLASSES_ROOT, find the extension and go from there to find the default handler. But depending on the type of file and the associated handler(s) you'll need to wade into CLSIDs and whatnot... you're probably better off catching an exception instead.
This information is in the registry. For example:
# Mount the HKCR drive in powershell
ps c:\> new-psdrive hkcr registry hkey_classes_root
ps c:\> cd hkcr:\.cs
# get default key for .cs
PS hkcr:\.cs> gp . ""
(default) : VisualStudio.cs.10.0
...
# dereference the "open" verb
PS hkcr:\.cs> dir ..\VisualStudio.cs.10.0\shell\open
Hive: hkey_classes_root\VisualStudio.cs.10.0\shell\open
Name Property
---- --------
Command (default) : "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe" /dde
ddeexec (default) : Open("%1")
Related
I'm currently creating a WPF application, and like to add as a small side feature the ability to clear the windows file explorer history.
If one were to manually do this operation, it is possible via the file menu within a file explorer window,
as shown here.
My goal is pretty much to programmatically execute the same action as this button does, but I've been unable to find what executable or user32.dll method is behind this operation (if it exists), and been also unsuccessful on finding the full logic behind it (namely, finding what folder and files it targets), to replicate it.
Can you help me?
As the comment by dxiv suggested, you can achieve this via the following:
enum ShellAddToRecentDocsFlags
{
Pidl = 0x001,
Path = 0x002,
PathW = 0x003
}
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
private static extern void SHAddToRecentDocs(ShellAddToRecentDocsFlags flag, string path);
// How To Clear Everything
SHAddToRecentDocs(ShellAddToRecentDocsFlags.Pidl, null);
I'm making a C# program and I need to find installation paths of some software that is installed on a computer.
What I have to work with is, I have the Program's name (e.g. Google Chrome), i have the process name (e.g. Chrome.exe).
What I need now is the path to Chrome.exe. How can i use C# to find the path if i was to pass either the program name or process name as a parameter for the search?
Actually I want to make a custom action which will find chrome.exe and invoke a link.
After that I will use the path for search chrome.exe and I want to default open a website via chrome. What should I do..?
Another option to consider is just launching the link using Process.Start() and letting the operating system use the default browser to open the link. That would likely be more what the user would expect.
In the WiX toolset, you can get that behavior for free using ShellExecute standard custom action from the WixUtilExtension.
You could try something like this
public string GetProcessPath(string name)
{
Process[] processes = Process.GetProcessesByName(name);
if (processes.Length > 0)
{
return processes[0].MainModule.FileName;
}
else
{
return string.Empty;
}
}
or you could use Linq
or you could do what you do but use linq
Process element = ( from p in Process.GetProcesses()
where p.ProcessName == "Chrome.exe"
select p ).FirstOrDefault( );
However there can be multiple process with same name .So you have to further modify the code according to your requirement.
hope this helps
I'm having issues with the SelectedPath property of the FolderBrowserDialog when the folder I select is on a remote server and is a symbolic link (or any kind of reparse point).
If i select a normal folder, then I get the full path returned, for example "\SERVER\folder\subfolder\thing_I_clicked_on".
However, if the folder is a reparse point, i get just "\SERVER\thing_I_clicked_on" (so it's missing the full path)
var dialog = new FolderBrowserDialog();
dialog.ShowDialog();
MessageBox.Show(dialog.SelectedPath);
Anyone come across this or have any suggestions? It doesn't appear to be permissions related, as if I know the full path i can quite happily browse to it, etc.
so, I've been investigating this quite a lot, and think i have an answer.
First, a bit of explanation about what I was seeing!
on server A, there is a share which contains a symbolic link to a share on server B:
\\serverA\Path\To\Folder
and the target of that is
\\serverB\Folder
What was actually happening was, the value returned from FolderBrowserDialog.SelectedPath was \\serverB\Folder, and I was mistakenly thinking it was missing parts of the path, because the strings serverA and serverB are very similar! Sorry for misleading everyone.
I created my own wrapper following this MSDN Example and noticed that the Shell32.dll function SHGetPathFromIDList is returning the Target of the reparse point, despite the fact that the documentation says
If the pidl parameter specifies a shortcut, the pszPath will contain
the path to the shortcut, not to the shortcut's target
I did notice that the path before that is the correct one though, so in my callback method when the status changed, I captured the untranslated selected path
private int FolderBrowserCallback(IntPtr hwnd, int msg, IntPtr lParam, IntPtr lpData)
{
switch (msg)
{
case BrowseForFolderMessages.BffmSelchanged:
if (haveValidPath && !String.IsNullOrEmpty(displayedPath))
{
if (IntPtr.Zero != _hwndEdit)
{
SelectedFullPath = displayedPath;
}
}
So the SelectedFullPath Property contains \\serverA\Path\To\Folder and SelectedPath property contains \\ServerB\Folder, which leaves me a lot to work with.
How do I programmatically access the target path of a windows symbolic link must be here. Are you sure that you need exactly full path, not path to reparse point? I think you can use this path instead.
"Notethat Windows does not support junctions to directories on remote shares." proof
Andy's answer looks like it will work except the details are missing, and I cannot figure out where to add his code snippet to the MSDN example code. I can get the "MSDN sample" code to work, just don't know where to slip in Andy's stuff.
So... for those of you like me who can't figure out what to do with the code above, (I really wish I knew what I was doing ;) here is a kind of lame workaround solution.
Use the filedialog class in lieu of the browseforfolder and set the;
DereferenceLinks Property = false
CheckFileExists = false
filename = a bogus default filename. (Let the user know that it doesn't matter what the file name is since they are just choosing a folder.)
Use either a OpenFileDialog, or SaveFileDialog.
Note that when this is done, if the user clicks on an old fashioned windows shortcut (*.lnk) it (the shortcut) will be returned as the filename, so you will have to account for that.
Hope this Helps ;)
(I'll work on getting some "reputation" so I can comment on Andy's post to get the details for his answer since it looks really nice.)
I want to get the path and version number of a ClickOnce application, provided the name of the ClickOnce application.
When I manually searched for it, I found it at the path as follows:
'C:\Users\krishnaim\AppData\Local\Apps\2.0\1HCG3KL0.K41\VO5BM4JR.RPO\head..tion_7446cb71d1187222_0005.0037_37dfcf0728461a82\HeadCount.exe'
But this keeps on changing, and it will become a hard-coded path. Is there another way to get a ClickOnce application (for example, HeadCount.exe which is already installed) path and version number using C#/.NET code?
It seems a little bizarre, but getting the current directory of the executing assembly is a bit tricky so my code below may be doing more than you think it should, but I assure you it is mitigating some issues where others may attempt to use Assembly.GetExecutingAssembly.Location property.
static public string AssemblyDirectory
{
get
{
//Don't use Assembly.GetExecutingAssembly().Location, instead use the CodeBase property
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return System.IO.Path.GetDirectoryName(path);
}
}
static public string AssemblyVersion
{
get
{
var asm = Assembly.GetExecutingAssembly();
//If you want the full four-part version number:
return asm.GetName().Version.ToString(4);
//You can reference asm.GetName().Version to get Major, Minor, MajorRevision, MinorRevision
//components individually and do with them as you please.
}
}
In order to do a ClickOnce application update you do not have to do so manually as long as you are using the standard deployment manifests (which I don't know how to ClickOnce unless you do use them).
The MSDN article Choosing a ClickOnce Update Strategy describes the different options for application updates.
I have a c# program which open *.postfix file.
If a user runs a (.lnk)shortcut which points to my type of file, my program will open the target.
So, how could my program know it is started by a (.lnk)shortcut (and get it's file path)?
In some circumstances,i need to replace the .lnk file.
Thanks!
Edited
First, thanks to guys who answered my question.
By following #Anders answer, i find out my problem lays here.
I made some changes to windows registry, so browser knows to throw customized protocol string to certain program.
some thing like this..
[InternetShortcut]
URL=myProtocol://abcdefg.....
That's maybe why i lost lpTitle. :(
I'm going to try this way:
Whenever my program invoked, of course fed with %1, program checks current opened explorer(Window), and try to get it's current path with IWebBrowserApp. With that path and desktop of course, scan and analyze *.lnk to determine which one to replace.
I think this will probably work, but not be sure. I will try.
continued
In native code you can call GetStartupInfo, if the STARTF_TITLEISLINKNAME bit is set in STARTUPINFO.dwFlags then the path to the .lnk is in STARTUPINFO.lpTitle. I don't know if there is a .NET way to get this info, you probably have to P/Invoke...
You don't. There's no way to do it. End of story.
So this has been brought to my attention due to a recent downvote. There's an accepted answer showing an idea that gets the path to the launching shortcut most of the time. However my answer is to the whole. OP wants the link to the shortcut so he can change it. That is what can't be done most of the time.
Most likely case is the shortcut file exists in the start menu but is unwritable. However other cases involve the shortcut coming from another launching application that didn't even read it from a disk but from a database (I've seen a lot of corporate level restricted application launch tools). I also have a program that launches programs from shortcuts not via IShellLink but by parsing the .lnk file (because it must not start COM for reasons) and launching the program contained. It doesn't pass STARTF_TITLEISLINKNAME because it's passing an actual title.
If you're using Visual Studio Setup Project to build an installer and do the file type association, you should follow these instructions http://www.dreamincode.net/forums/topic/58005-file-associations-in-visual-studio/
Open up your solution in Visual studio.
Add a Setup Project to your solution by file , add project,New project, Setup & Deployment projects,Setup project
Right-click on your setup project in the "Solution Explorer" window,Select view,then select file types.
you'll see the "file types" window displayed in Visual studio.At the top of the window will be "File types on target machine"
Right-click on "File types on target machine".the menu will pop up with Add "file type" Click on this.
you'll see "New document Type#1" added,and "&open"underneath it.
The "new document type#1" can be anything you want - change it to something descriptive.although the user never sees this,never use something common- be as unique as possible,Because you can overlay current file associations without even realizing it.For example,you might think"pngfile" might be a useful name- but using that will now send all"*.png" files to your application,instead of to an image viewer.A good practice maybe "YourCompantName.Filetype",where your company name is your name of your company's name, and "Filetype" is a descriptive text of your file.
In the "properties" window for your new type,you will need to change a few properties.:
Command:Change to the application that you want to run.If you click on the "..." and you will proberly want to locate and use the "primary Output..." File
Description: This is the description of the file type(if it doesn't describe it's self"
Extensions:This your list of extensions for you chosen Program.Separate each one with a ","
Icon:This will associate the icon with your file type,This shows up in the window explorer.
Now we move to that "&open ".This is an action that is available if your right-click on the file.The default action("&Open" is currently set as the default) is what happens when you double click on the file.Right click on your "New document type#1" to add actions,but for the moment,lets define our "&open" action
Click on "&Open".You will see in the properties window "Name","Arguments","Verbs". Verb is hidden from the user,but is the key that is stored in the registry.Leave it same as the name,But without the "&".The default for"Arguments" is "%1",Which means to pass the full path and filename to your application.You can add other stuff here as well,if you need to pass flags to your application to do special stuff.All this infomaton is getting passed to your application on the command line,so you'll need to be familiar with the "Environment.CommandLine" object.
If you need to set a different action as your default,just right click on the action and "set as default"
Basically, you'll pass the file path as an argument to your program. Then if it's a console application or Windows Forms , you should check the arguments in Program.Main
static void Main(string[] args)
{
//if file association done with Arguments %1 as per forum post above
//you file path should be in args[0]
string filePath = null;
if(args != null && args.Length > 0)
filePath = args[0];
}
For a WPF application you'll need to handle that in the StartUp event for your Application
void App_Startup(object sender, StartupEventArgs e)
{
string filePath = null;
if ((e.Args != null) && (e.Args.Length > 0))
{
filePath = e.Args[0];
}
}