7 #include "w32process.h"
8 #include "w32process-internals.h"
9 #include "w32process-win32-internals.h"
12 #include "object-internals.h"
14 #include "class-internals.h"
16 #include "utils/mono-proclib.h"
17 #include "utils/w32api.h"
18 #include "icall-decl.h"
21 /* define LOGDEBUG(...) g_message(__VA_ARGS__) */
23 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) && defined(HOST_WIN32)
26 mono_w32process_get_pid (gpointer handle
)
28 return GetProcessId (handle
);
32 mono_w32process_try_get_modules (gpointer process
, HMODULE
*modules
, guint32 size
, PDWORD needed
)
34 return EnumProcessModules (process
, modules
, size
, needed
);
38 mono_w32process_module_get_name (gpointer process
, gpointer module
, gunichar2
*basename
, guint32 size
)
40 return GetModuleBaseName (process
, (HMODULE
)module
, basename
, size
);
44 mono_w32process_module_get_filename (gpointer process
, gpointer module
, gunichar2
*basename
, guint32 size
)
46 return GetModuleFileNameEx (process
, (HMODULE
)module
, basename
, size
);
50 mono_w32process_module_get_information (gpointer process
, gpointer module
, MODULEINFO
*modinfo
, guint32 size
)
52 return GetModuleInformation (process
, (HMODULE
)module
, modinfo
, size
);
56 mono_w32process_get_fileversion_info (const gunichar2
*filename
, gpointer
*data
)
64 datasize
= GetFileVersionInfoSize (filename
, &handle
);
68 *data
= g_malloc0 (datasize
);
69 if (!GetFileVersionInfo (filename
, handle
, datasize
, *data
)) {
78 mono_w32process_ver_query_value (gconstpointer datablock
, const gunichar2
*subblock
, gpointer
*buffer
, guint32
*len
)
80 return VerQueryValue (datablock
, subblock
, buffer
, len
);
84 mono_w32process_ver_language_name (guint32 lang
, gunichar2
*lang_out
, guint32 lang_len
)
86 return VerLanguageName (lang
, lang_out
, lang_len
);
89 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) && defined(HOST_WIN32) */
91 static MonoImage
*system_image
;
94 stash_system_image (MonoImage
*image
)
100 get_file_version_info_class (void)
102 static MonoClass
*file_version_info_class
;
104 if (file_version_info_class
)
105 return file_version_info_class
;
107 g_assert (system_image
);
109 return file_version_info_class
= mono_class_load_from_name (
110 system_image
, "System.Diagnostics", "FileVersionInfo");
114 get_process_module_class (void)
116 static MonoClass
*process_module_class
;
118 if (process_module_class
)
119 return process_module_class
;
121 g_assert (system_image
);
123 return process_module_class
= mono_class_load_from_name (
124 system_image
, "System.Diagnostics", "ProcessModule");
128 unicode_chars (const gunichar2
*str
)
131 for (len
= 0; str
[len
] != '\0'; ++len
) {}
136 process_set_field_object (MonoObject
*obj
, const gchar
*fieldname
, MonoObject
*data
)
139 MonoClassField
*field
;
141 LOGDEBUG (g_message ("%s: Setting field %s to object at %p", __func__
, fieldname
, data
));
143 klass
= mono_object_class (obj
);
146 field
= mono_class_get_field_from_name_full (klass
, fieldname
, NULL
);
149 mono_gc_wbarrier_generic_store_internal (((char *)obj
) + field
->offset
, data
);
153 process_set_field_string (MonoObject
*obj
, const gchar
*fieldname
, const gunichar2
*val
, guint32 len
, MonoError
*error
)
157 MonoClassField
*field
;
162 LOGDEBUG (g_message ("%s: Setting field %s to [%s]", __func__
, fieldname
, g_utf16_to_utf8 (val
, len
, NULL
, NULL
, NULL
)));
164 domain
= mono_object_domain (obj
);
167 klass
= mono_object_class (obj
);
170 field
= mono_class_get_field_from_name_full (klass
, fieldname
, NULL
);
173 string
= mono_string_new_utf16_checked (domain
, val
, len
, error
);
174 return_if_nok (error
);
176 mono_gc_wbarrier_generic_store_internal (((char *)obj
) + field
->offset
, (MonoObject
*)string
);
180 process_set_field_string_char (MonoObject
*obj
, const gchar
*fieldname
, const gchar
*val
, MonoError
*error
)
184 MonoClassField
*field
;
188 LOGDEBUG (g_message ("%s: Setting field %s to [%s]", __func__
, fieldname
, val
));
190 domain
= mono_object_domain (obj
);
193 klass
= mono_object_class (obj
);
196 field
= mono_class_get_field_from_name_full (klass
, fieldname
, NULL
);
199 string
= mono_string_new_checked (domain
, val
, error
);
200 return_if_nok (error
);
202 mono_gc_wbarrier_generic_store_internal (((char *)obj
) + field
->offset
, (MonoObject
*)string
);
206 process_set_field_int (MonoObject
*obj
, const gchar
*fieldname
, guint32 val
)
209 MonoClassField
*field
;
211 LOGDEBUG (g_message ("%s: Setting field %s to %d", __func__
,fieldname
, val
));
213 klass
= mono_object_class (obj
);
216 field
= mono_class_get_field_from_name_full (klass
, fieldname
, NULL
);
219 *(guint32
*)(((char *)obj
) + field
->offset
)=val
;
223 process_set_field_intptr (MonoObject
*obj
, const gchar
*fieldname
, gpointer val
)
226 MonoClassField
*field
;
228 LOGDEBUG (g_message ("%s: Setting field %s to %p", __func__
, fieldname
, val
));
230 klass
= mono_object_class (obj
);
233 field
= mono_class_get_field_from_name_full (klass
, fieldname
, NULL
);
236 *(gpointer
*)(((char *)obj
) + field
->offset
) = val
;
240 process_set_field_bool (MonoObject
*obj
, const gchar
*fieldname
, gboolean val
)
243 MonoClassField
*field
;
245 LOGDEBUG (g_message ("%s: Setting field %s to %s", __func__
, fieldname
, val
? "TRUE":"FALSE"));
247 klass
= mono_object_class (obj
);
250 field
= mono_class_get_field_from_name_full (klass
, fieldname
, NULL
);
253 *(guint8
*)(((char *)obj
) + field
->offset
) = val
;
256 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
258 #define SFI_COMMENTS "\\StringFileInfo\\%02X%02X%02X%02X\\Comments"
259 #define SFI_COMPANYNAME "\\StringFileInfo\\%02X%02X%02X%02X\\CompanyName"
260 #define SFI_FILEDESCRIPTION "\\StringFileInfo\\%02X%02X%02X%02X\\FileDescription"
261 #define SFI_FILEVERSION "\\StringFileInfo\\%02X%02X%02X%02X\\FileVersion"
262 #define SFI_INTERNALNAME "\\StringFileInfo\\%02X%02X%02X%02X\\InternalName"
263 #define SFI_LEGALCOPYRIGHT "\\StringFileInfo\\%02X%02X%02X%02X\\LegalCopyright"
264 #define SFI_LEGALTRADEMARKS "\\StringFileInfo\\%02X%02X%02X%02X\\LegalTrademarks"
265 #define SFI_ORIGINALFILENAME "\\StringFileInfo\\%02X%02X%02X%02X\\OriginalFilename"
266 #define SFI_PRIVATEBUILD "\\StringFileInfo\\%02X%02X%02X%02X\\PrivateBuild"
267 #define SFI_PRODUCTNAME "\\StringFileInfo\\%02X%02X%02X%02X\\ProductName"
268 #define SFI_PRODUCTVERSION "\\StringFileInfo\\%02X%02X%02X%02X\\ProductVersion"
269 #define SFI_SPECIALBUILD "\\StringFileInfo\\%02X%02X%02X%02X\\SpecialBuild"
270 #define EMPTY_STRING (gunichar2*)"\000\000"
277 static StringTableEntry stringtable_entries
[] = {
278 { "comments", SFI_COMMENTS
},
279 { "companyname", SFI_COMPANYNAME
},
280 { "filedescription", SFI_FILEDESCRIPTION
},
281 { "fileversion", SFI_FILEVERSION
},
282 { "internalname", SFI_INTERNALNAME
},
283 { "legalcopyright", SFI_LEGALCOPYRIGHT
},
284 { "legaltrademarks", SFI_LEGALTRADEMARKS
},
285 { "originalfilename", SFI_ORIGINALFILENAME
},
286 { "privatebuild", SFI_PRIVATEBUILD
},
287 { "productname", SFI_PRODUCTNAME
},
288 { "productversion", SFI_PRODUCTVERSION
},
289 { "specialbuild", SFI_SPECIALBUILD
}
293 process_module_string_read (MonoObject
*filever
, gpointer data
, const gchar
*fieldname
,
294 guchar lang_hi
, guchar lang_lo
, const gchar
*key
, MonoError
*error
)
296 gchar
*lang_key_utf8
;
297 gunichar2
*lang_key
, *buffer
;
302 lang_key_utf8
= g_strdup_printf (key
, lang_lo
, lang_hi
, 0x04, 0xb0);
304 LOGDEBUG (g_message ("%s: asking for [%s]", __func__
, lang_key_utf8
));
306 lang_key
= g_utf8_to_utf16 (lang_key_utf8
, -1, NULL
, NULL
, NULL
);
308 if (mono_w32process_ver_query_value (data
, lang_key
, (gpointer
*)&buffer
, &chars
) && chars
> 0) {
309 LOGDEBUG (g_message ("%s: found %d chars of [%s]", __func__
, chars
, g_utf16_to_utf8 (buffer
, chars
, NULL
, NULL
, NULL
)));
310 /* chars includes trailing null */
311 process_set_field_string (filever
, fieldname
, buffer
, chars
- 1, error
);
313 process_set_field_string (filever
, fieldname
, EMPTY_STRING
, 0, error
);
317 g_free (lang_key_utf8
);
321 process_module_stringtable (MonoObject
*filever
, gpointer data
, guchar lang_hi
, guchar lang_lo
, MonoError
*error
)
323 for (int i
= 0; i
< G_N_ELEMENTS (stringtable_entries
); ++i
) {
324 process_module_string_read (filever
, data
, stringtable_entries
[i
].name
,
325 lang_hi
, lang_lo
, stringtable_entries
[i
].id
, error
);
326 return_if_nok (error
);
331 mono_w32process_get_fileversion (MonoObject
*filever
, gunichar2
*filename
, MonoError
*error
)
333 VS_FIXEDFILEINFO
*ffi
;
337 UINT ffi_size
, trans_size
;
338 gunichar2 lang_buf
[128];
339 guint32 lang
, lang_count
;
343 if (!mono_w32process_get_fileversion_info (filename
, &data
))
346 query
= g_utf8_to_utf16 ("\\", -1, NULL
, NULL
, NULL
);
352 if (mono_w32process_ver_query_value (data
, query
, (gpointer
*)&ffi
, &ffi_size
)) {
353 #define LOWORD(i32) ((guint16)((i32) & 0xFFFF))
354 #define HIWORD(i32) ((guint16)(((guint32)(i32) >> 16) & 0xFFFF))
356 LOGDEBUG (g_message ("%s: recording assembly: FileName [%s] FileVersionInfo [%d.%d.%d.%d]",
357 __func__
, g_utf16_to_utf8 (filename
, -1, NULL
, NULL
, NULL
), HIWORD (ffi
->dwFileVersionMS
),
358 LOWORD (ffi
->dwFileVersionMS
), HIWORD (ffi
->dwFileVersionLS
), LOWORD (ffi
->dwFileVersionLS
)));
360 process_set_field_int (filever
, "filemajorpart", HIWORD (ffi
->dwFileVersionMS
));
361 process_set_field_int (filever
, "fileminorpart", LOWORD (ffi
->dwFileVersionMS
));
362 process_set_field_int (filever
, "filebuildpart", HIWORD (ffi
->dwFileVersionLS
));
363 process_set_field_int (filever
, "fileprivatepart", LOWORD (ffi
->dwFileVersionLS
));
365 process_set_field_int (filever
, "productmajorpart", HIWORD (ffi
->dwProductVersionMS
));
366 process_set_field_int (filever
, "productminorpart", LOWORD (ffi
->dwProductVersionMS
));
367 process_set_field_int (filever
, "productbuildpart", HIWORD (ffi
->dwProductVersionLS
));
368 process_set_field_int (filever
, "productprivatepart", LOWORD (ffi
->dwProductVersionLS
));
370 process_set_field_bool (filever
, "isdebug", ((ffi
->dwFileFlags
& ffi
->dwFileFlagsMask
) & VS_FF_DEBUG
) != 0);
371 process_set_field_bool (filever
, "isprerelease", ((ffi
->dwFileFlags
& ffi
->dwFileFlagsMask
) & VS_FF_PRERELEASE
) != 0);
372 process_set_field_bool (filever
, "ispatched", ((ffi
->dwFileFlags
& ffi
->dwFileFlagsMask
) & VS_FF_PATCHED
) != 0);
373 process_set_field_bool (filever
, "isprivatebuild", ((ffi
->dwFileFlags
& ffi
->dwFileFlagsMask
) & VS_FF_PRIVATEBUILD
) != 0);
374 process_set_field_bool (filever
, "isspecialbuild", ((ffi
->dwFileFlags
& ffi
->dwFileFlagsMask
) & VS_FF_SPECIALBUILD
) != 0);
381 query
= g_utf8_to_utf16 ("\\VarFileInfo\\Translation", -1, NULL
, NULL
, NULL
);
387 if (mono_w32process_ver_query_value (data
, query
, (gpointer
*)&trans_data
, &trans_size
)) {
388 /* use the first language ID we see */
389 if (trans_size
>= 4) {
390 LOGDEBUG (g_message("%s: %s has 0x%0x 0x%0x 0x%0x 0x%0x", __func__
, g_utf16_to_utf8 (filename
, -1, NULL
, NULL
, NULL
), trans_data
[0], trans_data
[1], trans_data
[2], trans_data
[3]));
391 lang
= (trans_data
[0]) | (trans_data
[1] << 8) | (trans_data
[2] << 16) | (trans_data
[3] << 24);
392 /* Only give the lower 16 bits to mono_w32process_ver_language_name, as Windows gets confused otherwise */
393 lang_count
= mono_w32process_ver_language_name (lang
& 0xFFFF, lang_buf
, 128);
395 process_set_field_string (filever
, "language", lang_buf
, lang_count
, error
);
399 process_module_stringtable (filever
, data
, trans_data
[0], trans_data
[1], error
);
406 for (i
= 0; i
< G_N_ELEMENTS (stringtable_entries
); ++i
) {
407 /* No strings, so set every field to the empty string */
408 process_set_field_string (filever
, stringtable_entries
[i
].name
, EMPTY_STRING
, 0, error
);
413 /* And language seems to be set to en_US according to bug 374600 */
414 lang_count
= mono_w32process_ver_language_name (0x0409, lang_buf
, 128);
416 process_set_field_string (filever
, "language", lang_buf
, lang_count
, error
);
427 #endif /* #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
430 ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject
*this_obj
, MonoString
*filename
)
434 stash_system_image (m_class_get_image (mono_object_class (this_obj
)));
436 mono_w32process_get_fileversion (this_obj
, mono_string_chars_internal (filename
), error
);
437 if (!mono_error_ok (error
)) {
438 mono_error_set_pending_exception (error
);
442 process_set_field_string (this_obj
, "filename", mono_string_chars_internal (filename
), mono_string_length_internal (filename
), error
);
443 if (!mono_error_ok (error
)) {
444 mono_error_set_pending_exception (error
);
448 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
451 get_domain_assemblies (MonoDomain
*domain
)
454 GPtrArray
*assemblies
;
457 * Make a copy of the list of assemblies because we can't hold the assemblies
458 * lock while creating objects etc.
460 assemblies
= g_ptr_array_new ();
461 mono_domain_assemblies_lock (domain
);
462 for (tmp
= domain
->domain_assemblies
; tmp
; tmp
= tmp
->next
) {
463 MonoAssembly
*ass
= (MonoAssembly
*)tmp
->data
;
464 if (ass
->image
->fileio_used
)
466 g_ptr_array_add (assemblies
, ass
);
468 mono_domain_assemblies_unlock (domain
);
474 process_add_module (HANDLE process
, HMODULE mod
, gunichar2
*filename
, gunichar2
*modulename
, MonoClass
*proc_class
, MonoError
*error
)
476 MonoObject
*item
, *filever
;
477 MonoDomain
*domain
= mono_domain_get ();
483 /* Build a System.Diagnostics.ProcessModule with the data. */
484 item
= mono_object_new_checked (domain
, proc_class
, error
);
485 return_val_if_nok (error
, NULL
);
487 filever
= mono_object_new_checked (domain
, get_file_version_info_class (), error
);
488 return_val_if_nok (error
, NULL
);
490 mono_w32process_get_fileversion (filever
, filename
, error
);
491 return_val_if_nok (error
, NULL
);
493 process_set_field_string (filever
, "filename", filename
, unicode_chars (filename
), error
);
494 return_val_if_nok (error
, NULL
);
496 ok
= mono_w32process_module_get_information (process
, mod
, &modinfo
, sizeof(MODULEINFO
));
498 process_set_field_intptr (item
, "baseaddr", modinfo
.lpBaseOfDll
);
499 process_set_field_intptr (item
, "entryaddr", modinfo
.EntryPoint
);
500 process_set_field_int (item
, "memory_size", modinfo
.SizeOfImage
);
503 process_set_field_string (item
, "filename", filename
, unicode_chars (filename
), error
);
504 return_val_if_nok (error
, NULL
);
506 process_set_field_string (item
, "modulename", modulename
, unicode_chars (modulename
), error
);
507 return_val_if_nok (error
, NULL
);
509 process_set_field_object (item
, "version_info", filever
);
515 process_get_assembly_fileversion (MonoObject
*filever
, MonoAssembly
*assembly
)
517 process_set_field_int (filever
, "filemajorpart", assembly
->aname
.major
);
518 process_set_field_int (filever
, "fileminorpart", assembly
->aname
.minor
);
519 process_set_field_int (filever
, "filebuildpart", assembly
->aname
.build
);
523 process_get_module (MonoAssembly
*assembly
, MonoClass
*proc_class
, MonoError
*error
)
525 MonoObject
*item
, *filever
;
527 char *filename
= NULL
;
528 const gchar
*modulename
;
532 domain
= mono_domain_get ();
534 modulename
= assembly
->aname
.name
;
536 /* Build a System.Diagnostics.ProcessModule with the data. */
537 item
= mono_object_new_checked (domain
, proc_class
, error
);
538 goto_if_nok (error
, return_null
);
540 filever
= mono_object_new_checked (domain
, get_file_version_info_class (), error
);
541 goto_if_nok (error
, return_null
);
543 filename
= g_strdup_printf ("[In Memory] %s", modulename
);
545 process_get_assembly_fileversion (filever
, assembly
);
546 process_set_field_string_char (filever
, "filename", filename
, error
);
547 goto_if_nok (error
, return_null
);
548 process_set_field_object (item
, "version_info", filever
);
550 process_set_field_intptr (item
, "baseaddr", assembly
->image
->raw_data
);
551 process_set_field_int (item
, "memory_size", assembly
->image
->raw_data_len
);
552 process_set_field_string_char (item
, "filename", filename
, error
);
553 goto_if_nok (error
, return_null
);
554 process_set_field_string_char (item
, "modulename", modulename
, error
);
555 goto_if_nok (error
, return_null
);
565 /* Returns an array of System.Diagnostics.ProcessModule */
567 ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject
*this_obj
, HANDLE process
)
570 MonoArray
*temp_arr
= NULL
;
573 gunichar2 filename
[MAX_PATH
];
574 gunichar2 modname
[MAX_PATH
];
576 guint32 count
= 0, module_count
= 0, assembly_count
= 0;
577 guint32 i
, num_added
= 0;
578 GPtrArray
*assemblies
= NULL
;
580 stash_system_image (m_class_get_image (mono_object_class (this_obj
)));
582 if (mono_w32process_get_pid (process
) == mono_process_current_pid ()) {
583 assemblies
= get_domain_assemblies (mono_domain_get ());
584 assembly_count
= assemblies
->len
;
587 if (mono_w32process_try_get_modules (process
, mods
, sizeof(mods
), &needed
))
588 module_count
+= needed
/ sizeof(HMODULE
);
590 count
= module_count
+ assembly_count
;
591 temp_arr
= mono_array_new_checked (mono_domain_get (), get_process_module_class (), count
, error
);
592 if (mono_error_set_pending_exception (error
))
595 for (i
= 0; i
< module_count
; i
++) {
596 if (mono_w32process_module_get_name (process
, mods
[i
], modname
, MAX_PATH
)
597 && mono_w32process_module_get_filename (process
, mods
[i
], filename
, MAX_PATH
))
599 MonoObject
*module
= process_add_module (process
, mods
[i
], filename
, modname
, get_process_module_class (), error
);
600 if (!mono_error_ok (error
)) {
601 mono_error_set_pending_exception (error
);
604 mono_array_setref_internal (temp_arr
, num_added
++, module
);
609 for (i
= 0; i
< assembly_count
; i
++) {
610 MonoAssembly
*ass
= (MonoAssembly
*)g_ptr_array_index (assemblies
, i
);
611 MonoObject
*module
= process_get_module (ass
, get_process_module_class (), error
);
612 if (!mono_error_ok (error
)) {
613 mono_error_set_pending_exception (error
);
616 mono_array_setref_internal (temp_arr
, num_added
++, module
);
618 g_ptr_array_free (assemblies
, TRUE
);
621 if (count
== num_added
) {
624 /* shorter version of the array */
625 arr
= mono_array_new_checked (mono_domain_get (), get_process_module_class (), num_added
, error
);
626 if (mono_error_set_pending_exception (error
))
629 for (i
= 0; i
< num_added
; i
++)
630 mono_array_setref_internal (arr
, i
, mono_array_get_internal (temp_arr
, MonoObject
*, i
));
636 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
638 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
641 ves_icall_System_Diagnostics_Process_ProcessName_internal (HANDLE process
)
645 gunichar2 name
[MAX_PATH
];
651 ok
= mono_w32process_try_get_modules (process
, &mod
, sizeof(mod
), &needed
);
655 len
= mono_w32process_module_get_name (process
, mod
, name
, MAX_PATH
);
659 string
= mono_string_new_utf16_checked (mono_domain_get (), name
, len
, error
);
660 if (!mono_error_ok (error
))
661 mono_error_set_pending_exception (error
);
666 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
669 ves_icall_System_Diagnostics_Process_GetProcessData (int pid
, gint32 data_type
, gint32
*error
)
671 MonoProcessError perror
;
674 res
= mono_process_get_data_with_error (GINT_TO_POINTER (pid
), (MonoProcessData
)data_type
, &perror
);
681 mono_pin_string (MonoStringHandle in_coophandle
, MonoStringHandle
*out_coophandle
, gunichar2
**chars
, gsize
*length
, gchandle_t
*gchandle
)
683 *out_coophandle
= in_coophandle
;
684 if (!MONO_HANDLE_IS_NULL (in_coophandle
)) {
685 *chars
= mono_string_handle_pin_chars (in_coophandle
, gchandle
);
686 *length
= mono_string_handle_length (in_coophandle
);
691 mono_createprocess_coop_init (MonoCreateProcessCoop
*coop
, MonoW32ProcessStartInfoHandle proc_start_info
, MonoW32ProcessInfo
*process_info
)
693 memset (coop
, 0, sizeof (*coop
));
695 #define PIN_STRING(h, x) (mono_pin_string (h, &coop->coophandle.x, &coop->x, &coop->length.x, &coop->gchandle.x))
697 #define PIN(x) PIN_STRING (MONO_HANDLE_NEW_GET (MonoString, proc_start_info, x), x)
700 PIN (working_directory
);
703 #define PIN(x) PIN_STRING (MONO_HANDLE_NEW (MonoString, process_info->x), x)
710 mono_unpin_array (gchandle_t
*gchandles
, gsize count
)
712 for (gsize i
= 0; i
< count
; ++i
) {
713 mono_gchandle_free_internal (gchandles
[i
]);
719 mono_createprocess_coop_cleanup (MonoCreateProcessCoop
*coop
)
721 mono_unpin_array ((gchandle_t
*)&coop
->gchandle
, sizeof (coop
->gchandle
) / sizeof (gchandle_t
));
722 memset (coop
, 0, sizeof (*coop
));