Skip to content

Commit

Permalink
Separated NET47 edge case logic
Browse files Browse the repository at this point in the history
  • Loading branch information
DennisDyallo committed Oct 22, 2024
1 parent fc42c06 commit 6559a35
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 35 deletions.
113 changes: 113 additions & 0 deletions Yubico.Core/src/Yubico/PlatformInterop/Libraries.Net47.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2024 Yubico AB
//
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#if NET47
using System;
using System.IO;

namespace Yubico.PlatformInterop
{
/// <summary>
/// .NET Framework 4.7 specific implementation for native library management.
/// </summary>
internal static partial class Libraries
{
/// <summary>
/// Encapsulates the .NET Framework 4.7 specific implementation details for native library management.
/// This nested class handles the dynamic loading of architecture-specific (x86/x64) native libraries.
/// </summary>
private static class Net47Implementation
{
// Handle to the loaded native library
private static UnmanagedDynamicLibrary? _nativeShims;

/// <summary>
/// Gets the full path to the architecture-specific native library.
/// </summary>
/// <remarks>
/// The path is constructed based on:
/// - The application's base directory
/// - The current process architecture (x86/x64)
/// - The native library filename
/// </remarks>
private static string NativeShimsPath =>
Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
Environment.Is64BitProcess
? "x64"
: "x86",
NativeShims);

/// <summary>
/// Initializes the native library for the current architecture.
/// </summary>
/// <remarks>
/// This method is called by the public EnsureInitialized method.
/// It ensures the appropriate version (x86/x64) of the native library is loaded.
/// </remarks>
internal static void Initialize()
{
try
{
EnsureNativeShimsLoaded();
}
catch (Exception ex)
{
throw new DllNotFoundException(
$"Failed to load native library from {NativeShimsPath}. " +
$"Ensure the correct {(Environment.Is64BitProcess ? "x64" : "x86")} version is present.",
ex);
}
}

/// <summary>
/// Loads the native library if it hasn't been loaded already.
/// </summary>
/// <exception cref="Exception">
/// A variety of exceptions can be thrown during library loading, including but not limited to:
/// - FileNotFoundException: If the DLL file is not found
/// - BadImageFormatException: If the DLL is not compatible with the current architecture
/// - Other exceptions based on the specific error condition encountered during loading
/// </exception>
private static void EnsureNativeShimsLoaded()
{
if (_nativeShims != null)
{
return;
}

_nativeShims = UnmanagedDynamicLibrary.Open(NativeShimsPath);
}

/// <summary>
/// Releases resources associated with the native library.
/// </summary>
/// <remarks>
/// This method safely disposes of the native library handle and resets the internal state.
/// It can be called multiple times safely.
/// </remarks>
internal static void Cleanup()
{
if (_nativeShims is null)
{
return;
}

_nativeShims.Dispose();
_nativeShims = null;
}
}
}
}
#endif
38 changes: 6 additions & 32 deletions Yubico.Core/src/Yubico/PlatformInterop/Libraries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,39 +23,13 @@ namespace Yubico.PlatformInterop
internal static partial class Libraries
{
#if NET47
internal const string NativeShims = "Yubico.NativeS hims.dll";
private static bool _isNativeShimsIsLoaded;

/// <summary>
/// This method needs to run for .NET47 to determine to use either AppDirectory/x86/Yubico.NativeShims.dll or AppDirectory/x64/Yubico.NativeShims.dll
/// </summary>
/// <exception cref="DllNotFoundException"></exception>
internal static void EnsureNativeShimsLoaded()
{
if (_isNativeShimsIsLoaded)
{
return;
}

IntPtr moduleHandle = LoadLibrary(NativeShimsPath);
if (moduleHandle == IntPtr.Zero)
{
throw new DllNotFoundException($"Failed to load native library: {NativeShimsPath}. Error: {Marshal.GetLastWin32Error()}");
}

_isNativeShimsIsLoaded = true;
}

private static string NativeShimsPath => Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
Environment.Is64BitProcess ? "x64" : "x86",
NativeShims);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)]
private static extern IntPtr LoadLibrary(string lpFileName);
internal const string NativeShims = "Yubico.NativeShims.dll";
public static void EnsureInitialized() => Net47Implementation.Initialize();
public static void Cleanup() => Net47Implementation.Cleanup();
#else
internal const string NativeShims = "Yubico.NativeShims";
internal const string NativeShims = "Yubico.NativeShims";
public static void EnsureInitialized() { }
public static void Cleanup() { }
#endif
}
}
4 changes: 1 addition & 3 deletions Yubico.Core/src/Yubico/PlatformInterop/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,10 @@ internal static partial class NativeMethods
private const string MacDlLib = "libdl.dylib";
private const string LinuxDlLib = "libdl.so";

#if NET47
static NativeMethods()
{
Libraries.EnsureNativeShimsLoaded();
Libraries.EnsureInitialized();
}
#endif

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

0 comments on commit 6559a35

Please sign in to comment.