|
2 | 2 | // Licensed under the MIT License. See the LICENSE.
|
3 | 3 |
|
4 | 4 | using Microsoft.Extensions.Logging;
|
| 5 | +using Microsoft.Win32; |
5 | 6 | using Microsoft.Win32.SafeHandles;
|
6 | 7 | using System.Collections.Concurrent;
|
7 | 8 | using System.Drawing;
|
@@ -163,28 +164,15 @@ public static Task StartSTATask(Action action)
|
163 | 164 | return taskCompletionSource.Task;
|
164 | 165 | }
|
165 | 166 |
|
166 |
| - public static async Task<string?> GetFileAssociationAsync(string filename, bool checkDesktopFirst = false) |
| 167 | + public static async Task<string?> GetDefaultFileAssociationAsync(string filename, bool checkDesktopFirst = true) |
167 | 168 | {
|
168 |
| - // Find UWP apps |
169 |
| - async Task<string?> GetUwpAssoc() |
170 |
| - { |
171 |
| - var uwpApps = await Launcher.FindFileHandlersAsync(Path.GetExtension(filename)); |
172 |
| - return uwpApps.Any() ? uwpApps[0].PackageFamilyName : null; |
173 |
| - } |
| 169 | + // check if there exists an user choice first |
| 170 | + var userChoice = GetUserChoiceFileAssociation(filename); |
| 171 | + if (!string.IsNullOrEmpty(userChoice)) |
| 172 | + return userChoice; |
174 | 173 |
|
175 |
| - // Find desktop apps |
176 |
| - string? GetDesktopAssoc() |
177 |
| - { |
178 |
| - var lpResult = new StringBuilder(2048); |
179 |
| - var hResult = Shell32.FindExecutable(filename, null, lpResult); |
| 174 | + return await GetFileAssociationAsync(filename, checkDesktopFirst); |
180 | 175 |
|
181 |
| - return hResult.ToInt64() > 32 ? lpResult.ToString() : null; |
182 |
| - } |
183 |
| - |
184 |
| - if (checkDesktopFirst) |
185 |
| - return GetDesktopAssoc() ?? await GetUwpAssoc(); |
186 |
| - |
187 |
| - return await GetUwpAssoc() ?? GetDesktopAssoc(); |
188 | 176 | }
|
189 | 177 |
|
190 | 178 | public static string ExtractStringFromDLL(string file, int number)
|
@@ -1210,5 +1198,98 @@ public static bool GetWin32FindDataForPath(string targetPath, out Win32PInvoke.W
|
1210 | 1198 |
|
1211 | 1199 | return false;
|
1212 | 1200 | }
|
| 1201 | + |
| 1202 | + private static string? GetPackageFamilyNameFromAppRegistryName(string appRegistryName) |
| 1203 | + { |
| 1204 | + using var appXKey = Registry.ClassesRoot.OpenSubKey(appRegistryName + @"\Application"); |
| 1205 | + var appUserModelIdObj = appXKey?.GetValue("AppUserModelId"); |
| 1206 | + string? appUserModelId = appUserModelIdObj?.ToString(); |
| 1207 | + string? packageFamilyName = null; |
| 1208 | + if (!string.IsNullOrEmpty(appUserModelId)) |
| 1209 | + { |
| 1210 | + int bangIndex = appUserModelId.IndexOf('!'); |
| 1211 | + packageFamilyName = bangIndex > 0 ? appUserModelId[..bangIndex] : appUserModelId; |
| 1212 | + } |
| 1213 | + |
| 1214 | + return packageFamilyName; |
| 1215 | + } |
| 1216 | + |
| 1217 | + private static string? GetUserChoiceFileAssociation(string filename) |
| 1218 | + { |
| 1219 | + var fileExtension = Path.GetExtension(filename); |
| 1220 | + if (string.IsNullOrEmpty(filename)) |
| 1221 | + return null; |
| 1222 | + |
| 1223 | + try |
| 1224 | + { |
| 1225 | + // Get ProgId from UserChoice |
| 1226 | + using var userChoiceKey = Registry.CurrentUser.OpenSubKey($@"Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\{fileExtension}\UserChoice"); |
| 1227 | + var progIdObj = userChoiceKey?.GetValue("ProgId"); |
| 1228 | + string? progId = progIdObj?.ToString(); |
| 1229 | + |
| 1230 | + if (string.IsNullOrEmpty(progId)) |
| 1231 | + return null; |
| 1232 | + |
| 1233 | + // Get the package family name if it's an AppX app |
| 1234 | + if (progId.StartsWith("AppX", StringComparison.OrdinalIgnoreCase)) |
| 1235 | + { |
| 1236 | + string? packageFamilyName = GetPackageFamilyNameFromAppRegistryName(progId); |
| 1237 | + if (!string.IsNullOrEmpty(packageFamilyName)) |
| 1238 | + return packageFamilyName; |
| 1239 | + } |
| 1240 | + |
| 1241 | + // Find the open command for the ProgId |
| 1242 | + using var commandKey = Registry.ClassesRoot.OpenSubKey($@"{progId}\shell\open\command"); |
| 1243 | + var command = commandKey?.GetValue(null)?.ToString(); |
| 1244 | + |
| 1245 | + if (string.IsNullOrEmpty(command)) |
| 1246 | + return null; |
| 1247 | + |
| 1248 | + // Extract executable path from command string (e.g. "\"C:\\Program Files\\App\\app.exe\" \"%1\"") |
| 1249 | + var exePath = command.Trim(); |
| 1250 | + if (exePath.StartsWith("\"")) |
| 1251 | + { |
| 1252 | + int endQuote = exePath.IndexOf('\"', 1); |
| 1253 | + if (endQuote > 1) |
| 1254 | + exePath = exePath.Substring(1, endQuote - 1); |
| 1255 | + } |
| 1256 | + else |
| 1257 | + { |
| 1258 | + int firstSpace = exePath.IndexOf(' '); |
| 1259 | + if (firstSpace > 0) |
| 1260 | + exePath = exePath.Substring(0, firstSpace); |
| 1261 | + } |
| 1262 | + |
| 1263 | + return File.Exists(exePath) ? exePath : null; |
| 1264 | + } |
| 1265 | + catch |
| 1266 | + { |
| 1267 | + return null; |
| 1268 | + } |
| 1269 | + } |
| 1270 | + |
| 1271 | + private static async Task<string?> GetFileAssociationAsync(string filename, bool checkDesktopFirst = true) |
| 1272 | + { |
| 1273 | + // Find UWP apps |
| 1274 | + async Task<string?> GetUwpAssoc() |
| 1275 | + { |
| 1276 | + var uwpApps = await Launcher.FindFileHandlersAsync(Path.GetExtension(filename)); |
| 1277 | + return uwpApps.Any() ? uwpApps[0].PackageFamilyName : null; |
| 1278 | + } |
| 1279 | + |
| 1280 | + // Find desktop apps |
| 1281 | + string? GetDesktopAssoc() |
| 1282 | + { |
| 1283 | + var lpResult = new StringBuilder(2048); |
| 1284 | + var hResult = Shell32.FindExecutable(filename, null, lpResult); |
| 1285 | + |
| 1286 | + return hResult.ToInt64() > 32 ? lpResult.ToString() : null; |
| 1287 | + } |
| 1288 | + |
| 1289 | + if (checkDesktopFirst) |
| 1290 | + return GetDesktopAssoc() ?? await GetUwpAssoc(); |
| 1291 | + |
| 1292 | + return await GetUwpAssoc() ?? GetDesktopAssoc(); |
| 1293 | + } |
1213 | 1294 | }
|
1214 | 1295 | }
|
0 commit comments