11// Copyright (c) Microsoft Corporation.
22// Licensed under the MIT License.
33
4- #if ! NET7_0_OR_GREATER
4+ #if ! NETCOREAPP3_0_OR_GREATER
55
66using System ;
7+ using System . ComponentModel ;
78using System . Runtime . InteropServices ;
8- #if ! ( NETFRAMEWORK || NETSTANDARD )
9- using SysNativeLibrary = System . Runtime . InteropServices . NativeLibrary ;
10- #endif
119
1210namespace Microsoft . JavaScript . NodeApi . Runtime ;
1311
@@ -39,15 +37,53 @@ public static nint GetMainProgramHandle()
3937 /// <summary>
4038 /// Loads a native library using default flags.
4139 /// </summary>
42- /// <param name="libraryName ">The name of the native library to be loaded.</param>
40+ /// <param name="libraryPath ">The name of the native library to be loaded.</param>
4341 /// <returns>The OS handle for the loaded native library.</returns>
44- public static nint Load ( string libraryName )
42+ public static nint Load ( string libraryPath )
4543 {
46- #if NETFRAMEWORK || NETSTANDARD
47- return LoadLibrary ( libraryName ) ;
48- #else
49- return SysNativeLibrary . Load ( libraryName ) ;
50- #endif
44+ return LoadFromPath ( libraryPath , throwOnError : true ) ;
45+ }
46+
47+ /// <summary>
48+ /// Provides a simple API for loading a native library and returns a value that indicates whether the operation succeeded.
49+ /// </summary>
50+ /// <param name="libraryPath">The name of the native library to be loaded.</param>
51+ /// <param name="handle">When the method returns, the OS handle of the loaded native library.</param>
52+ /// <returns><c>true</c> if the native library was loaded successfully; otherwise, <c>false</c>.</returns>
53+ public static bool TryLoad ( string libraryPath , out nint handle )
54+ {
55+ handle = LoadFromPath ( libraryPath , throwOnError : false ) ;
56+ return handle != 0 ;
57+ }
58+
59+ static nint LoadFromPath ( string libraryPath , bool throwOnError )
60+ {
61+ if ( libraryPath is null )
62+ throw new ArgumentNullException ( nameof ( libraryPath ) ) ;
63+
64+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
65+ {
66+ nint handle = LoadLibrary ( libraryPath ) ;
67+ if ( handle == 0 && throwOnError )
68+ throw new DllNotFoundException ( new Win32Exception ( Marshal . GetLastWin32Error ( ) ) . Message ) ;
69+
70+ return handle ;
71+ }
72+ else
73+ {
74+ dlerror ( ) ;
75+ nint handle = dlopen ( libraryPath , RTLD_LAZY ) ;
76+ nint error = dlerror ( ) ;
77+ if ( error != 0 )
78+ {
79+ if ( throwOnError )
80+ throw new DllNotFoundException ( Marshal . PtrToStringAuto ( error ) ) ;
81+
82+ handle = 0 ;
83+ }
84+
85+ return handle ;
86+ }
5187 }
5288
5389 /// <summary>
@@ -58,53 +94,168 @@ public static nint Load(string libraryName)
5894 /// <returns>The address of the symbol.</returns>
5995 public static nint GetExport ( nint handle , string name )
6096 {
61- #if NETFRAMEWORK || NETSTANDARD
62- return GetProcAddress ( handle , name ) ;
63- #else
64- return SysNativeLibrary . GetExport ( handle , name ) ;
65- #endif
97+ return GetSymbol ( handle , name , throwOnError : true ) ;
6698 }
6799
68100 public static bool TryGetExport ( nint handle , string name , out nint procAddress )
69101 {
70- #if NETFRAMEWORK || NETSTANDARD
71- procAddress = GetProcAddress ( handle , name ) ;
72- return procAddress != default ;
73- #else
74- return SysNativeLibrary . TryGetExport ( handle , name , out procAddress ) ;
75- #endif
102+ procAddress = GetSymbol ( handle , name , throwOnError : false ) ;
103+ return procAddress != 0 ;
104+ }
105+
106+ static nint GetSymbol ( nint handle , string name , bool throwOnError )
107+ {
108+ if ( handle == 0 )
109+ throw new ArgumentNullException ( nameof ( handle ) ) ;
110+ if ( string . IsNullOrEmpty ( name ) )
111+ throw new ArgumentNullException ( nameof ( name ) ) ;
112+
113+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
114+ {
115+ nint procAddress = GetProcAddress ( handle , name ) ;
116+ if ( procAddress == 0 && throwOnError )
117+ throw new EntryPointNotFoundException ( new Win32Exception ( Marshal . GetLastWin32Error ( ) ) . Message ) ;
118+
119+ return procAddress ;
120+ }
121+ else
122+ {
123+ dlerror ( ) ;
124+ nint procAddress = dlsym ( handle , name ) ;
125+ nint error = dlerror ( ) ;
126+ if ( error != 0 )
127+ {
128+ if ( throwOnError )
129+ throw new EntryPointNotFoundException ( Marshal . PtrToStringAuto ( error ) ) ;
130+
131+ procAddress = 0 ;
132+ }
133+
134+ return procAddress ;
135+ }
76136 }
77137
78138#pragma warning disable CA2101 // Specify marshaling for P/Invoke string arguments
79139
80140 [ DllImport ( "kernel32" ) ]
81141 private static extern nint GetModuleHandle ( string ? moduleName ) ;
82142
83- [ DllImport ( "kernel32" ) ]
143+ [ DllImport ( "kernel32" , SetLastError = true ) ]
84144 private static extern nint LoadLibrary ( string moduleName ) ;
85145
86- [ DllImport ( "kernel32" ) ]
146+ [ DllImport ( "kernel32" , SetLastError = true ) ]
87147 private static extern nint GetProcAddress ( nint hModule , string procName ) ;
88148
89- private static nint dlopen ( nint fileName , int flags )
149+ private delegate nint DlErrorDelegate ( ) ;
150+ private static DlErrorDelegate ? s_dlerror ;
151+
152+ private static nint dlerror ( )
90153 {
91- // Some Linux distros / versions have libdl version 2 only.
92- // Mac OS only has the unversioned library.
154+ // cache dlerror function
155+ if ( s_dlerror is not null )
156+ return s_dlerror ( ) ;
157+
158+ // some operating systems have dlerror in libc, some in libdl, some in libdl.so.2
159+ // attempt in that order
93160 try
94161 {
95- return dlopen2 ( fileName , flags ) ;
162+ return dlerror0 ( ) ;
96163 }
97- catch ( DllNotFoundException )
164+ catch ( EntryPointNotFoundException )
98165 {
99- return dlopen1 ( fileName , flags ) ;
166+ try
167+ {
168+ return ( s_dlerror = dlerror1 ) ( ) ;
169+ }
170+ catch ( DllNotFoundException )
171+ {
172+ return ( s_dlerror = dlerror2 ) ( ) ;
173+ }
100174 }
101175 }
102176
103- [ DllImport ( "libdl" , EntryPoint = "dlopen" ) ]
104- private static extern nint dlopen1 ( nint fileName , int flags ) ;
177+ [ DllImport ( "c" , EntryPoint = "dlerror" ) ]
178+ private static extern nint dlerror0 ( ) ;
179+
180+ [ DllImport ( "dl" , EntryPoint = "dlerror" ) ]
181+ private static extern nint dlerror1 ( ) ;
182+
183+ [ DllImport ( "libdl.so.2" , EntryPoint = "dlerror" ) ]
184+ private static extern nint dlerror2 ( ) ;
185+
186+ private delegate nint DlOpenDelegate ( string ? fileName , int flags ) ;
187+ private static DlOpenDelegate ? s_dlopen ;
188+
189+ private static nint dlopen ( string ? fileName , int flags )
190+ {
191+ // cache dlopen function
192+ if ( s_dlopen is not null )
193+ return s_dlopen ( fileName , flags ) ;
194+
195+ // some operating systems have dlopen in libc, some in libdl, some in libdl.so.2
196+ // attempt in that order
197+ try
198+ {
199+ return dlopen0 ( fileName , flags ) ;
200+ }
201+ catch ( EntryPointNotFoundException )
202+ {
203+ try
204+ {
205+ return ( s_dlopen = dlopen1 ) ( fileName , flags ) ;
206+ }
207+ catch ( DllNotFoundException )
208+ {
209+ return ( s_dlopen = dlopen2 ) ( fileName , flags ) ;
210+ }
211+ }
212+ }
213+
214+ [ DllImport ( "c" , EntryPoint = "dlopen" ) ]
215+ private static extern nint dlopen0 ( string ? fileName , int flags ) ;
216+
217+ [ DllImport ( "dl" , EntryPoint = "dlopen" ) ]
218+ private static extern nint dlopen1 ( string ? fileName , int flags ) ;
105219
106220 [ DllImport ( "libdl.so.2" , EntryPoint = "dlopen" ) ]
107- private static extern nint dlopen2 ( nint fileName , int flags ) ;
221+ private static extern nint dlopen2 ( string ? fileName , int flags ) ;
222+
223+ private delegate nint DlSymDelegate ( nint handle , string symbol ) ;
224+ private static DlSymDelegate ? s_dlsym ;
225+
226+ private static nint dlsym ( nint handle , string symbol )
227+ {
228+ // cache dlsym function
229+ if ( s_dlsym is not null )
230+ return s_dlsym ( handle , symbol ) ;
231+
232+ // some operating systems have dlsym in libc, some in libdl, some in libdl.so.2
233+ // attempt in that order
234+ try
235+ {
236+ return dlsym0 ( handle , symbol ) ;
237+ }
238+ catch ( EntryPointNotFoundException )
239+ {
240+ try
241+ {
242+ return ( s_dlsym = dlsym1 ) ( handle , symbol ) ;
243+ }
244+ catch ( DllNotFoundException )
245+ {
246+ return ( s_dlsym = dlsym2 ) ( handle , symbol ) ;
247+ }
248+ }
249+ }
250+
251+ [ DllImport ( "c" , EntryPoint = "dlsym" ) ]
252+ private static extern nint dlsym0 ( nint handle , string symbol ) ;
253+
254+ [ DllImport ( "dl" , EntryPoint = "dlsym" ) ]
255+ private static extern nint dlsym1 ( nint handle , string symbol ) ;
256+
257+ [ DllImport ( "libdl.so.2" , EntryPoint = "dlsym" ) ]
258+ private static extern nint dlsym2 ( nint handle , string symbol ) ;
108259
109260 private const int RTLD_LAZY = 1 ;
110261
0 commit comments