1
1
// Licensed to the .NET Foundation under one or more agreements.
2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
+ using System . Diagnostics ;
4
5
using System . Diagnostics . CodeAnalysis ;
5
6
using System . Runtime . InteropServices ;
6
7
using static System . Net . Quic . Implementations . MsQuic . Internal . MsQuicNativeMethods ;
@@ -117,12 +118,17 @@ private MsQuicApi(NativeApi* vtable)
117
118
Registration = handle ;
118
119
}
119
120
120
- internal static MsQuicApi Api { get ; } = null ! ;
121
+ private static readonly delegate * unmanaged[ Cdecl] < uint , NativeApi * * , uint > MsQuicOpenVersion ;
122
+ private static readonly delegate * unmanaged[ Cdecl] < NativeApi * , void > MsQuicClose ;
123
+
124
+ private static readonly Lazy < MsQuicApi > s_api = new Lazy < MsQuicApi > ( AllocateMsQuicApi ) ;
125
+ internal static MsQuicApi Api => s_api . Value ;
121
126
122
127
internal static bool IsQuicSupported { get ; }
123
128
124
129
private const int MsQuicVersion = 1 ;
125
130
131
+ #pragma warning disable CA1810 // Initialize all static fields in 'MsQuicApi' when those fields are declared and remove the explicit static constructor
126
132
static MsQuicApi ( )
127
133
{
128
134
if ( OperatingSystem . IsWindows ( ) && ! IsWindowsVersionSupported ( ) )
@@ -135,30 +141,59 @@ static MsQuicApi()
135
141
return ;
136
142
}
137
143
138
- if ( NativeLibrary . TryLoad ( Interop . Libraries . MsQuic , typeof ( MsQuicApi ) . Assembly , DllImportSearchPath . AssemblyDirectory , out IntPtr msQuicHandle ) )
144
+ if ( ! NativeLibrary . TryLoad ( Interop . Libraries . MsQuic , typeof ( MsQuicApi ) . Assembly , DllImportSearchPath . AssemblyDirectory , out IntPtr msQuicHandle ) )
139
145
{
140
- try
141
- {
142
- if ( NativeLibrary . TryGetExport ( msQuicHandle , "MsQuicOpenVersion" , out IntPtr msQuicOpenVersionAddress ) )
143
- {
144
- delegate * unmanaged[ Cdecl] < uint , out NativeApi * , uint > msQuicOpenVersion =
145
- ( delegate * unmanaged[ Cdecl] < uint , out NativeApi * , uint > ) msQuicOpenVersionAddress ;
146
- uint status = msQuicOpenVersion ( MsQuicVersion , out NativeApi * vtable ) ;
147
- if ( MsQuicStatusHelper . SuccessfulStatusCode ( status ) )
148
- {
149
- IsQuicSupported = true ;
150
- Api = new MsQuicApi ( vtable ) ;
151
- }
152
- }
153
- }
154
- finally
146
+ // MsQuic library not loaded
147
+ return ;
148
+ }
149
+
150
+ MsQuicOpenVersion = ( delegate * unmanaged[ Cdecl] < uint , NativeApi * * , uint > ) NativeLibrary . GetExport ( msQuicHandle , nameof ( MsQuicOpenVersion ) ) ;
151
+ MsQuicClose = ( delegate * unmanaged[ Cdecl] < NativeApi * , void > ) NativeLibrary . GetExport ( msQuicHandle , nameof ( MsQuicClose ) ) ;
152
+
153
+ if ( ! TryOpenMsQuic ( out NativeApi * apiTable , out _ ) )
154
+ {
155
+ // Different version of the library.
156
+ return ;
157
+ }
158
+
159
+ IsQuicSupported = true ;
160
+
161
+ // Gracefully close the API table to free resources. The API table will be allocated lazily again if needed
162
+ MsQuicClose ( apiTable ) ;
163
+ }
164
+ #pragma warning restore CA1810
165
+
166
+ private static MsQuicApi AllocateMsQuicApi ( )
167
+ {
168
+ Debug . Assert ( IsQuicSupported ) ;
169
+
170
+ if ( ! TryOpenMsQuic ( out NativeApi * apiTable , out uint openStatus ) )
171
+ {
172
+ QuicExceptionHelpers . ThrowIfFailed ( openStatus ) ;
173
+ }
174
+
175
+ return new MsQuicApi ( apiTable ) ;
176
+ }
177
+
178
+ private static bool TryOpenMsQuic ( out NativeApi * apiTable , out uint openStatus )
179
+ {
180
+ Debug . Assert ( MsQuicOpenVersion != null ) ;
181
+
182
+ NativeApi * table = null ;
183
+ openStatus = MsQuicOpenVersion ( ( uint ) MsQuicVersion , & table ) ;
184
+ if ( ! MsQuicStatusHelper . SuccessfulStatusCode ( openStatus ) )
185
+ {
186
+ if ( NetEventSource . Log . IsEnabled ( ) )
155
187
{
156
- if ( ! IsQuicSupported )
157
- {
158
- NativeLibrary . Free ( msQuicHandle ) ;
159
- }
188
+ NetEventSource . Info ( null , $ "MsQuicOpenVersion(version: { MsQuicVersion } ) returned { MsQuicStatusCodes . GetError ( openStatus ) } status code.") ;
160
189
}
190
+
191
+ apiTable = null ;
192
+ return false ;
161
193
}
194
+
195
+ apiTable = table ;
196
+ return true ;
162
197
}
163
198
164
199
private static bool IsWindowsVersionSupported ( ) => OperatingSystem . IsWindowsVersionAtLeast ( MinWindowsVersion . Major ,
0 commit comments