Skip to content

Instantly share code, notes, and snippets.

@smourier
Created September 28, 2017 11:19
Show Gist options
  • Save smourier/0b85ab1336c0e3e3f15a75315cc6fe63 to your computer and use it in GitHub Desktop.
Save smourier/0b85ab1336c0e3e3f15a75315cc6fe63 to your computer and use it in GitHub Desktop.
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Access.Dao;
using Microsoft.Win32;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
const string progid = "DAO.DBEngine.120";
var engineClsid = GetClickToRunClsid(progid);
string acedaoPath = GetClickToRunServerPath(progid);
using (var server = new ComServer(acedaoPath))
{
// you can still use the Microsoft.Office.Interop.Access.Dao references
var engine = (DBEngine)server.CreateInstance(engineClsid, typeof(DBEngine).GUID);
foreach (Property prop in engine.Properties)
{
try
{
Console.WriteLine(prop.Name + " = " + prop.Value);
}
catch
{
Console.WriteLine(prop.Name + " ?");
}
}
}
}
public const string ClickToRunClasses = @"SOFTWARE\Microsoft\Office\ClickToRun\REGISTRY\MACHINE\Software\Classes";
// this needs to run as 64-bit if OS is 64, etc.
public static string GetClickToRunServerPath(string progid)
{
if (progid == null)
throw new ArgumentNullException(nameof(progid));
using (var key = Registry.LocalMachine.OpenSubKey(Path.Combine(ClickToRunClasses, "CLSID", GetClickToRunClsid(progid).ToString("B"), "InprocServer32"), false))
{
return key?.GetValue(null) as string;
}
}
// this needs to run as 64-bit if OS is 64, etc.
public static Guid GetClickToRunClsid(string progid)
{
if (progid == null)
throw new ArgumentNullException(nameof(progid));
using (var key = Registry.LocalMachine.OpenSubKey(Path.Combine(ClickToRunClasses, progid, "CLSID"), false))
{
var str = key?.GetValue(null) as string;
if (str == null || !Guid.TryParse(str, out Guid clsid))
return Guid.Empty;
return clsid;
}
}
}
public sealed class ComServer : IDisposable
{
private DllGetClassObject _getClassObject;
public ComServer(string path)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
Module = LoadLibrary(path);
if (Module == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
IntPtr gpa = GetProcAddress(Module, nameof(DllGetClassObject));
if (gpa == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
_getClassObject = Marshal.GetDelegateForFunctionPointer<DllGetClassObject>(gpa);
}
public IntPtr Module { get; private set; }
public object CreateInstance(Guid clsid, Guid riid)
{
if (Module == IntPtr.Zero)
throw new ObjectDisposedException(nameof(Module));
int hr = _getClassObject.Invoke(clsid, typeof(IClassFactory).GUID, out IClassFactory factory);
if (hr != 0)
throw new Win32Exception(hr);
return factory.CreateInstance(null, riid); // InvalidCastException here means the class doesn't support the requested interface
}
public void Dispose()
{
if (Module != IntPtr.Zero)
{
FreeLibrary(Module);
Module = IntPtr.Zero;
}
}
private delegate int DllGetClassObject([MarshalAs(UnmanagedType.LPStruct)] Guid rclsid, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IClassFactory ppv);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
[DllImport("kernel32.dll")]
private static extern bool FreeLibrary(IntPtr hModule);
[Guid("00000001-0000-0000-c000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IClassFactory
{
[return: MarshalAs(UnmanagedType.Interface)]
object CreateInstance([MarshalAs(UnmanagedType.Interface)] object pUnkOuter, [MarshalAs(UnmanagedType.LPStruct)] Guid riid);
void LockServer(bool fLock);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment