1
1
// Copyright (c) Microsoft Corporation.
2
2
// Licensed under the MIT License.
3
3
4
- #if ! NET7_0_OR_GREATER
4
+ #if ! NETCOREAPP3_0_OR_GREATER
5
5
6
6
using System ;
7
+ using System . ComponentModel ;
7
8
using System . Runtime . InteropServices ;
8
- #if ! ( NETFRAMEWORK || NETSTANDARD )
9
- using SysNativeLibrary = System . Runtime . InteropServices . NativeLibrary ;
10
- #endif
11
9
12
10
namespace Microsoft . JavaScript . NodeApi . Runtime ;
13
11
@@ -39,15 +37,53 @@ public static nint GetMainProgramHandle()
39
37
/// <summary>
40
38
/// Loads a native library using default flags.
41
39
/// </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>
43
41
/// <returns>The OS handle for the loaded native library.</returns>
44
- public static nint Load ( string libraryName )
42
+ public static nint Load ( string libraryPath )
45
43
{
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
+ }
51
87
}
52
88
53
89
/// <summary>
@@ -58,53 +94,168 @@ public static nint Load(string libraryName)
58
94
/// <returns>The address of the symbol.</returns>
59
95
public static nint GetExport ( nint handle , string name )
60
96
{
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 ) ;
66
98
}
67
99
68
100
public static bool TryGetExport ( nint handle , string name , out nint procAddress )
69
101
{
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
+ }
76
136
}
77
137
78
138
#pragma warning disable CA2101 // Specify marshaling for P/Invoke string arguments
79
139
80
140
[ DllImport ( "kernel32" ) ]
81
141
private static extern nint GetModuleHandle ( string ? moduleName ) ;
82
142
83
- [ DllImport ( "kernel32" ) ]
143
+ [ DllImport ( "kernel32" , SetLastError = true ) ]
84
144
private static extern nint LoadLibrary ( string moduleName ) ;
85
145
86
- [ DllImport ( "kernel32" ) ]
146
+ [ DllImport ( "kernel32" , SetLastError = true ) ]
87
147
private static extern nint GetProcAddress ( nint hModule , string procName ) ;
88
148
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 ( )
90
153
{
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
93
160
try
94
161
{
95
- return dlopen2 ( fileName , flags ) ;
162
+ return dlerror0 ( ) ;
96
163
}
97
- catch ( DllNotFoundException )
164
+ catch ( EntryPointNotFoundException )
98
165
{
99
- return dlopen1 ( fileName , flags ) ;
166
+ try
167
+ {
168
+ return ( s_dlerror = dlerror1 ) ( ) ;
169
+ }
170
+ catch ( DllNotFoundException )
171
+ {
172
+ return ( s_dlerror = dlerror2 ) ( ) ;
173
+ }
100
174
}
101
175
}
102
176
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 ) ;
105
219
106
220
[ 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 ) ;
108
259
109
260
private const int RTLD_LAZY = 1 ;
110
261
0 commit comments