Skip to content

Instantly share code, notes, and snippets.

@chrcar01
Created April 3, 2022 00:36
Show Gist options
  • Save chrcar01/e2d4a71ed67593ab727f725f8bd27042 to your computer and use it in GitHub Desktop.
Save chrcar01/e2d4a71ed67593ab727f725f8bd27042 to your computer and use it in GitHub Desktop.
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