Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

518 lookup system def before user def #531

Merged
merged 1 commit into from
Jun 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
93 changes: 22 additions & 71 deletions OneMore/Helpers/Office/Word.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@

namespace River.OneMoreAddIn.Helpers.Office
{
using Microsoft.Win32;
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml.Linq;
using MSWord = Microsoft.Office.Interop.Word;

Expand Down Expand Up @@ -245,33 +243,29 @@ public void ResolveAttachmentRefs(string docPath, XElement root, bool embedded)
{
var ext = Path.GetExtension(target);

var application = GetApplication(ext);
if (!string.IsNullOrEmpty(application))
var association = RegistryHelper.GetAssociation(ext);
if (association != null)
{
object progID = GetProgID(ext);
if (!string.IsNullOrEmpty(progID as string))
{
selection.Text = String.Empty;

object filename = target;
object linkToFile = !embedded;
object displayAsIcon = true;
object iconIndex = 0;
object iconFilename = application;
object iconLabel = Path.GetFileName(target);

selection.InlineShapes.AddOLEObject(
progID,
ref filename,
ref linkToFile,
ref displayAsIcon,
ref iconFilename,
ref iconIndex,
ref iconLabel
);

return;
}
selection.Text = String.Empty;

object filename = target;
object linkToFile = !embedded;
object displayAsIcon = true;
object iconIndex = association.IconIndex;
object iconFilename = association.IconFilename;
object iconLabel = Path.GetFileName(target);

selection.InlineShapes.AddOLEObject(
association.ProgID,
ref filename,
ref linkToFile,
ref displayAsIcon,
ref iconFilename,
ref iconIndex,
ref iconLabel
);

return;
}

// get a well-formed URL but also decode it so avoid encoding problems
Expand All @@ -283,48 +277,5 @@ ref iconLabel
object uri = System.Web.HttpUtility.UrlDecode(new Uri(target).AbsoluteUri);
word.Selection.Hyperlinks.Add(range, ref uri);
}


private string GetApplication(string ext)
{
string application = null;

// fetch length of output buffer
uint length = 0;
uint ret = Native.AssocQueryString(
Native.AssocF.None, Native.AssocStr.Executable, ext, null, null, ref length);

if (ret == 1) // expect S_FALSE
{
// fill buffer with executable path
var buffer = new StringBuilder((int)length); // long enough for zero-term
ret = Native.AssocQueryString(
Native.AssocF.None, Native.AssocStr.Executable, ext, null, buffer, ref length);

if (ret == 0) // expect S_OK
{
application = buffer.ToString();
}
}

return application;
}


private string GetProgID(string ext)
{
string progID = null;

using (var key = Registry.CurrentUser.OpenSubKey(
$@"Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\{ext}\UserChoice"))
{
if (key != null)
{
progID = key.GetValue("ProgID") as string;
}
}

return progID;
}
}
}
197 changes: 197 additions & 0 deletions OneMore/Helpers/RegistryHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
//************************************************************************************************
// Copyright © 2022 Steven M Cohn. All rights reserved.
//************************************************************************************************

namespace River.OneMoreAddIn.Helpers
{
using Microsoft.Win32;
using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;


internal class FileAssociation
{
public string ProgID { get; set; }
public string Application { get; set; }
public string DefaultExt { get; set; }
public string IconFilename { get; set; }
public int IconIndex { get; set; }
}


internal static class RegistryHelper
{
/// <summary>
/// Load the registered application details for a given file extension.
/// </summary>
/// <param name="ext">A file extension string similar to ".txt"</param>
/// <returns>A FileAssociation instance or null if not found</returns>
public static FileAssociation GetAssociation(string ext)
{
var application = GetApplication(ext);
if (application == null)
{
return null;
}

var association = LookupAssociation(ext, application);
if (association != null)
{
return association;
}

var progID = GetUserChoiceProgID(ext);
if (progID != null)
{
association = new FileAssociation
{
Application = application,
ProgID = progID,
DefaultExt = ext,
IconFilename = application,
IconIndex = 0
};
}

return association;
}


private static string GetApplication(string ext)
{
string application = null;

// fetch length of output buffer
uint length = 0;
uint ret = Native.AssocQueryString(
Native.AssocF.None, Native.AssocStr.Executable, ext, null, null, ref length);

if (ret == 1) // expect S_FALSE
{
// fill buffer with executable path
var buffer = new StringBuilder((int)length); // long enough for zero-term
ret = Native.AssocQueryString(
Native.AssocF.None, Native.AssocStr.Executable, ext, null, buffer, ref length);

if (ret == 0) // expect S_OK
{
application = buffer.ToString();
}
}

return application;
}


private static FileAssociation LookupAssociation(string ext, string application)
{
using (var key = Registry.ClassesRoot.OpenSubKey("CLSID"))
{
foreach (var classNames in key.GetSubKeyNames())
{
using (var classKey = key.OpenSubKey(classNames))
{
var association = GetDetails(classKey, ext, application);
if (association != null)
{
return association;
}
}
}
}

return null;
}


private static FileAssociation GetDetails(RegistryKey key, string ext, string application)
{
var names = key.GetSubKeyNames();
if (names.Contains("VersionIndependentProgID") &&
names.Contains("DefaultExtension") &&
names.Contains("DefaultIcon") &&
(names.Contains("InprocServer32") || names.Contains("LocalServer32"))
)
{
var association = new FileAssociation
{
ProgID = (string)key.GetValue(string.Empty)
};

if (!string.IsNullOrEmpty(association.ProgID))
{
association.DefaultExt = GetDefaultValue(key, "DefaultExtension");

if (string.IsNullOrEmpty(association.DefaultExt) ||
!Regex.IsMatch(association.DefaultExt, $@"\{ext}\b?"))
{
return null;
}

association.Application = GetDefaultValue(key, "InprocServer32");
if (string.IsNullOrEmpty(association.Application))
{
association.Application = GetDefaultValue(key, "LocalServer32");
}

if (!string.IsNullOrEmpty(association.Application) &&
association.Application.Equals(application, StringComparison.InvariantCultureIgnoreCase))
{
var defaultIcon = GetDefaultValue(key, "DefaultIcon");
var matches = Regex.Matches(defaultIcon, @"([^,]+),(\d+)");
if (matches.Count > 0)
{
association.IconFilename = matches[0].Groups[1].Value;
if (int.TryParse(matches[0].Groups[2].Value, out var iconIndex))
{
association.IconIndex = iconIndex;
}
}
else
{
association.IconFilename = association.Application;
association.IconIndex = 0;
}

return association;
}
}
}

return null;
}


private static string GetDefaultValue(RegistryKey key, string name)
{
using (var subkey = key.OpenSubKey(name))
{
if (subkey != null)
{
return (string)subkey.GetValue(string.Empty);
}
}

return null;
}


private static string GetUserChoiceProgID(string ext)
{
string progID = null;
using (var key = Registry.CurrentUser.OpenSubKey(
$@"Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\{ext}\UserChoice"))
{
if (key != null)
{
progID = (string)key.GetValue("ProgID");
}
}

return progID;
}

}
}
1 change: 1 addition & 0 deletions OneMore/OneMore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@
<Compile Include="Helpers\Office\Models\OutlookTaskFolders.cs" />
<Compile Include="Helpers\Office\Models\OutlookTasks.cs" />
<Compile Include="Helpers\Office\Outlook.cs" />
<Compile Include="Helpers\RegistryHelper.cs" />
<Compile Include="Helpers\SingleThreaded.cs" />
<Compile Include="Helpers\SynchronizationContextAwaiter.cs" />
<Compile Include="Commands\Tools\Updater\IUpdateReport.cs" />
Expand Down