Skip to content

Commit e38d428

Browse files
committed
Add ComClassFactory
Add ComClassFactory based on WinForms implementation.
1 parent ca01a5b commit e38d428

File tree

4 files changed

+147
-0
lines changed

4 files changed

+147
-0
lines changed

src/thirtytwo/NativeMethods.txt

+2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ DISPID_Parent
6060
DISPID_PROPERTYPUT
6161
DISPID_STARTENUM
6262
DISPID_UNKNOWN
63+
DLLVERSIONINFO
6364
DPI_AWARENESS_CONTEXT_*
6465
DrawFocusRect
6566
DrawIconEx
@@ -263,6 +264,7 @@ LineTo
263264
LoadCursor
264265
LoadIcon
265266
LoadLibrary
267+
LoadLibraryEx
266268
LoadRegTypeLib
267269
LOCALE_NAME_MAX_LENGTH
268270
LocalFree

src/thirtytwo/Win32/Foundation/HMODULE.cs

+58
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
using Windows.Support;
5+
using Windows.Win32.System.LibraryLoader;
56

67
namespace Windows.Win32.Foundation;
78

89
public unsafe partial struct HMODULE : IHandle<HMODULE>
910
{
11+
private const string DllGetVersionMethodName = "DllGetVersion";
12+
1013
HMODULE IHandle<HMODULE>.Handle => this;
1114
object? IHandle<HMODULE>.Wrapper => null;
1215

@@ -68,4 +71,59 @@ public static HMODULE FromName(string name, bool incrementRefCount = false)
6871
return hmodule;
6972
}
7073
}
74+
75+
/// <summary>
76+
/// Loads the module from the specified file path. Throws on failure.
77+
/// </summary>
78+
public static HMODULE LoadModule(string filePath, LOAD_LIBRARY_FLAGS flags = default)
79+
{
80+
HMODULE module = Interop.LoadLibraryEx(filePath, flags);
81+
if (module.IsNull)
82+
{
83+
Error.ThrowLastError();
84+
}
85+
86+
return module;
87+
}
88+
89+
/// <summary>
90+
/// Gets the dll version of the module. Throws if the module doesn't expose `DllGetVersion`.
91+
/// </summary>
92+
public Version GetDllVersion()
93+
{
94+
FARPROC proc = Interop.GetProcAddress(this, DllGetVersionMethodName);
95+
96+
if (proc.IsNull)
97+
{
98+
Error.ThrowLastError();
99+
}
100+
101+
DLLVERSIONINFO versionInfo = new()
102+
{
103+
cbSize = (uint)sizeof(DLLVERSIONINFO)
104+
};
105+
106+
// HRESULT Dllgetversionproc(DLLVERSIONINFO* version)
107+
((delegate* unmanaged<DLLVERSIONINFO*, HRESULT>)proc.Value)(&versionInfo).ThrowOnFailure();
108+
109+
return new Version(
110+
(int)versionInfo.dwMajorVersion,
111+
(int)versionInfo.dwMinorVersion,
112+
(int)versionInfo.dwBuildNumber,
113+
(int)versionInfo.dwPlatformID);
114+
}
115+
116+
/// <summary>
117+
/// Gets the address of the specified procedure. Throws if the procedure can't be found.
118+
/// </summary>
119+
public FARPROC GetProcAddress(string procName)
120+
{
121+
FARPROC proc = Interop.GetProcAddress(this, procName);
122+
if (proc.IsNull)
123+
{
124+
Error.ThrowLastError();
125+
}
126+
127+
return proc;
128+
}
71129
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright (c) Jeremy W. Kuhne. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
// Original license (from https://github.com/dotnet/winforms):
5+
//
6+
// Licensed to the .NET Foundation under one or more agreements.
7+
// The .NET Foundation licenses this file to you under the MIT license.
8+
9+
using Windows.Support;
10+
using RuntimeMarshal = System.Runtime.InteropServices.Marshal;
11+
12+
namespace Windows.Win32.System.Com;
13+
14+
/// <summary>
15+
/// Wraps an <see cref="IClassFactory"/> from a dynamically loaded assembly.
16+
/// </summary>
17+
internal unsafe class ComClassFactory : IDisposable
18+
{
19+
private readonly HMODULE _module;
20+
private readonly bool _unloadModule;
21+
private readonly IClassFactory* _classFactory;
22+
23+
public Guid ClassId { get; }
24+
25+
private const string ExportMethodName = "DllGetClassObject";
26+
27+
public ComClassFactory(
28+
string filePath,
29+
Guid classId) : this(HMODULE.LoadModule(filePath), classId)
30+
{
31+
_unloadModule = true;
32+
}
33+
34+
public ComClassFactory(
35+
HMODULE module,
36+
Guid classId)
37+
{
38+
_module = module;
39+
ClassId = classId;
40+
41+
// Dynamically get the class factory method.
42+
43+
// HRESULT DllGetClassObject(
44+
// [in] REFCLSID rclsid,
45+
// [in] REFIID riid,
46+
// [out] LPVOID* ppv
47+
// );
48+
49+
FARPROC proc = Interop.GetProcAddress(module, ExportMethodName);
50+
51+
if (proc.IsNull)
52+
{
53+
Error.ThrowLastError();
54+
}
55+
56+
IClassFactory* classFactory;
57+
((delegate* unmanaged<Guid*, Guid*, void**, HRESULT>)proc.Value)(
58+
&classId, IID.Get<IClassFactory>(),
59+
(void**)&classFactory).ThrowOnFailure();
60+
_classFactory = classFactory;
61+
}
62+
63+
internal HRESULT CreateInstance(out IUnknown* unknown)
64+
{
65+
unknown = default;
66+
fixed (IUnknown** u = &unknown)
67+
{
68+
return _classFactory->CreateInstance(null, IID.Get<IUnknown>(), (void**)u);
69+
}
70+
}
71+
72+
internal HRESULT CreateInstance(out object? unknown)
73+
{
74+
HRESULT result = CreateInstance(out IUnknown* punk);
75+
unknown = punk is null ? null : RuntimeMarshal.GetObjectForIUnknown((nint)punk);
76+
return result;
77+
}
78+
79+
public void Dispose()
80+
{
81+
_classFactory->Release();
82+
if (_unloadModule && !_module.IsNull)
83+
{
84+
Interop.FreeLibrary(_module);
85+
}
86+
}
87+
}
File renamed without changes.

0 commit comments

Comments
 (0)