Created
April 3, 2022 00:36
-
-
Save chrcar01/e2d4a71ed67593ab727f725f8bd27042 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Runtime.InteropServices; | |
using System.Text; | |
// https://www.pinvoke.net/default.aspx/shlwapi.assocquerystring | |
// https://docs.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-assocquerystringa | |
// https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-findexecutablea | |
// https://stackoverflow.com/a/9540278 | |
internal static class DefaultAppHelper | |
{ | |
[DllImport("shell32.dll", EntryPoint = "FindExecutable")] | |
private static extern long FindExecutable(string lpFile, string lpDirectory, StringBuilder lpResult); | |
[DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)] | |
private static extern uint AssocQueryString( | |
AssocF flags, | |
AssocStr str, | |
string pszAssoc, | |
string pszExtra, | |
[Out] StringBuilder pszOut, | |
ref uint pcchOut); | |
public static bool TryFindExecutable(string path, out string executablePath) | |
{ | |
var executable = new StringBuilder(1024); | |
var result = FindExecutable(path, string.Empty, executable); | |
if (result > 32) | |
{ | |
executablePath = executable.ToString(); | |
return true; | |
} | |
var extension = new FileInfo(path).Extension; | |
executablePath = AssocQueryString(AssocStr.Executable, extension); | |
return !string.IsNullOrEmpty(executablePath); | |
} | |
private static string AssocQueryString(AssocStr association, string extension) | |
{ | |
uint length = 0; | |
uint ret = AssocQueryString(AssocF.None, association, extension, null!, null!, ref length); | |
if (ret != 1) // expected S_FALSE | |
{ | |
return string.Empty; | |
} | |
var sb = new StringBuilder((int)length); // (length-1) will probably work too as null termination is added | |
ret = AssocQueryString(AssocF.None, association, extension, null!, sb, ref length); | |
if (ret != 0) // expected S_OK | |
{ | |
return string.Empty; | |
} | |
return sb.ToString(); | |
} | |
[Flags] | |
private enum AssocF | |
{ | |
None = 0, | |
Init_NoRemapCLSID = 0x1, | |
Init_ByExeName = 0x2, | |
Open_ByExeName = 0x2, | |
Init_DefaultToStar = 0x4, | |
Init_DefaultToFolder = 0x8, | |
NoUserSettings = 0x10, | |
NoTruncate = 0x20, | |
Verify = 0x40, | |
RemapRunDll = 0x80, | |
NoFixUps = 0x100, | |
IgnoreBaseClass = 0x200, | |
Init_IgnoreUnknown = 0x400, | |
Init_Fixed_ProgId = 0x800, | |
Is_Protocol = 0x1000, | |
Init_For_File = 0x2000 | |
} | |
private enum AssocStr | |
{ | |
Command = 1, | |
Executable, | |
FriendlyDocName, | |
FriendlyAppName, | |
NoOpen, | |
ShellNewValue, | |
DDECommand, | |
DDEIfExec, | |
DDEApplication, | |
DDETopic, | |
InfoTip, | |
QuickTip, | |
TileInfo, | |
ContentType, | |
DefaultIcon, | |
ShellExtension, | |
DropTarget, | |
DelegateExecute, | |
Supported_Uri_Protocols, | |
ProgID, | |
AppID, | |
AppPublisher, | |
AppIconReference, | |
Max | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment