Skip to content

Commit d6f2685

Browse files
committed
Separated NET47 edge case logic
1 parent fc42c06 commit d6f2685

File tree

3 files changed

+153
-35
lines changed

3 files changed

+153
-35
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright 2024 Yubico AB
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License").
4+
// You may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#if NET47
16+
using System;
17+
using System.IO;
18+
19+
namespace Yubico.PlatformInterop
20+
{
21+
/// <summary>
22+
/// .NET Framework 4.7 specific implementation for native library management.
23+
/// </summary>
24+
internal static partial class Libraries
25+
{
26+
/// <summary>
27+
/// Encapsulates the .NET Framework 4.7 specific implementation details for native library management.
28+
/// This nested class handles the dynamic loading of architecture-specific (x86/x64) native libraries.
29+
/// </summary>
30+
private static class Net47Implementation
31+
{
32+
// Handle to the loaded native library
33+
private static UnmanagedDynamicLibrary? _nativeShims;
34+
35+
/// <summary>
36+
/// Gets the full path to the architecture-specific native library.
37+
/// </summary>
38+
/// <remarks>
39+
/// The path is constructed based on:
40+
/// - The application's base directory
41+
/// - The current process architecture (x86/x64)
42+
/// - The native library filename
43+
/// </remarks>
44+
private static string NativeShimsPath =>
45+
Path.Combine(
46+
AppDomain.CurrentDomain.BaseDirectory,
47+
Environment.Is64BitProcess
48+
? "x64"
49+
: "x86",
50+
NativeShims);
51+
52+
/// <summary>
53+
/// Initializes the native library for the current architecture.
54+
/// </summary>
55+
/// <remarks>
56+
/// This method is called by the public EnsureInitialized method.
57+
/// It ensures the appropriate version (x86/x64) of the native library is loaded.
58+
/// </remarks>
59+
internal static void Initialize()
60+
{
61+
try
62+
{
63+
EnsureNativeShimsLoaded();
64+
}
65+
catch (Exception ex)
66+
{
67+
throw new DllNotFoundException(
68+
$"Failed to load native library from {NativeShimsPath}. " +
69+
$"Ensure the correct {(Environment.Is64BitProcess ? "x64" : "x86")} version is present.",
70+
ex);
71+
}
72+
}
73+
74+
/// <summary>
75+
/// Loads the native library if it hasn't been loaded already.
76+
/// </summary>
77+
/// <exception cref="Exception">
78+
/// A variety of exceptions can be thrown during library loading, including but not limited to:
79+
/// - FileNotFoundException: If the DLL file is not found
80+
/// - BadImageFormatException: If the DLL is not compatible with the current architecture
81+
/// - Other exceptions based on the specific error condition encountered during loading
82+
/// </exception>
83+
private static void EnsureNativeShimsLoaded()
84+
{
85+
if (_nativeShims != null)
86+
{
87+
return;
88+
}
89+
90+
_nativeShims = UnmanagedDynamicLibrary.Open(NativeShimsPath);
91+
}
92+
}
93+
}
94+
}
95+
#endif

Yubico.Core/src/Yubico/PlatformInterop/Libraries.cs

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,50 +12,75 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
#if NET47
1615
using System;
17-
using System.IO;
18-
using System.Runtime.InteropServices;
19-
#endif
2016

2117
namespace Yubico.PlatformInterop
2218
{
19+
/// <summary>
20+
/// Handles the loading and management of native libraries required by the Yubico SDK.
21+
/// </summary>
22+
/// <remarks>
23+
/// This class provides cross-platform and cross-framework support for loading native libraries.
24+
/// The implementation differs based on the target framework:
25+
///
26+
/// For .NET Framework 4.7:
27+
/// - Native libraries must be placed in architecture-specific subdirectories (x86/x64)
28+
/// - Library loading is handled explicitly at runtime based on process architecture
29+
/// - Requires proper cleanup through the Cleanup method
30+
///
31+
/// For Modern .NET:
32+
/// - Native libraries are handled by the runtime's built-in library loading mechanism
33+
/// - Architecture-specific loading is managed automatically
34+
/// - No explicit cleanup is required
35+
/// </remarks>
2336
internal static partial class Libraries
2437
{
2538
#if NET47
26-
internal const string NativeShims = "Yubico.NativeS hims.dll";
27-
private static bool _isNativeShimsIsLoaded;
28-
2939
/// <summary>
30-
/// This method needs to run for .NET47 to determine to use either AppDirectory/x86/Yubico.NativeShims.dll or AppDirectory/x64/Yubico.NativeShims.dll
40+
/// The filename of the native shims library for .NET Framework 4.7.
3141
/// </summary>
32-
/// <exception cref="DllNotFoundException"></exception>
33-
internal static void EnsureNativeShimsLoaded()
34-
{
35-
if (_isNativeShimsIsLoaded)
36-
{
37-
return;
38-
}
39-
40-
IntPtr moduleHandle = LoadLibrary(NativeShimsPath);
41-
if (moduleHandle == IntPtr.Zero)
42-
{
43-
throw new DllNotFoundException($"Failed to load native library: {NativeShimsPath}. Error: {Marshal.GetLastWin32Error()}");
44-
}
42+
/// <remarks>
43+
/// For .NET Framework 4.7, the DLL must be placed in an architecture-specific subdirectory:
44+
/// - x86/Yubico.NativeShims.dll for 32-bit processes
45+
/// - x64/Yubico.NativeShims.dll for 64-bit processes
46+
/// The correct version is loaded at runtime based on the process architecture.
47+
/// </remarks>
48+
internal const string NativeShims = "Yubico.NativeShims.dll";
4549

46-
_isNativeShimsIsLoaded = true;
47-
}
48-
49-
private static string NativeShimsPath => Path.Combine(
50-
AppDomain.CurrentDomain.BaseDirectory,
51-
Environment.Is64BitProcess ? "x64" : "x86",
52-
NativeShims);
53-
54-
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
55-
[DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)]
56-
private static extern IntPtr LoadLibrary(string lpFileName);
50+
/// <summary>
51+
/// Ensures the native library is properly loaded for .NET Framework 4.7.
52+
/// </summary>
53+
/// <exception cref="DllNotFoundException">
54+
/// Thrown when the native library cannot be loaded. This could be due to:
55+
/// - Missing DLL file in the architecture-specific directory (x86/x64)
56+
/// - Incorrect architecture (x86/x64 mismatch)
57+
/// - Missing dependencies
58+
/// - Insufficient permissions
59+
/// </exception>
60+
/// <remarks>
61+
/// This method must be called before any P/Invoke calls are made.
62+
/// The implementation details are handled in Libraries.Net47.cs.
63+
/// </remarks>
64+
public static void EnsureInitialized() => Net47Implementation.Initialize();
5765
#else
66+
/// <summary>
67+
/// The filename of the native shims library for modern .NET versions.
68+
/// </summary>
69+
/// <remarks>
70+
/// For modern .NET, the runtime automatically handles library loading and architecture selection.
71+
/// The DLL extension is omitted as it's platform-specific and managed by the runtime.
72+
/// The library should be properly packaged with the correct runtimes/* folder structure in the NuGet package.
73+
/// </remarks>
5874
internal const string NativeShims = "Yubico.NativeShims";
75+
76+
/// <summary>
77+
/// No-op implementation for modern .NET versions.
78+
/// </summary>
79+
/// <remarks>
80+
/// Library loading is handled automatically by the runtime.
81+
/// This method exists only for API compatibility with .NET Framework 4.7 code.
82+
/// </remarks>
83+
public static void EnsureInitialized() { }
5984
#endif
6085
}
6186
}

Yubico.Core/src/Yubico/PlatformInterop/NativeMethods.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,10 @@ internal static partial class NativeMethods
2424
private const string MacDlLib = "libdl.dylib";
2525
private const string LinuxDlLib = "libdl.so";
2626

27-
#if NET47
2827
static NativeMethods()
2928
{
30-
Libraries.EnsureNativeShimsLoaded();
29+
Libraries.EnsureInitialized();
3130
}
32-
#endif
3331

3432
// Note that the DefaultDllImportSearchPaths attribute is a security best
3533
// practice on the Windows platform (and required by our analyzer

0 commit comments

Comments
 (0)