@@ -219,14 +219,43 @@ private static void InjectBootstrap(int processId, string pluginLoaderPath)
219219
220220 Console . WriteLine ( $ "Using native bootstrap: { bootstrapPath } ") ;
221221
222- // Wait a moment for process to initialize
223- Thread . Sleep ( 500 ) ;
222+ // Poll until we can open the process
223+ IntPtr hProcess = IntPtr . Zero ;
224+ for ( int i = 0 ; i < 50 ; i ++ )
225+ {
226+ hProcess = OpenProcess ( PROCESS_ALL_ACCESS , false , processId ) ;
227+ if ( hProcess != IntPtr . Zero )
228+ break ;
229+ Thread . Sleep ( 10 ) ;
230+ }
224231
225- // Open process with full access
226- IntPtr hProcess = OpenProcess ( PROCESS_ALL_ACCESS , false , processId ) ;
227232 if ( hProcess == IntPtr . Zero )
228233 {
229- throw new Exception ( $ "Failed to open process. Error: { Marshal . GetLastWin32Error ( ) } ") ;
234+ throw new Exception ( $ "Failed to open process after polling. Error: { Marshal . GetLastWin32Error ( ) } ") ;
235+ }
236+
237+ // Wait for CLR to be fully initialized by polling for clrjit.dll,
238+ // one of the last modules loaded during CLR startup.
239+ // Injecting before the CLR is ready causes a fatal crash (80131506).
240+ Console . WriteLine ( "Waiting for CLR to initialize in target process..." ) ;
241+ bool clrReady = false ;
242+ for ( int i = 0 ; i < 300 ; i ++ ) // 300 * 10ms = 3s max
243+ {
244+ if ( ProcessHasModule ( processId , "clrjit.dll" ) )
245+ {
246+ clrReady = true ;
247+ break ;
248+ }
249+ Thread . Sleep ( 10 ) ;
250+ }
251+
252+ if ( ! clrReady )
253+ {
254+ Console . WriteLine ( "WARNING: CLR may not be fully initialized, proceeding anyway..." ) ;
255+ }
256+ else
257+ {
258+ Console . WriteLine ( "CLR initialized, injecting..." ) ;
230259 }
231260
232261 try
@@ -303,6 +332,62 @@ private static void InjectBootstrap(int processId, string pluginLoaderPath)
303332 private const uint MEM_COMMIT = 0x1000 ;
304333 private const uint MEM_RESERVE = 0x2000 ;
305334 private const uint PAGE_READWRITE = 0x04 ;
335+ private const uint TH32CS_SNAPMODULE = 0x00000008 ;
336+ private const uint TH32CS_SNAPMODULE32 = 0x00000010 ;
337+
338+ [ StructLayout ( LayoutKind . Sequential , CharSet = CharSet . Unicode ) ]
339+ private struct MODULEENTRY32W
340+ {
341+ public uint dwSize ;
342+ public uint th32ModuleID ;
343+ public uint th32ProcessID ;
344+ public uint GlblcntUsage ;
345+ public uint ProccntUsage ;
346+ public IntPtr modBaseAddr ;
347+ public uint modBaseSize ;
348+ public IntPtr hModule ;
349+ [ MarshalAs ( UnmanagedType . ByValTStr , SizeConst = 256 ) ]
350+ public string szModule ;
351+ [ MarshalAs ( UnmanagedType . ByValTStr , SizeConst = 260 ) ]
352+ public string szExePath ;
353+ }
354+
355+ private static bool ProcessHasModule ( int processId , string moduleName )
356+ {
357+ IntPtr snap = CreateToolhelp32Snapshot ( TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32 , ( uint ) processId ) ;
358+ if ( snap == IntPtr . Zero || snap == new IntPtr ( - 1 ) )
359+ return false ;
360+
361+ try
362+ {
363+ var entry = new MODULEENTRY32W ( ) ;
364+ entry . dwSize = ( uint ) Marshal . SizeOf ( typeof ( MODULEENTRY32W ) ) ;
365+
366+ if ( ! Module32FirstW ( snap , ref entry ) )
367+ return false ;
368+
369+ do
370+ {
371+ if ( entry . szModule . Equals ( moduleName , StringComparison . OrdinalIgnoreCase ) )
372+ return true ;
373+ } while ( Module32NextW ( snap , ref entry ) ) ;
374+
375+ return false ;
376+ }
377+ finally
378+ {
379+ CloseHandle ( snap ) ;
380+ }
381+ }
382+
383+ [ DllImport ( "kernel32.dll" , SetLastError = true ) ]
384+ private static extern IntPtr CreateToolhelp32Snapshot ( uint dwFlags , uint th32ProcessID ) ;
385+
386+ [ DllImport ( "kernel32.dll" , SetLastError = true , CharSet = CharSet . Unicode ) ]
387+ private static extern bool Module32FirstW ( IntPtr hSnapshot , ref MODULEENTRY32W lpme ) ;
388+
389+ [ DllImport ( "kernel32.dll" , SetLastError = true , CharSet = CharSet . Unicode ) ]
390+ private static extern bool Module32NextW ( IntPtr hSnapshot , ref MODULEENTRY32W lpme ) ;
306391
307392 [ DllImport ( "kernel32.dll" , SetLastError = true ) ]
308393 private static extern IntPtr GetModuleHandle ( string lpModuleName ) ;
0 commit comments