X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Ffile-io.c;h=ff67e997f05b4b633775601e9450477a4091854f;hb=36d8e487f6074290bf2c2b8034e372f51339c1ea;hp=8cc723152696623fe9c01aeaf92f72d5f81cc6c6;hpb=a097b5471761180c4aae2dab224ed9caeeae3e86;p=mono.git diff --git a/mono/metadata/file-io.c b/mono/metadata/file-io.c index 8cc72315269..ff67e997f05 100644 --- a/mono/metadata/file-io.c +++ b/mono/metadata/file-io.c @@ -5,16 +5,18 @@ * Dick Porter (dick@ximian.com) * Gonzalo Paniagua Javier (gonzalo@ximian.com) * - * (C) 2001,2002,2003 Ximian, Inc. - * Copyright (c) 2004,2005,2006 Novell, Inc. (http://www.novell.com) + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) */ #include + #include #include #include -#include +#ifdef HAVE_UNISTD_H #include +#endif #ifdef HAVE_SYS_STAT_H #include #endif @@ -93,28 +95,25 @@ static guint32 convert_access(MonoFileAccess mono_access) static guint32 convert_share(MonoFileShare mono_share) { - guint32 share; + guint32 share = 0; - switch(mono_share) { - case FileShare_None: - share=0; - break; - case FileShare_Read: - share=FILE_SHARE_READ; - break; - case FileShare_Write: - share=FILE_SHARE_WRITE; - break; - case FileShare_ReadWrite: - share=FILE_SHARE_READ|FILE_SHARE_WRITE; - break; - default: + if (mono_share & FileShare_Read) { + share |= FILE_SHARE_READ; + } + if (mono_share & FileShare_Write) { + share |= FILE_SHARE_WRITE; + } + if (mono_share & FileShare_Delete) { + share |= FILE_SHARE_DELETE; + } + + if (mono_share & ~(FileShare_Read|FileShare_Write|FileShare_Delete)) { g_warning("System.IO.FileShare has unknown value 0x%x", mono_share); /* Safe fallback */ share=0; } - + return(share); } @@ -188,7 +187,7 @@ static void convert_win32_file_attribute_data (const WIN32_FILE_ATTRIBUTE_DATA * while (name [len]) ++ len; - stat->name = mono_string_new_utf16 (mono_domain_get (), name, len); + MONO_STRUCT_SETREF (stat, name, mono_string_new_utf16 (mono_domain_get (), name, len)); } /* Managed file attributes have nearly but not quite the same values @@ -203,6 +202,73 @@ static guint32 convert_attrs(MonoFileAttributes attrs) return(attrs); } +/* + * On Win32, GetFileAttributes|Ex () seems to try opening the file, + * which might lead to sharing violation errors, whereas FindFirstFile + * always succeeds. These 2 wrappers resort to FindFirstFile if + * GetFileAttributes|Ex () has failed. + */ +static guint32 +get_file_attributes (const gunichar2 *path) +{ + guint32 res; + WIN32_FIND_DATA find_data; + HANDLE find_handle; + gint32 error; + + res = GetFileAttributes (path); + if (res != -1) + return res; + + error = GetLastError (); + + if (error != ERROR_SHARING_VIOLATION) + return res; + + find_handle = FindFirstFile (path, &find_data); + + if (find_handle == INVALID_HANDLE_VALUE) + return res; + + FindClose (find_handle); + + return find_data.dwFileAttributes; +} + +static gboolean +get_file_attributes_ex (const gunichar2 *path, WIN32_FILE_ATTRIBUTE_DATA *data) +{ + gboolean res; + WIN32_FIND_DATA find_data; + HANDLE find_handle; + gint32 error; + + res = GetFileAttributesEx (path, GetFileExInfoStandard, data); + if (res) + return TRUE; + + error = GetLastError (); + + if (error != ERROR_SHARING_VIOLATION) + return FALSE; + + find_handle = FindFirstFile (path, &find_data); + + if (find_handle == INVALID_HANDLE_VALUE) + return FALSE; + + FindClose (find_handle); + + data->dwFileAttributes = find_data.dwFileAttributes; + data->ftCreationTime = find_data.ftCreationTime; + data->ftLastAccessTime = find_data.ftLastAccessTime; + data->ftLastWriteTime = find_data.ftLastWriteTime; + data->nFileSizeHigh = find_data.nFileSizeHigh; + data->nFileSizeLow = find_data.nFileSizeLow; + + return TRUE; +} + /* System.IO.MonoIO internal calls */ MonoBoolean @@ -239,291 +305,219 @@ ves_icall_System_IO_MonoIO_RemoveDirectory (MonoString *path, gint32 *error) return(ret); } -static gint -get_error_from_g_file_error (gint error) -{ - switch (error) { - case G_FILE_ERROR_ACCES: - error = ERROR_ACCESS_DENIED; - break; - case G_FILE_ERROR_NAMETOOLONG: - error = ERROR_FILENAME_EXCED_RANGE; - break; - case G_FILE_ERROR_NOENT: - error = ERROR_FILE_NOT_FOUND; - break; - case G_FILE_ERROR_NOTDIR: - error = ERROR_FILE_NOT_FOUND; - break; - case G_FILE_ERROR_ROFS: - error = ERROR_ACCESS_DENIED; - break; - case G_FILE_ERROR_TXTBSY: - error = ERROR_SHARING_VIOLATION; - break; - case G_FILE_ERROR_NOSPC: - error = ERROR_HANDLE_DISK_FULL; - break; - case G_FILE_ERROR_NFILE: - case G_FILE_ERROR_MFILE: - error = ERROR_TOO_MANY_OPEN_FILES; - break; - case G_FILE_ERROR_BADF: - error = ERROR_INVALID_HANDLE; - break; - case G_FILE_ERROR_INVAL: - error = ERROR_INVALID_PARAMETER; - break; - case G_FILE_ERROR_AGAIN: - error = ERROR_SHARING_VIOLATION; - break; - case G_FILE_ERROR_INTR: - error = ERROR_IO_PENDING; - break; - case G_FILE_ERROR_PERM: - error = ERROR_ACCESS_DENIED; - break; - case G_FILE_ERROR_FAILED: - error = ERROR_INVALID_PARAMETER; - break; - case G_FILE_ERROR_NXIO: - case G_FILE_ERROR_NOMEM: - case G_FILE_ERROR_NODEV: - case G_FILE_ERROR_FAULT: - case G_FILE_ERROR_LOOP: - case G_FILE_ERROR_PIPE: - case G_FILE_ERROR_IO: - default: - error = ERROR_GEN_FAILURE; - break; - - } - - return error; -} - -static gint -file_compare (gconstpointer a, gconstpointer b) -{ - gchar *astr = *(gchar **) a; - gchar *bstr = *(gchar **) b; - - return strcmp (astr, bstr); -} - -static gint -get_file_attributes (const char *filename) +MonoArray * +ves_icall_System_IO_MonoIO_GetFileSystemEntries (MonoString *path, + MonoString *path_with_pattern, + gint attrs, gint mask, + gint32 *error) { -#ifdef PLATFORM_WIN32 - gunichar2 *full16; - gint result; + MonoDomain *domain; + MonoArray *result; + int i; + WIN32_FIND_DATA data; + HANDLE find_handle; + GPtrArray *names; + gchar *utf8_path, *utf8_result, *full_name; + + MONO_ARCH_SAVE_REGS; - full16 = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL); - if (full16 == NULL) { - g_message ("Bad encoding for '%s'\n", filename); - return FALSE; - } + *error = ERROR_SUCCESS; - result = GetFileAttributes (full16); - g_free (full16); - return result; -#else - struct stat buf; - struct stat linkbuf; - int result; - int file_attrs; - gboolean issymlink = FALSE; - - result = lstat (filename, &buf); - if (result == -1) - return FALSE; + domain = mono_domain_get (); + mask = convert_attrs (mask); + + find_handle = FindFirstFile (mono_string_chars (path_with_pattern), + &data); + if (find_handle == INVALID_HANDLE_VALUE) { + gint32 find_error = GetLastError (); + + if (find_error == ERROR_FILE_NOT_FOUND) { + /* No files, so just return an empty array */ + result = mono_array_new (domain, + mono_defaults.string_class, + 0); - if (S_ISLNK (buf.st_mode)) { - issymlink = TRUE; - result = stat (filename, &linkbuf); - if (result != -1) { - buf = linkbuf; + return(result); } + + *error = find_error; + return(NULL); } - /* Sockets (0140000) != Directory (040000) + Regular file (0100000) */ - if (S_ISSOCK (buf.st_mode)) - buf.st_mode &= ~S_IFSOCK; /* don't consider socket protection */ - - file_attrs = 0; - if (S_ISDIR (buf.st_mode)) - file_attrs |= FILE_ATTRIBUTE_DIRECTORY; - else - file_attrs |= FILE_ATTRIBUTE_ARCHIVE; + utf8_path = mono_string_to_utf8 (path); /*If this raises there is not memory to release*/ + names = g_ptr_array_new (); - if ((buf.st_mode & S_IWUSR) == 0) - file_attrs |= FILE_ATTRIBUTE_READONLY; + do { + if ((data.cFileName[0] == '.' && data.cFileName[1] == 0) || + (data.cFileName[0] == '.' && data.cFileName[1] == '.' && data.cFileName[2] == 0)) { + continue; + } + + if ((data.dwFileAttributes & mask) == attrs) { + utf8_result = g_utf16_to_utf8 (data.cFileName, -1, NULL, NULL, NULL); + if (utf8_result == NULL) { + continue; + } + + full_name = g_build_filename (utf8_path, utf8_result, NULL); + g_ptr_array_add (names, full_name); + + g_free (utf8_result); + } + } while(FindNextFile (find_handle, &data)); - if (*filename == '.') - file_attrs |= FILE_ATTRIBUTE_HIDDEN; + if (FindClose (find_handle) == FALSE) { + *error = GetLastError (); + result = NULL; + } else { + result = mono_array_new (domain, mono_defaults.string_class, names->len); + for (i = 0; i < names->len; i++) { + mono_array_setref (result, i, mono_string_new (domain, g_ptr_array_index (names, i))); + } + } - if (issymlink) { - file_attrs |= FILE_ATTRIBUTE_REPARSE_POINT; + for (i = 0; i < names->len; i++) { + g_free (g_ptr_array_index (names, i)); } + g_ptr_array_free (names, TRUE); + g_free (utf8_path); - return file_attrs; -#endif + return result; } +typedef struct { + MonoDomain *domain; + gchar *utf8_path; + HANDLE find_handle; +} IncrementalFind; + static gboolean -test_file (const char *filename, int attrs, int mask) +incremental_find_check_match (IncrementalFind *handle, WIN32_FIND_DATA *data, MonoString **result) { - int file_attr; - - file_attr = get_file_attributes (filename); - if (file_attr == FALSE) + gchar *utf8_result; + gchar *full_name; + + if ((data->cFileName[0] == '.' && data->cFileName[1] == 0) || (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0)) return FALSE; - return ((file_attr & mask) == attrs); + utf8_result = g_utf16_to_utf8 (data->cFileName, -1, NULL, NULL, NULL); + if (utf8_result == NULL) + return FALSE; + + full_name = g_build_filename (handle->utf8_path, utf8_result, NULL); + g_free (utf8_result); + *result = mono_string_new (mono_domain_get (), full_name); + g_free (full_name); + + return TRUE; } -/* scandir using glib */ -static gint -mono_io_scandir (const gchar *dirname, const gchar *pattern, int attrs, - int mask, gchar ***namelist) +MonoString * +ves_icall_System_IO_MonoIO_FindFirst (MonoString *path, + MonoString *path_with_pattern, + gint32 *result_attr, gint32 *error, + gpointer *handle) { - GError *error = NULL; - GDir *dir; - GPtrArray *names; - const gchar *name; - gint result; - GPatternSpec *patspec; - gchar *full_name; - - mask = convert_attrs (mask); - *namelist = NULL; - dir = g_dir_open (dirname, 0, &error); - if (dir == NULL) { - /* g_dir_open returns ENOENT on directories on which we don't - * have read/x permission */ - gint errnum = get_error_from_g_file_error (error->code); - g_error_free (error); - if (errnum == ERROR_FILE_NOT_FOUND && g_file_test (dirname, G_FILE_TEST_IS_DIR)) - errnum = ERROR_ACCESS_DENIED; - - SetLastError (errnum); - return -1; + WIN32_FIND_DATA data; + HANDLE find_handle; + IncrementalFind *ifh; + MonoString *result; + + *error = ERROR_SUCCESS; + + find_handle = FindFirstFile (mono_string_chars (path_with_pattern), &data); + + if (find_handle == INVALID_HANDLE_VALUE) { + gint32 find_error = GetLastError (); + *handle = NULL; + + if (find_error == ERROR_FILE_NOT_FOUND) + return NULL; + + *error = find_error; + return NULL; } - patspec = g_pattern_spec_new (pattern); - names = g_ptr_array_new (); - while ((name = g_dir_read_name (dir)) != NULL) { - if (!g_pattern_match_string (patspec, name)) - continue; - - full_name = g_build_filename (dirname, name, NULL); - if (FALSE == test_file (full_name, attrs, mask)) { - g_free (full_name); - continue; + ifh = g_new (IncrementalFind, 1); + ifh->find_handle = find_handle; + ifh->utf8_path = mono_string_to_utf8 (path); + ifh->domain = mono_domain_get (); + *handle = ifh; + + while (incremental_find_check_match (ifh, &data, &result) == 0){ + if (FindNextFile (find_handle, &data) == FALSE){ + int e = GetLastError (); + if (e != ERROR_NO_MORE_FILES) + *error = e; + return NULL; } - - g_ptr_array_add (names, full_name); } + *result_attr = data.dwFileAttributes; - g_pattern_spec_free (patspec); - g_dir_close (dir); - result = names->len; - if (result > 0) { - g_ptr_array_sort (names, file_compare); - g_ptr_array_set_size (names, result + 1); - - *namelist = (gchar **) g_ptr_array_free (names, FALSE); - } else { - g_ptr_array_free (names, TRUE); - } - return result; } -MonoArray * -ves_icall_System_IO_MonoIO_GetFileSystemEntries (MonoString *_path, MonoString *_pattern, - gint attrs, gint mask, gint32 *error) +MonoString * +ves_icall_System_IO_MonoIO_FindNext (gpointer handle, gint32 *result_attr, gint32 *error) { - MonoDomain *domain; - MonoArray *result; - gchar **namelist; - gchar *path; - gchar *pattern; - int i, nnames; - int removed; - MonoString *str_name; -#ifndef PLATFORM_WIN32 - gunichar2 *utf16; - gsize nbytes; -#endif - - MONO_ARCH_SAVE_REGS; - - *error = ERROR_SUCCESS; - - path = mono_string_to_utf8 (_path); - pattern = mono_string_to_utf8 (_pattern); - nnames = mono_io_scandir (path, pattern, attrs, mask, &namelist); - if (nnames < 0) { - *error = GetLastError (); - g_free (pattern); - g_free (path); - return NULL; - } - - domain = mono_domain_get (); - result = mono_array_new (domain, mono_defaults.string_class, nnames); - removed = 0; - for (i = 0; i < nnames; i++) { -#if PLATFORM_WIN32 - str_name = mono_string_new (domain, namelist [i]); -#else - utf16 = mono_unicode_from_external (namelist [i], &nbytes); - if (utf16 == NULL) { - g_message ("Bad encoding for '%s'\nConsider using MONO_EXTERNAL_ENCODINGS\n", - namelist [i]); - removed++; - continue; - } - str_name = mono_string_from_utf16 (utf16); - g_free (utf16); -#endif - mono_array_setref (result, i - removed, str_name); - } + IncrementalFind *ifh = handle; + WIN32_FIND_DATA data; + MonoString *result; - if (removed > 0) { - MonoArray *shrinked; - shrinked = mono_array_new (domain, mono_defaults.string_class, nnames - removed); - for (i = 0; i < (nnames - removed); i++) { - MonoString *str; - str = mono_array_get (result, MonoString *, i); - mono_array_setref (shrinked, i, str); + error = ERROR_SUCCESS; + do { + if (FindNextFile (ifh->find_handle, &data) == FALSE){ + int e = GetLastError (); + if (e != ERROR_NO_MORE_FILES) + *error = e; + return NULL; } - result = shrinked; - } + } while (incremental_find_check_match (ifh, &data, &result) == 0); - g_strfreev (namelist); - g_free (pattern); - g_free (path); + *result_attr = data.dwFileAttributes; return result; } +int +ves_icall_System_IO_MonoIO_FindClose (gpointer handle) +{ + IncrementalFind *ifh = handle; + gint32 error; + + if (FindClose (ifh->find_handle) == FALSE){ + error = GetLastError (); + } else + error = ERROR_SUCCESS; + g_free (ifh->utf8_path); + g_free (ifh); + + return error; +} + MonoString * ves_icall_System_IO_MonoIO_GetCurrentDirectory (gint32 *error) { MonoString *result; gunichar2 *buf; - int len; + int len, res_len; MONO_ARCH_SAVE_REGS; - len = MAX_PATH + 1; + len = MAX_PATH + 1; /*FIXME this is too smal under most unix systems.*/ buf = g_new (gunichar2, len); *error=ERROR_SUCCESS; result = NULL; - if (GetCurrentDirectory (len, buf) > 0) { + res_len = GetCurrentDirectory (len, buf); + if (res_len > len) { /*buf is too small.*/ + int old_res_len = res_len; + g_free (buf); + buf = g_new (gunichar2, res_len); + res_len = GetCurrentDirectory (res_len, buf) == old_res_len; + } + + if (res_len) { len = 0; while (buf [len]) ++ len; @@ -573,6 +567,36 @@ ves_icall_System_IO_MonoIO_MoveFile (MonoString *path, MonoString *dest, return(ret); } +MonoBoolean +ves_icall_System_IO_MonoIO_ReplaceFile (MonoString *sourceFileName, MonoString *destinationFileName, + MonoString *destinationBackupFileName, MonoBoolean ignoreMetadataErrors, + gint32 *error) +{ + gboolean ret; + gunichar2 *utf16_sourceFileName = NULL, *utf16_destinationFileName = NULL, *utf16_destinationBackupFileName = NULL; + guint32 replaceFlags = REPLACEFILE_WRITE_THROUGH; + + MONO_ARCH_SAVE_REGS; + + if (sourceFileName) + utf16_sourceFileName = mono_string_chars (sourceFileName); + if (destinationFileName) + utf16_destinationFileName = mono_string_chars (destinationFileName); + if (destinationBackupFileName) + utf16_destinationBackupFileName = mono_string_chars (destinationBackupFileName); + + *error = ERROR_SUCCESS; + if (ignoreMetadataErrors) + replaceFlags |= REPLACEFILE_IGNORE_MERGE_ERRORS; + + ret = ReplaceFile (utf16_destinationFileName, utf16_sourceFileName, utf16_destinationBackupFileName, + replaceFlags, NULL, NULL); + if (ret == FALSE) + *error = GetLastError (); + + return ret; +} + MonoBoolean ves_icall_System_IO_MonoIO_CopyFile (MonoString *path, MonoString *dest, MonoBoolean overwrite, gint32 *error) @@ -617,7 +641,7 @@ ves_icall_System_IO_MonoIO_GetFileAttributes (MonoString *path, gint32 *error) *error=ERROR_SUCCESS; - ret=GetFileAttributes (mono_string_chars (path)); + ret=get_file_attributes (mono_string_chars (path)); /* * The definition of INVALID_FILE_ATTRIBUTES in the cygwin win32 @@ -683,7 +707,7 @@ ves_icall_System_IO_MonoIO_GetFileStat (MonoString *path, MonoIOStat *stat, *error=ERROR_SUCCESS; - result = GetFileAttributesEx (mono_string_chars (path), GetFileExInfoStandard, &data); + result = get_file_attributes_ex (mono_string_chars (path), &data); if (result) { convert_win32_file_attribute_data (&data, @@ -721,6 +745,9 @@ ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode, if (options & FileOptions_RandomAccess) attributes |= FILE_FLAG_RANDOM_ACCESS; + if (options & FileOptions_Temporary) + attributes |= FILE_ATTRIBUTE_TEMPORARY; + /* Not sure if we should set FILE_FLAG_OVERLAPPED, how does this mix with the "Async" bool here? */ if (options & FileOptions_Asynchronous) attributes |= FILE_FLAG_OVERLAPPED; @@ -732,7 +759,7 @@ ves_icall_System_IO_MonoIO_Open (MonoString *filename, gint32 mode, /* If we're opening a directory we need to set the extra flag */ - attrs = GetFileAttributes (chars); + attrs = get_file_attributes (chars); if (attrs != INVALID_FILE_ATTRIBUTES) { if (attrs & FILE_ATTRIBUTE_DIRECTORY) { attributes |= FILE_FLAG_BACKUP_SEMANTICS; @@ -831,7 +858,7 @@ ves_icall_System_IO_MonoIO_Seek (HANDLE handle, gint64 offset, gint32 origin, *error=ERROR_SUCCESS; offset_hi = offset >> 32; - offset = SetFilePointer (handle, offset & 0xFFFFFFFF, &offset_hi, + offset = SetFilePointer (handle, (gint32) (offset & 0xFFFFFFFF), &offset_hi, convert_seekorigin (origin)); if(offset==INVALID_SET_FILE_POINTER) { @@ -1009,10 +1036,28 @@ ves_icall_System_IO_MonoIO_CreatePipe (HANDLE *read_handle, return(TRUE); } +MonoBoolean ves_icall_System_IO_MonoIO_DuplicateHandle (HANDLE source_process_handle, + HANDLE source_handle, HANDLE target_process_handle, HANDLE *target_handle, + gint32 access, gint32 inherit, gint32 options) +{ + /* This is only used on Windows */ + gboolean ret; + + MONO_ARCH_SAVE_REGS; + + ret=DuplicateHandle (source_process_handle, source_handle, target_process_handle, target_handle, access, inherit, options); + if(ret==FALSE) { + /* FIXME: throw an exception? */ + return(FALSE); + } + + return(TRUE); +} + gunichar2 ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar () { -#if defined (PLATFORM_WIN32) +#if defined (TARGET_WIN32) return (gunichar2) ':'; /* colon */ #else return (gunichar2) '/'; /* forward slash */ @@ -1022,7 +1067,7 @@ ves_icall_System_IO_MonoIO_get_VolumeSeparatorChar () gunichar2 ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar () { -#if defined (PLATFORM_WIN32) +#if defined (TARGET_WIN32) return (gunichar2) '\\'; /* backslash */ #else return (gunichar2) '/'; /* forward slash */ @@ -1032,7 +1077,7 @@ ves_icall_System_IO_MonoIO_get_DirectorySeparatorChar () gunichar2 ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar () { -#if defined (PLATFORM_WIN32) +#if defined (TARGET_WIN32) return (gunichar2) '/'; /* forward slash */ #else return (gunichar2) '/'; /* slash, same as DirectorySeparatorChar */ @@ -1042,7 +1087,7 @@ ves_icall_System_IO_MonoIO_get_AltDirectorySeparatorChar () gunichar2 ves_icall_System_IO_MonoIO_get_PathSeparator () { -#if defined (PLATFORM_WIN32) +#if defined (TARGET_WIN32) return (gunichar2) ';'; /* semicolon */ #else return (gunichar2) ':'; /* colon */ @@ -1051,7 +1096,7 @@ ves_icall_System_IO_MonoIO_get_PathSeparator () static const gunichar2 invalid_path_chars [] = { -#if defined (PLATFORM_WIN32) +#if defined (TARGET_WIN32) 0x0022, /* double quote, which seems allowed in MS.NET but should be rejected */ 0x003c, /* less than */ 0x003e, /* greater than */ @@ -1107,12 +1152,11 @@ ves_icall_System_IO_MonoIO_GetTempPath (MonoString **mono_name) if(ret>0) { #ifdef DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": Temp path is [%s] (len %d)", name, ret); + g_message ("%s: Temp path is [%s] (len %d)", __func__, name, ret); #endif - *mono_name=mono_string_new_utf16 (mono_domain_get (), name, - ret); + mono_gc_wbarrier_generic_store ((gpointer) mono_name, + (MonoObject*) mono_string_new_utf16 (mono_domain_get (), name, ret)); } g_free (name);