11// Licensed to the .NET Foundation under one or more agreements.
22// The .NET Foundation licenses this file to you under the MIT license.
33
4+ using System . Diagnostics ;
45using System . Diagnostics . CodeAnalysis ;
56using System . Runtime . InteropServices ;
67using Microsoft . Quic ;
@@ -47,7 +48,8 @@ private MsQuicApi(QUIC_API_TABLE* apiTable)
4748 }
4849 }
4950
50- internal static MsQuicApi Api { get ; } = null ! ;
51+ internal static Lazy < MsQuicApi > _api = new Lazy < MsQuicApi > ( AllocateMsQuicApi ) ;
52+ internal static MsQuicApi Api => _api . Value ;
5153
5254 internal static bool IsQuicSupported { get ; }
5355
@@ -58,29 +60,21 @@ private MsQuicApi(QUIC_API_TABLE* apiTable)
5860
5961 static MsQuicApi ( )
6062 {
61- IntPtr msQuicHandle ;
62- if ( ! NativeLibrary . TryLoad ( $ "{ Interop . Libraries . MsQuic } .{ MsQuicVersion . Major } ", typeof ( MsQuicApi ) . Assembly , DllImportSearchPath . AssemblyDirectory , out msQuicHandle ) &&
63- ! NativeLibrary . TryLoad ( Interop . Libraries . MsQuic , typeof ( MsQuicApi ) . Assembly , DllImportSearchPath . AssemblyDirectory , out msQuicHandle ) )
63+ if ( ! TryLoadMsQuic ( out IntPtr msQuicHandle ) )
6464 {
6565 return ;
6666 }
6767
6868 try
6969 {
70- if ( ! NativeLibrary . TryGetExport ( msQuicHandle , "MsQuicOpenVersion" , out IntPtr msQuicOpenVersionAddress ) )
71- {
72- return ;
73- }
74-
75- QUIC_API_TABLE * apiTable = null ;
76- delegate * unmanaged[ Cdecl] < uint , QUIC_API_TABLE * * , int > msQuicOpenVersion = ( delegate * unmanaged[ Cdecl] < uint , QUIC_API_TABLE * * , int > ) msQuicOpenVersionAddress ;
77- if ( StatusFailed ( msQuicOpenVersion ( ( uint ) MsQuicVersion . Major , & apiTable ) ) )
70+ if ( ! TryOpenMsQuic ( msQuicHandle , out QUIC_API_TABLE * apiTable ) )
7871 {
7972 return ;
8073 }
8174
8275 try
8376 {
77+ // check version
8478 int arraySize = 4 ;
8579 uint * libVersion = stackalloc uint [ arraySize ] ;
8680 uint size = ( uint ) arraySize * sizeof ( uint ) ;
@@ -99,7 +93,7 @@ static MsQuicApi()
9993 return ;
10094 }
10195
102- // Assume SChannel is being used on windows and query for the actual provider from the library
96+ // Assume SChannel is being used on windows and query for the actual provider from the library if querying is supported
10397 QUIC_TLS_PROVIDER provider = OperatingSystem . IsWindows ( ) ? QUIC_TLS_PROVIDER . SCHANNEL : QUIC_TLS_PROVIDER . OPENSSL ;
10498 size = sizeof ( QUIC_TLS_PROVIDER ) ;
10599 apiTable ->GetParam ( null , QUIC_PARAM_GLOBAL_TLS_PROVIDER , & size , & provider ) ;
@@ -122,18 +116,14 @@ static MsQuicApi()
122116 Tls13ClientMayBeDisabled = IsTls13Disabled ( isServer : false ) ;
123117 }
124118
125- Api = new MsQuicApi ( apiTable ) ;
126119 IsQuicSupported = true ;
127120 }
128121 finally
129122 {
130- if ( ! IsQuicSupported && NativeLibrary . TryGetExport ( msQuicHandle , "MsQuicClose" , out IntPtr msQuicClose ) )
131- {
132- // Gracefully close the API table
133- ( ( delegate * unmanaged[ Cdecl] < QUIC_API_TABLE * , void > ) msQuicClose ) ( apiTable ) ;
134- }
123+ // Gracefully close the API table to free resources. The API table will be allocated lazily again if needed
124+ bool closed = TryCloseMsQuic ( msQuicHandle , apiTable ) ;
125+ Debug . Assert ( closed , "Failed to close MsQuic" ) ;
135126 }
136-
137127 }
138128 finally
139129 {
@@ -144,6 +134,53 @@ static MsQuicApi()
144134 }
145135 }
146136
137+ internal static MsQuicApi AllocateMsQuicApi ( )
138+ {
139+ Debug . Assert ( IsQuicSupported ) ;
140+
141+ if ( TryLoadMsQuic ( out IntPtr msQuicHandle ) &&
142+ TryOpenMsQuic ( msQuicHandle , out QUIC_API_TABLE * apiTable ) )
143+ {
144+ return new MsQuicApi ( apiTable ) ;
145+ }
146+
147+ throw new Exception ( "Failed to create MsQuicApi instance" ) ;
148+ }
149+
150+ internal static bool TryLoadMsQuic ( out IntPtr msQuicHandle ) =>
151+ NativeLibrary . TryLoad ( $ "{ Interop . Libraries . MsQuic } .{ MsQuicVersion . Major } ", typeof ( MsQuicApi ) . Assembly , DllImportSearchPath . AssemblyDirectory , out msQuicHandle ) ||
152+ NativeLibrary . TryLoad ( Interop . Libraries . MsQuic , typeof ( MsQuicApi ) . Assembly , DllImportSearchPath . AssemblyDirectory , out msQuicHandle ) ;
153+
154+ internal static bool TryOpenMsQuic ( IntPtr msQuicHandle , out QUIC_API_TABLE * apiTable )
155+ {
156+ apiTable = null ;
157+ if ( ! NativeLibrary . TryGetExport ( msQuicHandle , "MsQuicOpenVersion" , out IntPtr msQuicOpenVersionAddress ) )
158+ {
159+ return false ;
160+ }
161+
162+ QUIC_API_TABLE * table = null ;
163+ delegate * unmanaged[ Cdecl] < uint , QUIC_API_TABLE * * , int > msQuicOpenVersion = ( delegate * unmanaged[ Cdecl] < uint , QUIC_API_TABLE * * , int > ) msQuicOpenVersionAddress ;
164+ if ( StatusFailed ( msQuicOpenVersion ( ( uint ) MsQuicVersion . Major , & table ) ) )
165+ {
166+ return false ;
167+ }
168+
169+ apiTable = table ;
170+ return true ;
171+ }
172+
173+ internal static bool TryCloseMsQuic ( IntPtr msQuicHandle , QUIC_API_TABLE * apiTable )
174+ {
175+ if ( NativeLibrary . TryGetExport ( msQuicHandle , "MsQuicClose" , out IntPtr msQuicClose ) )
176+ {
177+ ( ( delegate * unmanaged[ Cdecl] < QUIC_API_TABLE * , void > ) msQuicClose ) ( apiTable ) ;
178+ return true ;
179+ }
180+
181+ return false ;
182+ }
183+
147184 private static bool IsWindowsVersionSupported ( ) => OperatingSystem . IsWindowsVersionAtLeast ( MinWindowsVersion . Major ,
148185 MinWindowsVersion . Minor , MinWindowsVersion . Build , MinWindowsVersion . Revision ) ;
149186
0 commit comments