+2007-11-15 Dick Porter <dick@ximian.com>
+
+ * processes.c: Implement process module support by reading from
+ /proc if it's available.
+
+ * versioninfo.h:
+ * versioninfo.c: New functions to implement PE file version info
+ support.
+
2007-11-08 Dick Porter <dick@ximian.com>
* critical-sections.h (EnterCriticalSection): Disable the critical
timefuncs-private.h \
types.h \
uglify.h \
+ versioninfo.c \
versioninfo.h \
wait.c \
wait.h \
#include <config.h>
#include <glib.h>
+#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <sched.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <fcntl.h>
+#include <sys/param.h>
/* sys/resource.h (for rusage) is required when using osx 10.3 (but not 10.4) */
#ifdef __APPLE__
return(TRUE);
}
+typedef struct
+{
+ gpointer address_start;
+ gpointer address_end;
+ gchar *perms;
+ gpointer address_offset;
+ dev_t device;
+ ino_t inode;
+ gchar *filename;
+} WapiProcModule;
+
+static void free_procmodule (WapiProcModule *mod)
+{
+ if (mod->perms != NULL) {
+ g_free (mod->perms);
+ }
+ if (mod->filename != NULL) {
+ g_free (mod->filename);
+ }
+ g_free (mod);
+}
+
+static GSList *load_modules (FILE *fp)
+{
+ gchar buf[MAXPATHLEN + 1], **fields, **addresses;
+ GSList *ret = NULL;
+ WapiProcModule *mod;
+ char *ep;
+ long address_start, address_end, address_offset, maj, min;
+ dev_t device;
+ ino_t inode;
+
+ while (fgets (buf, sizeof(buf), fp)) {
+ /* Have to specify 7 fields here, as otherwise
+ * g_strsplit_set gives us variable numbers of empty
+ * fields when the input buffer contains contiguous
+ * spaces before the filename. Unuseful.
+ */
+ fields = g_strsplit_set (buf, " \t:", 7);
+ if (g_strv_length (fields) < 7) {
+ g_strfreev (fields);
+ continue;
+ }
+
+ g_strstrip (fields[6]);
+ if ((strlen (fields[0]) == 0) ||
+ (strlen (fields[2]) == 0) ||
+ (strlen (fields[3]) == 0) ||
+ (strlen (fields[4]) == 0) ||
+ (strlen (fields[5]) == 0) ||
+ (strlen (fields[6]) == 0)) {
+ g_strfreev (fields);
+ continue;
+ }
+
+ ep = NULL;
+ address_offset = strtol (fields[2], &ep, 16);
+ if ((address_offset == LONG_MIN) ||
+ (address_offset == LONG_MAX) ||
+ (ep == NULL) ||
+ (*ep != '\0')) {
+ g_strfreev (fields);
+ continue;
+ }
+
+ ep = NULL;
+ maj = strtol (fields[3], &ep, 16);
+ if ((maj == LONG_MIN) ||
+ (maj == LONG_MAX) ||
+ (ep == NULL) ||
+ (*ep != '\0')) {
+ g_strfreev (fields);
+ continue;
+ }
+
+ ep = NULL;
+ min = strtol (fields[4], &ep, 16);
+ if ((min == LONG_MIN) ||
+ (min == LONG_MAX) ||
+ (ep == NULL) ||
+ (*ep != '\0')) {
+ g_strfreev (fields);
+ continue;
+ }
+
+ inode = (ino_t)atoi (fields[5]);
+ device = makedev ((int)maj, (int)min);
+
+ if ((device == 0) &&
+ (inode == 0)) {
+ g_strfreev (fields);
+ continue;
+ }
+
+ addresses = g_strsplit (fields[0], "-", -1);
+ if (g_strv_length (addresses) != 2) {
+ g_strfreev (addresses);
+ g_strfreev (fields);
+ continue;
+ }
+
+ ep = NULL;
+ address_start = strtol (addresses[0], &ep, 16);
+ if ((address_start == LONG_MIN) ||
+ (address_start == LONG_MAX) ||
+ (ep == NULL) ||
+ (*ep != '\0')) {
+ g_strfreev (addresses);
+ g_strfreev (fields);
+ continue;
+ }
+
+ ep = NULL;
+ address_end = strtol (addresses[1], &ep, 16);
+ if ((address_end == LONG_MIN) ||
+ (address_end == LONG_MAX) ||
+ (ep == NULL) ||
+ (*ep != '\0')) {
+ g_strfreev (addresses);
+ g_strfreev (fields);
+ continue;
+ }
+
+
+ mod = g_new0 (WapiProcModule, 1);
+ mod->address_start = GUINT_TO_POINTER (address_start);
+ mod->address_end = GUINT_TO_POINTER (address_end);
+ mod->perms = NULL;
+ mod->address_offset = GUINT_TO_POINTER (address_offset);
+ mod->device = device;
+ mod->inode = inode;
+ mod->filename = g_strdup (fields[6]);
+
+ ret = g_slist_prepend (ret, mod);
+
+ g_strfreev (addresses);
+ g_strfreev (fields);
+ }
+
+ ret = g_slist_reverse (ret);
+
+ return(ret);
+}
+
gboolean EnumProcessModules (gpointer process, gpointer *modules,
guint32 size, guint32 *needed)
{
+ struct _WapiHandle_process *process_handle;
+ gboolean ok;
+ gchar *filename;
+ FILE *fp;
+ GSList *mods = NULL;
+ WapiProcModule *module;
+ guint32 count, avail = size / sizeof(gpointer);
+ int i;
+
/* Store modules in an array of pointers (main module as
* modules[0]), using the load address for each module as a
* token. (Use 'NULL' as an alternative for the main module
* other systems will have to implement /dev/kmem reading or
* whatever other horrid technique is needed.
*/
- if(size<sizeof(gpointer)) {
+ if (size < sizeof(gpointer)) {
return(FALSE);
}
-
-#ifdef linux
- modules[0]=NULL;
- *needed=sizeof(gpointer);
-#else
- modules[0]=NULL;
- *needed=sizeof(gpointer);
+
+ ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+ (gpointer *)&process_handle);
+ if (ok == FALSE) {
+#ifdef DEBUG
+ g_message ("%s: Can't find process %p", __func__, process);
#endif
+
+ return(FALSE);
+ }
+
+ filename = g_strdup_printf ("/proc/%d/maps", process_handle->id);
+ if ((fp = fopen (filename, "r")) == NULL) {
+ /* No /proc/<pid>/maps so just return the main module
+ * shortcut for now
+ */
+ modules[0] = NULL;
+ *needed = sizeof(gpointer);
+ } else {
+ mods = load_modules (fp);
+ count = g_slist_length (mods);
+
+ *needed = sizeof(gpointer) * count;
+
+ /* Use the NULL shortcut, as the first line in
+ * /proc/<pid>/maps isn't the executable, and we need
+ * that first in the returned list
+ */
+ modules[0] = NULL;
+ for (i = 1; i < avail && i < count; i++) {
+ module = (WapiProcModule *)g_slist_nth_data (mods, i);
+ modules[i] = module->address_start;
+ }
+
+ for (i = 0; i < count; i++) {
+ free_procmodule (g_slist_nth_data (mods, i));
+ }
+ g_slist_free (mods);
+ }
+
+ fclose (fp);
+ g_free (filename);
return(TRUE);
}
-guint32 GetModuleBaseName (gpointer process, gpointer module,
- gunichar2 *basename, guint32 size)
+static guint32 get_module_name (gpointer process, gpointer module,
+ gunichar2 *basename, guint32 size,
+ gboolean base)
{
struct _WapiHandle_process *process_handle;
gboolean ok;
+ pid_t pid;
+ gunichar2 *procname;
+ gchar *procname_ext = NULL;
+ glong len;
+ gsize bytes;
+ gchar *filename;
+ FILE *fp;
+ GSList *mods = NULL;
+ WapiProcModule *found_module;
+ guint32 count;
+ int i;
mono_once (&process_current_once, process_set_current);
__func__, process, module);
#endif
- if(basename==NULL || size==0) {
- return(FALSE);
+ if (basename == NULL || size == 0) {
+ return(0);
}
- ok=_wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
- (gpointer *)&process_handle);
- if(ok==FALSE) {
+ ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+ (gpointer *)&process_handle);
+ if (ok == FALSE) {
#ifdef DEBUG
g_message ("%s: Can't find process %p", __func__, process);
#endif
- return(FALSE);
+ return(0);
}
+ pid = process_handle->id;
- if(module==NULL) {
+ if (module == NULL) {
/* Shorthand for the main module, which has the
* process name recorded in the handle data
*/
- pid_t pid;
- gunichar2 *procname;
- gchar *procname_utf8 = NULL;
- glong len, bytes;
#ifdef DEBUG
g_message ("%s: Returning main module name", __func__);
#endif
- pid=process_handle->id;
- procname_utf8 = process_handle->proc_name;
-
+ if (base) {
+ procname_ext = g_path_get_basename (process_handle->proc_name);
+ } else {
+ procname_ext = g_strdup (process_handle->proc_name);
+ }
+ } else {
+ /* Look up the address in /proc/<pid>/maps */
+ filename = g_strdup_printf ("/proc/%d/maps", pid);
+ if ((fp = fopen (filename, "r")) == NULL) {
+ /* No /proc/<pid>/maps, so just return failure
+ * for now
+ */
+ g_free (filename);
+ return(0);
+ } else {
+ mods = load_modules (fp);
+ fclose (fp);
+ count = g_slist_length (mods);
+
+ for (i = 0; i < count; i++) {
+ found_module = (WapiProcModule *)g_slist_nth_data (mods, i);
+ if (procname_ext == NULL &&
+ found_module->address_start == module) {
+ if (base) {
+ procname_ext = g_path_get_basename (found_module->filename);
+ } else {
+ procname_ext = g_strdup (found_module->filename);
+ }
+ }
+
+ free_procmodule (found_module);
+ }
+
+ g_slist_free (mods);
+ g_free (filename);
+ }
+ }
+
+ if (procname_ext != NULL) {
#ifdef DEBUG
g_message ("%s: Process name is [%s]", __func__,
- procname_utf8);
+ procname_ext);
#endif
- procname = g_utf8_to_utf16 (procname_utf8, -1, NULL, &len,
- NULL);
+ procname = mono_unicode_from_external (procname_ext, &bytes);
if (procname == NULL) {
/* bugger */
+ g_free (procname_ext);
return(0);
}
-
- /* Add the terminator, and convert chars to bytes */
- bytes = (len + 1) * 2;
+
+ len = (bytes / 2);
+
+ /* Add the terminator */
+ bytes += 2;
if (size < bytes) {
#ifdef DEBUG
}
g_free (procname);
-
+ g_free (procname_ext);
+
return(len);
+ }
+
+ return(0);
+}
+
+guint32 GetModuleBaseName (gpointer process, gpointer module,
+ gunichar2 *basename, guint32 size)
+{
+ return(get_module_name (process, module, basename, size, TRUE));
+}
+
+guint32 GetModuleFileNameEx (gpointer process, gpointer module,
+ gunichar2 *filename, guint32 size)
+{
+ return(get_module_name (process, module, filename, size, FALSE));
+}
+
+gboolean GetModuleInformation (gpointer process, gpointer module,
+ WapiModuleInfo *modinfo, guint32 size)
+{
+ struct _WapiHandle_process *process_handle;
+ gboolean ok;
+ pid_t pid;
+ gchar *filename;
+ FILE *fp;
+ GSList *mods = NULL;
+ WapiProcModule *found_module;
+ guint32 count;
+ int i;
+ gboolean ret = FALSE;
+
+ mono_once (&process_current_once, process_set_current);
+
+#ifdef DEBUG
+ g_message ("%s: Getting module info, process handle %p module %p",
+ __func__, process, module);
+#endif
+
+ if (modinfo == NULL || size < sizeof(WapiModuleInfo)) {
+ return(FALSE);
+ }
+
+ ok = _wapi_lookup_handle (process, WAPI_HANDLE_PROCESS,
+ (gpointer *)&process_handle);
+ if (ok == FALSE) {
+#ifdef DEBUG
+ g_message ("%s: Can't find process %p", __func__, process);
+#endif
+
+ return(FALSE);
+ }
+ pid = process_handle->id;
+
+ if (module == NULL) {
+ /* Shorthand for the main module, which has the
+ * process name recorded in the handle data
+ *
+ * FIXME: try and dig through the /proc/<pid>/maps
+ * list matching filename?
+ */
+ return(FALSE);
} else {
/* Look up the address in /proc/<pid>/maps */
+ filename = g_strdup_printf ("/proc/%d/maps", pid);
+ if ((fp = fopen (filename, "r")) == NULL) {
+ /* No /proc/<pid>/maps, so just return failure
+ * for now
+ */
+ g_free (filename);
+ return(FALSE);
+ } else {
+ mods = load_modules (fp);
+ fclose (fp);
+ count = g_slist_length (mods);
+
+ for (i = 0; i < count; i++) {
+ found_module = (WapiProcModule *)g_slist_nth_data (mods, i);
+ if (found_module->address_start == module) {
+ modinfo->lpBaseOfDll = found_module->address_start;
+ modinfo->SizeOfImage = GPOINTER_TO_UINT(found_module->address_end) - GPOINTER_TO_UINT (found_module->address_start);
+ modinfo->EntryPoint = found_module->address_offset;
+ ret = TRUE;
+ }
+
+ free_procmodule (found_module);
+ }
+
+ g_slist_free (mods);
+ g_free (filename);
+ }
}
- return(0);
+ return(ret);
}
gboolean GetProcessWorkingSetSize (gpointer process, size_t *min, size_t *max)
#include <mono/io-layer/handles.h>
#include <mono/io-layer/access.h>
+#include <mono/io-layer/versioninfo.h>
G_BEGIN_DECLS
guint32 size, guint32 *needed);
extern guint32 GetModuleBaseName (gpointer process, gpointer module,
gunichar2 *basename, guint32 size);
+extern guint32 GetModuleFileNameEx (gpointer process, gpointer module,
+ gunichar2 *filename, guint32 size);
+extern gboolean GetModuleInformation (gpointer process, gpointer module,
+ WapiModuleInfo *modinfo, guint32 size);
extern gboolean GetProcessWorkingSetSize (gpointer process, size_t *min,
size_t *max);
extern gboolean SetProcessWorkingSetSize (gpointer process, size_t min,
typedef WapiStartupInfo *LPSTARTUPINFO;
typedef WapiProcessInformation PROCESS_INFORMATION;
typedef WapiFixedFileInfo VS_FIXEDFILEINFO;
+typedef WapiModuleInfo MODULEINFO;
+typedef WapiModuleInfo *LPMODULEINFO;
+typedef WapiImageDosHeader IMAGE_DOS_HEADER;
+typedef WapiImageDosHeader *PIMAGE_DOS_HEADER;
+typedef WapiImageFileHeader IMAGE_FILE_HEADER;
+typedef WapiImageFileHeader *PIMAGE_FILE_HEADER;
+typedef WapiImageDataDirectory IMAGE_DATA_DIRECTORY;
+typedef WapiImageDataDirectory *PIMAGE_DATA_DIRECTORY;
+typedef WapiImageOptionalHeader32 IMAGE_OPTIONAL_HEADER32;
+typedef WapiImageOptionalHeader32 *PIMAGE_OPTIONAL_HEADER32;
+typedef WapiImageOptionalHeader64 IMAGE_OPTIONAL_HEADER64;
+typedef WapiImageOptionalHeader64 *PIMAGE_OPTIONAL_HEADER64;
+typedef WapiImageOptionalHeader IMAGE_OPTIONAL_HEADER;
+typedef WapiImageOptionalHeader *PIMAGE_OPTIONAL_HEADER;
+typedef WapiImageNTHeaders32 IMAGE_NT_HEADERS32;
+typedef WapiImageNTHeaders32 *PIMAGE_NT_HEADERS32;
+typedef WapiImageNTHeaders64 IMAGE_NT_HEADERS64;
+typedef WapiImageNTHeaders64 *PIMAGE_NT_HEADERS64;
+typedef WapiImageNTHeaders IMAGE_NT_HEADERS;
+typedef WapiImageNTHeaders *PIMAGE_NT_HEADERS;
+typedef WapiImageSectionHeader IMAGE_SECTION_HEADER;
+typedef WapiImageSectionHeader *PIMAGE_SECTION_HEADER;
+typedef WapiImageResourceDirectory IMAGE_RESOURCE_DIRECTORY;
+typedef WapiImageResourceDirectory *PIMAGE_RESOURCE_DIRECTORY;
+typedef WapiImageResourceDirectoryEntry IMAGE_RESOURCE_DIRECTORY_ENTRY;
+typedef WapiImageResourceDirectoryEntry *PIMAGE_RESOURCE_DIRECTORY_ENTRY;
+typedef WapiImageResourceDataEntry IMAGE_RESOURCE_DATA_ENTRY;
+typedef WapiImageResourceDataEntry *PIMAGE_RESOURCE_DATA_ENTRY;
typedef WapiApcProc PAPCFUNC;
typedef WapiShellExecuteInfo SHELLEXECUTEINFO;
typedef WapiShellExecuteInfo *LPSHELLEXECUTEINFO;
--- /dev/null
+/*
+ * versioninfo.c: Version information support
+ *
+ * Author:
+ * Dick Porter (dick@ximian.com)
+ *
+ * (C) 2007 Novell, Inc.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <mono/io-layer/wapi.h>
+#include <mono/io-layer/wapi-private.h>
+#include <mono/io-layer/versioninfo.h>
+#include <mono/io-layer/io-portability.h>
+#include <mono/io-layer/error.h>
+#include <mono/utils/strenc.h>
+
+#undef DEBUG
+
+static WapiImageSectionHeader *get_enclosing_section_header (guint32 rva, WapiImageNTHeaders *nt_headers)
+{
+ WapiImageSectionHeader *section = IMAGE_FIRST_SECTION (nt_headers);
+ guint32 i;
+
+ for (i = 0; i < nt_headers->FileHeader.NumberOfSections; i++, section++) {
+ guint32 size = section->Misc.VirtualSize;
+ if (size == 0) {
+ size = section->SizeOfRawData;
+ }
+
+ if ((rva >= section->VirtualAddress) &&
+ (rva < (section->VirtualAddress + size))) {
+ return(section);
+ }
+ }
+
+ return(NULL);
+}
+
+static gpointer get_ptr_from_rva (guint32 rva, WapiImageNTHeaders *ntheaders,
+ gpointer file_map)
+{
+ WapiImageSectionHeader *section_header;
+ guint32 delta;
+
+ section_header = get_enclosing_section_header (rva, ntheaders);
+ if (section_header == NULL) {
+ return(NULL);
+ }
+
+ delta = (guint32)(section_header->VirtualAddress -
+ section_header->PointerToRawData);
+
+ return(GUINT_TO_POINTER (GPOINTER_TO_UINT (file_map) + rva - delta));
+}
+
+static gpointer scan_resource_dir (WapiImageResourceDirectory *root,
+ WapiImageNTHeaders *nt_headers,
+ gpointer file_map,
+ WapiImageResourceDirectoryEntry *entry,
+ int level, guint32 res_id, guint32 lang_id,
+ guint32 *size)
+{
+ gboolean is_string = entry->NameIsString;
+ gboolean is_dir = entry->DataIsDirectory;
+ guint32 name_offset = GUINT32_FROM_LE (entry->NameOffset);
+ guint32 dir_offset = GUINT32_FROM_LE (entry->OffsetToDirectory);
+ guint32 data_offset = GUINT32_FROM_LE (entry->OffsetToData);
+
+ if (level == 0) {
+ /* Normally holds a directory entry for each type of
+ * resource
+ */
+ if ((is_string == FALSE &&
+ name_offset != res_id) ||
+ (is_string == TRUE)) {
+ return(NULL);
+ }
+ } else if (level == 1) {
+ /* Normally holds a directory entry for each resource
+ * item
+ */
+ } else if (level == 2) {
+ /* Normally holds a directory entry for each language
+ */
+ if ((is_string == FALSE &&
+ name_offset != lang_id &&
+ lang_id != 0) ||
+ (is_string == TRUE)) {
+ return(NULL);
+ }
+ } else {
+ g_assert_not_reached ();
+ }
+
+ if (is_dir == TRUE) {
+ WapiImageResourceDirectory *res_dir = (WapiImageResourceDirectory *)GUINT_TO_POINTER (GPOINTER_TO_UINT (root) + dir_offset);
+ WapiImageResourceDirectoryEntry *sub_entries = (WapiImageResourceDirectoryEntry *)(res_dir + 1);
+ guint32 entries, i;
+
+ entries = GUINT16_FROM_LE (res_dir->NumberOfNamedEntries) + GUINT16_FROM_LE (res_dir->NumberOfIdEntries);
+
+ for (i = 0; i < entries; i++) {
+ WapiImageResourceDirectoryEntry *sub_entry = &sub_entries[i];
+ gpointer ret;
+
+ ret = scan_resource_dir (root, nt_headers, file_map,
+ sub_entry, level + 1, res_id,
+ lang_id, size);
+ if (ret != NULL) {
+ return(ret);
+ }
+ }
+
+ return(NULL);
+ } else {
+ WapiImageResourceDataEntry *data_entry = (WapiImageResourceDataEntry *)GUINT_TO_POINTER (GPOINTER_TO_UINT (root) + data_offset);
+ *size = GUINT32_FROM_LE (data_entry->Size);
+
+ return(get_ptr_from_rva (data_entry->OffsetToData, nt_headers, file_map));
+ }
+}
+
+static gpointer find_pe_file_resources (gpointer file_map, guint32 map_size,
+ guint32 res_id, guint32 lang_id,
+ guint32 *size)
+{
+ WapiImageDosHeader *dos_header;
+ WapiImageNTHeaders *nt_headers;
+ WapiImageResourceDirectory *resource_dir;
+ WapiImageResourceDirectoryEntry *resource_dir_entry;
+ guint32 resource_rva, entries, i;
+ gpointer ret = NULL;
+
+ dos_header = (WapiImageDosHeader *)file_map;
+ if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
+#ifdef DEBUG
+ g_message ("%s: Bad dos signature 0x%x", __func__,
+ dos_header->e_magic);
+#endif
+
+ SetLastError (ERROR_INVALID_DATA);
+ return(NULL);
+ }
+
+ if (map_size < sizeof(WapiImageNTHeaders) + GUINT32_FROM_LE (dos_header->e_lfanew)) {
+#ifdef DEBUG
+ g_message ("%s: File is too small: %d", __func__, map_size);
+#endif
+
+ SetLastError (ERROR_BAD_LENGTH);
+ return(NULL);
+ }
+
+ nt_headers = (WapiImageNTHeaders *)GUINT_TO_POINTER ((GPOINTER_TO_UINT (file_map) + GUINT32_FROM_LE (dos_header->e_lfanew)));
+ if (nt_headers->Signature != IMAGE_NT_SIGNATURE) {
+#ifdef DEBUG
+ g_message ("%s: Bad NT signature 0x%x", __func__,
+ nt_headers->Signature);
+#endif
+
+ SetLastError (ERROR_INVALID_DATA);
+ return(NULL);
+ }
+
+ if (nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ /* Do 64-bit stuff */
+ resource_rva = GUINT32_FROM_LE (((WapiImageNTHeaders64 *)nt_headers)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
+ } else {
+ resource_rva = GUINT32_FROM_LE (nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
+ }
+
+ if (resource_rva == 0) {
+#ifdef DEBUG
+ g_message ("%s: No resources in file!", __func__);
+#endif
+ SetLastError (ERROR_INVALID_DATA);
+ return(NULL);
+ }
+
+ resource_dir = (WapiImageResourceDirectory *)get_ptr_from_rva (resource_rva, nt_headers, file_map);
+ if (resource_dir == NULL) {
+#ifdef DEBUG
+ g_message ("%s: Can't find resource directory", __func__);
+#endif
+ SetLastError (ERROR_INVALID_DATA);
+ return(NULL);
+ }
+
+ entries = GUINT16_FROM_LE (resource_dir->NumberOfNamedEntries) + GUINT16_FROM_LE (resource_dir->NumberOfIdEntries);
+ resource_dir_entry = (WapiImageResourceDirectoryEntry *)(resource_dir + 1);
+
+ for (i = 0; i < entries; i++) {
+ WapiImageResourceDirectoryEntry *direntry = &resource_dir_entry[i];
+ ret = scan_resource_dir (resource_dir, nt_headers, file_map,
+ direntry, 0, res_id, lang_id, size);
+ if (ret != NULL) {
+ return(ret);
+ }
+ }
+
+ return(NULL);
+}
+
+static gpointer map_pe_file (gunichar2 *filename, guint32 *map_size)
+{
+ gchar *filename_ext;
+ int fd;
+ struct stat statbuf;
+ gpointer file_map;
+
+ /* According to the MSDN docs, a search path is applied to
+ * filename. FIXME: implement this, for now just pass it
+ * straight to fopen
+ */
+
+ filename_ext = mono_unicode_to_external (filename);
+ if (filename_ext == NULL) {
+#ifdef DEBUG
+ g_message ("%s: unicode conversion returned NULL", __func__);
+#endif
+
+ SetLastError (ERROR_INVALID_NAME);
+ return(NULL);
+ }
+
+ fd = _wapi_open (filename_ext, O_RDONLY, 0);
+ if (fd == -1) {
+#ifdef DEBUG
+ g_message ("%s: Error opening file %s: %s", __func__,
+ filename_ext, strerror (errno));
+#endif
+
+ SetLastError (_wapi_get_win32_file_error (errno));
+ g_free (filename_ext);
+
+ return(NULL);
+ }
+
+ if (fstat (fd, &statbuf) == -1) {
+#ifdef DEBUG
+ g_message ("%s: Error stat()ing file %s: %s", __func__,
+ filename_ext, strerror (errno));
+#endif
+
+ SetLastError (_wapi_get_win32_file_error (errno));
+ g_free (filename_ext);
+ close (fd);
+ return(NULL);
+ }
+ *map_size = statbuf.st_size;
+
+ /* Check basic file size */
+ if (statbuf.st_size < sizeof(WapiImageDosHeader)) {
+#ifdef DEBUG
+ g_message ("%s: File %s is too small: %ld", __func__,
+ filename_ext, statbuf.st_size);
+#endif
+
+ SetLastError (ERROR_BAD_LENGTH);
+ g_free (filename_ext);
+ close (fd);
+ return(NULL);
+ }
+
+ file_map = mmap (NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (file_map == MAP_FAILED) {
+#ifdef DEBUG
+ g_message ("%s: Error mmap()int file %s: %s", __func__,
+ filename_ext, strerror (errno));
+#endif
+
+ SetLastError (_wapi_get_win32_file_error (errno));
+ g_free (filename_ext);
+ close (fd);
+ return(NULL);
+ }
+
+ /* Don't need the fd any more */
+ close (fd);
+
+ return(file_map);
+}
+
+static void unmap_pe_file (gpointer file_map, guint32 map_size)
+{
+ munmap (file_map, map_size);
+}
+
+guint32 GetFileVersionInfoSize (gunichar2 *filename, guint32 *handle)
+{
+ gpointer file_map;
+ gpointer versioninfo;
+ guint32 map_size;
+ guint32 size;
+
+ /* This value is unused, but set to zero */
+ *handle = 0;
+
+ file_map = map_pe_file (filename, &map_size);
+ if (file_map == NULL) {
+ return(0);
+ }
+
+ versioninfo = find_pe_file_resources (file_map, map_size, RT_VERSION,
+ 0, &size);
+ if (versioninfo == NULL) {
+ /* Didn't find the resource, so set the return value
+ * to 0
+ */
+ size = 0;
+ }
+
+ unmap_pe_file (file_map, map_size);
+
+ return(size);
+}
+
+gboolean GetFileVersionInfo (gunichar2 *filename, guint32 handle G_GNUC_UNUSED,
+ guint32 len, gpointer data)
+{
+ gpointer file_map;
+ gpointer versioninfo;
+ guint32 map_size;
+ guint32 size;
+ gboolean ret = FALSE;
+
+ file_map = map_pe_file (filename, &map_size);
+ if (file_map == NULL) {
+ return(FALSE);
+ }
+
+ versioninfo = find_pe_file_resources (file_map, map_size, RT_VERSION,
+ 0, &size);
+ if (versioninfo != NULL) {
+ /* This could probably process the data so that
+ * VerQueryValue() doesn't have to follow the data
+ * blocks every time. But hey, these functions aren't
+ * likely to appear in many profiles.
+ */
+ memcpy (data, versioninfo, len < size?len:size);
+ ret = TRUE;
+ }
+
+ unmap_pe_file (file_map, map_size);
+
+ return(ret);
+}
+
+static guint32 unicode_chars (const gunichar2 *str)
+{
+ guint32 len = 0;
+
+ do {
+ if (str[len] == '\0') {
+ return(len);
+ }
+ len++;
+ } while(1);
+}
+
+static gboolean unicode_compare (const gunichar2 *str1, const gunichar2 *str2)
+{
+ while (*str1 && *str2) {
+ if (GUINT16_TO_LE (*str1) != GUINT16_TO_LE (*str2)) {
+ return(FALSE);
+ }
+ ++str1;
+ ++str2;
+ }
+
+ return(*str1 == *str2);
+}
+
+/* compare a little-endian null-terminated utf16 string and a normal string.
+ * Can be used only for ascii or latin1 chars.
+ */
+static gboolean unicode_string_equals (const gunichar2 *str1, const gchar *str2)
+{
+ while (*str1 && *str2) {
+ if (GUINT16_TO_LE (*str1) != *str2) {
+ return(FALSE);
+ }
+ ++str1;
+ ++str2;
+ }
+
+ return(*str1 == *str2);
+}
+
+typedef struct
+{
+ guint16 data_len;
+ guint16 value_len;
+ guint16 type;
+ gunichar2 *key;
+} version_data;
+
+/* Returns a pointer to the value data, because there's no way to know
+ * how big that data is (value_len is set to zero for most blocks :-( )
+ */
+static gconstpointer get_versioninfo_block (gconstpointer data,
+ version_data *block)
+{
+ block->data_len = GUINT16_FROM_LE (*((guint16 *)data));
+ data = (char *)data + sizeof(guint16);
+ block->value_len = GUINT16_FROM_LE (*((guint16 *)data));
+ data = (char *)data + sizeof(guint16);
+
+ /* No idea what the type is supposed to indicate */
+ block->type = GUINT16_FROM_LE (*((guint16 *)data));
+ data = (char *)data + sizeof(guint16);
+ block->key = ((gunichar2 *)data);
+
+ /* Skip over the key (including the terminator) */
+ data = ((gunichar2 *)data) + (unicode_chars (block->key) + 1);
+
+ /* align on a 32-bit boundary */
+ data = (gpointer)((char *)data + 3);
+ data = (gpointer)((char *)data - (GPOINTER_TO_INT (data) & 3));
+
+ return(data);
+}
+
+static gconstpointer get_fixedfileinfo_block (gconstpointer data,
+ version_data *block)
+{
+ gconstpointer data_ptr;
+ gint32 data_len; /* signed to guard against underflow */
+ WapiFixedFileInfo *ffi;
+
+ data_ptr = get_versioninfo_block (data, block);
+ data_len = block->data_len;
+
+ if (block->value_len != sizeof(WapiFixedFileInfo)) {
+#ifdef DEBUG
+ g_message ("%s: FIXEDFILEINFO size mismatch", __func__);
+#endif
+ return(NULL);
+ }
+
+ if (!unicode_string_equals (block->key, "VS_VERSION_INFO")) {
+#ifdef DEBUG
+ g_message ("%s: VS_VERSION_INFO mismatch", __func__);
+#endif
+ return(NULL);
+ }
+
+ ffi = ((WapiFixedFileInfo *)data_ptr);
+ if ((ffi->dwSignature != VS_FFI_SIGNATURE) ||
+ (ffi->dwStrucVersion != VS_FFI_STRUCVERSION)) {
+#ifdef DEBUG
+ g_message ("%s: FIXEDFILEINFO bad signature", __func__);
+#endif
+ return(NULL);
+ }
+
+ return(data_ptr);
+}
+
+static gconstpointer get_varfileinfo_block (gconstpointer data_ptr,
+ version_data *block)
+{
+ /* data is pointing at a Var block
+ */
+ data_ptr = get_versioninfo_block (data_ptr, block);
+
+ return(data_ptr);
+}
+
+static gconstpointer get_string_block (gconstpointer data_ptr,
+ const gunichar2 *string_key,
+ gpointer *string_value,
+ guint32 *string_value_len,
+ version_data *block)
+{
+ guint16 data_len = block->data_len;
+ guint16 string_len = 28; /* Length of the StringTable block */
+
+ /* data_ptr is pointing at an array of one or more String blocks
+ * with total length (not including alignment padding) of
+ * data_len
+ */
+ while (string_len < data_len) {
+ gunichar2 *value;
+
+ /* align on a 32-bit boundary */
+ data_ptr = (gpointer)((char *)data_ptr + 3);
+ data_ptr = (gpointer)((char *)data_ptr - (GPOINTER_TO_INT (data_ptr) & 3));
+
+ data_ptr = get_versioninfo_block (data_ptr, block);
+ if (block->data_len == 0) {
+ /* We must have hit padding, so give up
+ * processing now
+ */
+#ifdef DEBUG
+ g_message ("%s: Hit 0-length block, giving up",
+ __func__);
+#endif
+ return(NULL);
+ }
+
+ string_len = string_len + block->data_len;
+ value = (gunichar2 *)data_ptr;
+
+ if (string_key != NULL &&
+ string_value != NULL &&
+ string_value_len != NULL &&
+ unicode_compare (string_key, block->key) == TRUE) {
+ *string_value = (gpointer)data_ptr;
+ *string_value_len = block->value_len;
+ }
+
+ /* Skip over the value */
+ data_ptr = ((gunichar2 *)data_ptr) + block->value_len;
+ }
+
+ return(data_ptr);
+}
+
+/* Returns a pointer to the byte following the Stringtable block, or
+ * NULL if the data read hits padding. We can't recover from this
+ * because the data length does not include padding bytes, so it's not
+ * possible to just return the start position + length
+ */
+static gconstpointer get_stringtable_block (gconstpointer data_ptr,
+ gunichar2 lang[8],
+ const gunichar2 *string_key,
+ gpointer *string_value,
+ guint32 *string_value_len,
+ version_data *block)
+{
+ guint16 data_len = block->data_len;
+ guint16 string_len = 36; /* length of the StringFileInfo block */
+
+ /* data_ptr is pointing at an array of StringTable blocks,
+ * with total length (not including alignment padding) of
+ * data_len
+ */
+
+ while(string_len < data_len) {
+ /* align on a 32-bit boundary */
+ data_ptr = (gpointer)((char *)data_ptr + 3);
+ data_ptr = (gpointer)((char *)data_ptr - (GPOINTER_TO_INT (data_ptr) & 3));
+
+ data_ptr = get_versioninfo_block (data_ptr, block);
+ if (block->data_len == 0) {
+ /* We must have hit padding, so give up
+ * processing now
+ */
+#ifdef DEBUG
+ g_message ("%s: Hit 0-length block, giving up",
+ __func__);
+#endif
+ return(NULL);
+ }
+
+ string_len = string_len + block->data_len;
+
+ if (!memcmp (block->key, lang, 8 * sizeof(gunichar2))) {
+ /* Got the one we're interested in */
+ data_ptr = get_string_block (data_ptr, string_key,
+ string_value,
+ string_value_len, block);
+ } else {
+ data_ptr = get_string_block (data_ptr, NULL, NULL,
+ NULL, block);
+ }
+
+ if (data_ptr == NULL) {
+ /* Child block hit padding */
+#ifdef DEBUG
+ g_message ("%s: Child block hit 0-length block, giving up", __func__);
+#endif
+ return(NULL);
+ }
+ }
+
+ return(data_ptr);
+}
+
+gboolean VerQueryValue (gconstpointer datablock, const gunichar2 *subblock,
+ gpointer *buffer, guint32 *len)
+{
+ gchar *subblock_utf8;
+ gboolean ret = FALSE;
+ version_data block;
+ gconstpointer data_ptr;
+ gint32 data_len; /* signed to guard against underflow */
+ gboolean want_var = FALSE;
+ gboolean want_string = FALSE;
+ gunichar2 lang[8];
+ const gunichar2 *string_key = NULL;
+ gpointer string_value = NULL;
+ guint32 string_value_len = 0;
+
+ subblock_utf8 = g_utf16_to_utf8 (subblock, -1, NULL, NULL, NULL);
+ if (subblock_utf8 == NULL) {
+ return(FALSE);
+ }
+
+ if (!strcmp (subblock_utf8, "\\VarFileInfo\\Translation")) {
+ want_var = TRUE;
+ } else if (!strncmp (subblock_utf8, "\\StringFileInfo\\", 16)) {
+ want_string = TRUE;
+ memcpy (lang, subblock + 16, 8 * sizeof(gunichar2));
+ string_key = subblock + 25;
+ }
+
+ if (!strcmp (subblock_utf8, "\\")) {
+ data_ptr = get_fixedfileinfo_block (datablock, &block);
+ if (data_ptr != NULL) {
+ *buffer = (gpointer)data_ptr;
+ *len = block.value_len;
+
+ ret = TRUE;
+ }
+ } else if (want_var || want_string) {
+ data_ptr = get_fixedfileinfo_block (datablock, &block);
+ if (data_ptr != NULL) {
+ /* The FFI and header occupies the first 92
+ * bytes
+ */
+ data_ptr = (char *)data_ptr + sizeof(WapiFixedFileInfo);
+ data_len = block.data_len - 92;
+
+ /* There now follow zero or one StringFileInfo
+ * blocks and zero or one VarFileInfo blocks
+ */
+ while (data_len > 0) {
+ /* align on a 32-bit boundary */
+ data_ptr = (gpointer)((char *)data_ptr + 3);
+ data_ptr = (gpointer)((char *)data_ptr - (GPOINTER_TO_INT (data_ptr) & 3));
+
+ data_ptr = get_versioninfo_block (data_ptr,
+ &block);
+ if (block.data_len == 0) {
+ /* We must have hit padding,
+ * so give up processing now
+ */
+#ifdef DEBUG
+ g_message ("%s: Hit 0-length block, giving up", __func__);
+#endif
+ goto done;
+ }
+
+ data_len = data_len - block.data_len;
+
+ if (unicode_string_equals (block.key, "VarFileInfo")) {
+ data_ptr = get_varfileinfo_block (data_ptr, &block);
+ if (want_var) {
+ *buffer = (gpointer)data_ptr;
+ *len = block.value_len;
+ ret = TRUE;
+ goto done;
+ } else {
+ /* Skip over the Var block */
+ data_ptr = ((guchar *)data_ptr) + block.value_len;
+ }
+ } else if (unicode_string_equals (block.key, "StringFileInfo")) {
+ data_ptr = get_stringtable_block (data_ptr, lang, string_key, &string_value, &string_value_len, &block);
+ if (want_string &&
+ string_value != NULL &&
+ string_value_len != 0) {
+ *buffer = string_value;
+ *len = string_value_len;
+ ret = TRUE;
+ goto done;
+ }
+ } else {
+ /* Bogus data */
+#ifdef DEBUG
+ g_message ("%s: Not a valid VERSIONINFO child block", __func__);
+#endif
+ goto done;
+ }
+
+ if (data_ptr == NULL) {
+ /* Child block hit padding */
+#ifdef DEBUG
+ g_message ("%s: Child block hit 0-length block, giving up", __func__);
+#endif
+ goto done;
+ }
+ }
+ }
+ }
+
+ done:
+ g_free (subblock_utf8);
+ return(ret);
+}
+
+guint32 VerLanguageName (guint32 lang, gunichar2 *lang_out, guint32 lang_len)
+{
+ return(0);
+}
guint32 dwFileDateLS;
} WapiFixedFileInfo;
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+#define VS_FFI_SIGNATURE 0xbd04effe
+#define VS_FFI_STRUCVERSION 0x00000100
+#else
+#define VS_FFI_SIGNATURE 0xfeef04bd
+#define VS_FFI_STRUCVERSION 0x00010000
+#endif
+
+#define VS_FFI_FILEFLAGSMASK 0x3f
+
+typedef struct
+{
+ gpointer lpBaseOfDll;
+ guint32 SizeOfImage;
+ gpointer EntryPoint;
+} WapiModuleInfo;
+
+#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
+
+#define IMAGE_DIRECTORY_ENTRY_EXPORT 0
+#define IMAGE_DIRECTORY_ENTRY_IMPORT 1
+#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
+#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
+#define IMAGE_DIRECTORY_ENTRY_SECURITY 4
+#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
+#define IMAGE_DIRECTORY_ENTRY_DEBUG 6
+#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7
+#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7
+#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8
+#define IMAGE_DIRECTORY_ENTRY_TLS 9
+#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
+#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11
+#define IMAGE_DIRECTORY_ENTRY_IAT 12
+#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13
+#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14
+
+#define IMAGE_SIZEOF_SHORT_NAME 8
+
+#define IMAGE_RESOURCE_NAME_IS_STRING 0x80000000
+#define IMAGE_RESOURCE_DATA_IS_DIRECTORY 0x80000000
+
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+#define IMAGE_DOS_SIGNATURE 0x4d5a
+#define IMAGE_NT_SIGNATURE 0x50450000
+#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0xb10
+#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0xb20
+#else
+#define IMAGE_DOS_SIGNATURE 0x5a4d
+#define IMAGE_NT_SIGNATURE 0x00004550
+#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b
+#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b
+#endif
+
+typedef struct
+{
+ guint16 e_magic;
+ guint16 e_cblp;
+ guint16 e_cp;
+ guint16 e_crlc;
+ guint16 e_cparhdr;
+ guint16 e_minalloc;
+ guint16 e_maxalloc;
+ guint16 e_ss;
+ guint16 e_sp;
+ guint16 e_csum;
+ guint16 e_ip;
+ guint16 e_cs;
+ guint16 e_lfarlc;
+ guint16 e_ovno;
+ guint16 e_res[4];
+ guint16 e_oemid;
+ guint16 e_oeminfo;
+ guint16 e_res2[10];
+ guint32 e_lfanew;
+} WapiImageDosHeader;
+
+typedef struct
+{
+ guint16 Machine;
+ guint16 NumberOfSections;
+ guint32 TimeDateStamp;
+ guint32 PointerToSymbolTable;
+ guint32 NumberOfSymbols;
+ guint16 SizeOfOptionalHeader;
+ guint16 Characteristics;
+} WapiImageFileHeader;
+
+typedef struct
+{
+ guint32 VirtualAddress;
+ guint32 Size;
+} WapiImageDataDirectory;
+
+typedef struct
+{
+ guint16 Magic;
+ guint8 MajorLinkerVersion;
+ guint8 MinorLinkerVersion;
+ guint32 SizeOfCode;
+ guint32 SizeOfInitializedData;
+ guint32 SizeOfUninitializedData;
+ guint32 AddressOfEntryPoint;
+ guint32 BaseOfCode;
+ guint32 BaseOfData;
+ guint32 ImageBase;
+ guint32 SectionAlignment;
+ guint32 FileAlignment;
+ guint16 MajorOperatingSystemVersion;
+ guint16 MinorOperatingSystemVersion;
+ guint16 MajorImageVersion;
+ guint16 MinorImageVersion;
+ guint16 MajorSubsystemVersion;
+ guint16 MinorSubsystemVersion;
+ guint32 Win32VersionValue;
+ guint32 SizeOfImage;
+ guint32 SizeOfHeaders;
+ guint32 CheckSum;
+ guint16 Subsystem;
+ guint16 DllCharacteristics;
+ guint32 SizeOfStackReserve;
+ guint32 SizeOfStackCommit;
+ guint32 SizeOfHeapReserve;
+ guint32 SizeOfHeapCommit;
+ guint32 LoaderFlags;
+ guint32 NumberOfRvaAndSizes;
+ WapiImageDataDirectory DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+} WapiImageOptionalHeader32;
+
+typedef struct
+{
+ guint16 Magic;
+ guint8 MajorLinkerVersion;
+ guint8 MinorLinkerVersion;
+ guint32 SizeOfCode;
+ guint32 SizeOfInitializedData;
+ guint32 SizeOfUninitializedData;
+ guint32 AddressOfEntryPoint;
+ guint32 BaseOfCode;
+ guint64 ImageBase;
+ guint32 SectionAlignment;
+ guint32 FileAlignment;
+ guint16 MajorOperatingSystemVersion;
+ guint16 MinorOperatingSystemVersion;
+ guint16 MajorImageVersion;
+ guint16 MinorImageVersion;
+ guint16 MajorSubsystemVersion;
+ guint16 MinorSubsystemVersion;
+ guint32 Win32VersionValue;
+ guint32 SizeOfImage;
+ guint32 SizeOfHeaders;
+ guint32 CheckSum;
+ guint16 Subsystem;
+ guint16 DllCharacteristics;
+ guint64 SizeOfStackReserve;
+ guint64 SizeOfStackCommit;
+ guint64 SizeOfHeapReserve;
+ guint64 SizeOfHeapCommit;
+ guint32 LoaderFlags;
+ guint32 NumberOfRvaAndSizes;
+ WapiImageDataDirectory DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+} WapiImageOptionalHeader64;
+
+#if SIZEOF_VOID_P == 8
+typedef WapiImageOptionalHeader64 WapiImageOptionalHeader;
+#else
+typedef WapiImageOptionalHeader32 WapiImageOptionalHeader;
+#endif
+
+typedef struct
+{
+ guint32 Signature;
+ WapiImageFileHeader FileHeader;
+ WapiImageOptionalHeader OptionalHeader;
+} WapiImageNTHeaders32;
+
+typedef struct
+{
+ guint32 Signature;
+ WapiImageFileHeader FileHeader;
+ WapiImageOptionalHeader64 OptionalHeader;
+} WapiImageNTHeaders64;
+
+#if SIZEOF_VOID_P == 8
+typedef WapiImageNTHeaders64 WapiImageNTHeaders;
+#else
+typedef WapiImageNTHeaders32 WapiImageNTHeaders;
+#endif
+
+typedef struct
+{
+ guint8 Name[IMAGE_SIZEOF_SHORT_NAME];
+ union
+ {
+ guint32 PhysicalAddress;
+ guint32 VirtualSize;
+ } Misc;
+ guint32 VirtualAddress;
+ guint32 SizeOfRawData;
+ guint32 PointerToRawData;
+ guint32 PointerToRelocations;
+ guint32 PointerToLinenumbers;
+ guint16 NumberOfRelocations;
+ guint16 NumberOfLinenumbers;
+ guint32 Characteristics;
+} WapiImageSectionHeader;
+
+#define IMAGE_FIRST_SECTION(header) ((WapiImageSectionHeader *)(GPOINTER_TO_UINT (header) + G_STRUCT_OFFSET (WapiImageNTHeaders, OptionalHeader) + ((WapiImageNTHeaders *)(header))->FileHeader.SizeOfOptionalHeader))
+
+#define RT_CURSOR 0x01
+#define RT_BITMAP 0x02
+#define RT_ICON 0x03
+#define RT_MENU 0x04
+#define RT_DIALOG 0x05
+#define RT_STRING 0x06
+#define RT_FONTDIR 0x07
+#define RT_FONT 0x08
+#define RT_ACCELERATOR 0x09
+#define RT_RCDATA 0x0a
+#define RT_MESSAGETABLE 0x0b
+#define RT_GROUP_CURSOR 0x0c
+#define RT_GROUP_ICON 0x0e
+#define RT_VERSION 0x10
+#define RT_DLGINCLUDE 0x11
+#define RT_PLUGPLAY 0x13
+#define RT_VXD 0x14
+#define RT_ANICURSOR 0x15
+#define RT_ANIICON 0x16
+#define RT_HTML 0x17
+#define RT_MANIFEST 0x18
+
+typedef struct
+{
+ guint32 Characteristics;
+ guint32 TimeDateStamp;
+ guint16 MajorVersion;
+ guint16 MinorVersion;
+ guint16 NumberOfNamedEntries;
+ guint16 NumberOfIdEntries;
+} WapiImageResourceDirectory;
+
+typedef struct
+{
+ union
+ {
+ struct
+ {
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+ guint32 NameIsString:1;
+ guint32 NameOffset:31;
+#else
+ guint32 NameOffset:31;
+ guint32 NameIsString:1;
+#endif
+ };
+ guint32 Name;
+ guint16 Id;
+ };
+ union
+ {
+ guint32 OffsetToData;
+ struct
+ {
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+ guint32 DataIsDirectory:1;
+ guint32 OffsetToDirectory:31;
+#else
+ guint32 OffsetToDirectory:31;
+ guint32 DataIsDirectory:1;
+#endif
+ };
+ };
+} WapiImageResourceDirectoryEntry;
+
+typedef struct
+{
+ guint32 OffsetToData;
+ guint32 Size;
+ guint32 CodePage;
+ guint32 Reserved;
+} WapiImageResourceDataEntry;
+
#define VS_FF_DEBUG 0x0001
#define VS_FF_PRERELEASE 0x0002
#define VS_FF_PATCHED 0x0004
#define VFT2_FONT_VECTOR 0x0002
#define VFT2_FONT_TRUETYPE 0x0003
-#if G_BYTE_ORDER != G_LITTLE_ENDIAN
-#define VS_FFI_SIGNATURE 0xbd04effe
-#define VS_FFI_STRUCVERSION 0x00000100
-#else
-#define VS_FFI_SIGNATURE 0xfeef04bd
-#define VS_FFI_STRUCVERSION 0x00010000
-#endif
-
-#define VS_FFI_FILEFLAGSMASK 0x3f
+extern guint32 GetFileVersionInfoSize (gunichar2 *filename, guint32 *handle);
+extern gboolean GetFileVersionInfo (gunichar2 *filename, guint32 handle,
+ guint32 len, gpointer data);
+extern gboolean VerQueryValue (gconstpointer datablock,
+ const gunichar2 *subblock, gpointer *buffer,
+ guint32 *len);
+extern guint32 VerLanguageName (guint32 lang, gunichar2 *lang_out,
+ guint32 lang_len);
#endif /* _WAPI_VERSIONINFO_H_ */
+2007-11-15 Dick Porter <dick@ximian.com>
+
+ * process.c: Read file version info from the files pointed at by
+ process modules, not the current process. Fixes bug 315969.
+
+ Use windows typedef names in some places to fix warnings on the
+ windows build.
+
2007-11-15 Mark Probst <mark.probst@gmail.com>
* image.c, metadata-internals.h: Added a generic_class_cache hash
ICALL(PROCESS_1, "CreateProcess_internal(System.Diagnostics.ProcessStartInfo,intptr,intptr,intptr,System.Diagnostics.Process/ProcInfo&)", ves_icall_System_Diagnostics_Process_CreateProcess_internal)
ICALL(PROCESS_2, "ExitCode_internal(intptr)", ves_icall_System_Diagnostics_Process_ExitCode_internal)
ICALL(PROCESS_3, "ExitTime_internal(intptr)", ves_icall_System_Diagnostics_Process_ExitTime_internal)
-ICALL(PROCESS_4, "GetModules_internal()", ves_icall_System_Diagnostics_Process_GetModules_internal)
+ICALL(PROCESS_4, "GetModules_internal(intptr)", ves_icall_System_Diagnostics_Process_GetModules_internal)
ICALL(PROCESS_5, "GetPid_internal()", ves_icall_System_Diagnostics_Process_GetPid_internal)
ICALL(PROCESS_5B, "GetPriorityClass(intptr,int&)", ves_icall_System_Diagnostics_Process_GetPriorityClass)
ICALL(PROCESS_6, "GetProcess_internal(int)", ves_icall_System_Diagnostics_Process_GetProcess_internal)
} while(1);
}
-static guint32 unicode_bytes (const gunichar2 *str)
-{
- guint32 len=0;
-
- do {
- if(str[len]=='\0') {
- /* Include the terminators */
- return((len*2)+2);
- }
- len++;
- } while(1);
-}
-
-static gunichar2*
-unicode_get (const gunichar2 *str)
-{
- gunichar2 *swapped;
- int i, len;
-
- len = unicode_bytes (str);
- swapped = g_malloc0 (len);
- i = 0;
- while (str [i]) {
- swapped [i] = GUINT16_FROM_LE (str [i]);
- i ++;
- }
-
- return swapped;
-}
-
-/*
- * compare a little-endian null-terminated utf16 string and a normal string.
- * Can be used only for ascii or latin1 chars.
- */
-static gboolean
-unicode_string_equals (const gunichar2 *str1, const gchar *str2)
-{
- while (*str1 && *str2) {
- if (GUINT16_TO_LE (*str1) != *str2)
- return FALSE;
- ++str1;
- ++str2;
- }
- return *str1 == *str2;
-}
-
static void process_set_field_object (MonoObject *obj, const gchar *fieldname,
MonoObject *data)
{
*(MonoString **)(((char *)obj) + field->offset)=string;
}
-static void process_set_field_string_utf8 (MonoObject *obj,
- const gchar *fieldname,
- const gchar *val)
+static void process_set_field_int (MonoObject *obj, const gchar *fieldname,
+ guint32 val)
{
MonoClassField *field;
- MonoString *string;
#ifdef DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to [%s]",
+ g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to %d",
fieldname, val);
#endif
-
- string=mono_string_new (mono_object_domain (obj), val);
field=mono_class_get_field_from_name (mono_object_class (obj),
fieldname);
- /* FIXME: moving GC */
- *(MonoString **)(((char *)obj) + field->offset)=string;
+ *(guint32 *)(((char *)obj) + field->offset)=val;
}
-static void process_set_field_int (MonoObject *obj, const gchar *fieldname,
- guint32 val)
+static void process_set_field_intptr (MonoObject *obj, const gchar *fieldname,
+ gpointer val)
{
MonoClassField *field;
#ifdef DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION ": Setting field %s to %d",
- fieldname, val);
+ g_message ("%s: Setting field %s to %p", __func__, fieldname, val);
#endif
field=mono_class_get_field_from_name (mono_object_class (obj),
fieldname);
- *(guint32 *)(((char *)obj) + field->offset)=val;
+ *(gpointer *)(((char *)obj) + field->offset)=val;
}
static void process_set_field_bool (MonoObject *obj, const gchar *fieldname,
*(guint8 *)(((char *)obj) + field->offset)=val;
}
-typedef struct {
- guint16 data_len;
- guint16 value_len;
- guint16 type;
- gunichar2 *key;
-} version_data;
-
-/* Returns a pointer to the value data, because theres no way to know
- * how big that data is (value_len is set to zero for most blocks :-()
- */
-static gpointer process_get_versioninfo_block (gpointer data,
- version_data *block)
-{
- block->data_len=GUINT16_TO_LE (*((guint16 *)data));
- data = (char *)data + sizeof(guint16);
- block->value_len=GUINT16_TO_LE (*((guint16 *)data));
- data = (char *)data + sizeof(guint16);
-
- /* No idea what the type is supposed to indicate */
- block->type=GUINT16_TO_LE (*((guint16 *)data));
- data = (char *)data + sizeof(guint16);
- block->key=((gunichar2 *)data);
-
- /* skip over the key (including the terminator) */
- data=((gunichar2 *)data)+(unicode_chars (block->key)+1);
-
- /* align on a 32-bit boundary */
- data=(gpointer)((char *)data + 3);
- data=(gpointer)((char *)data - (GPOINTER_TO_INT(data) & 3));
-
- return(data);
-}
+#define SFI_COMMENTS "\\StringFileInfo\\%02X%02X%02X%02X\\Comments"
+#define SFI_COMPANYNAME "\\StringFileInfo\\%02X%02X%02X%02X\\CompanyName"
+#define SFI_FILEDESCRIPTION "\\StringFileInfo\\%02X%02X%02X%02X\\FileDescription"
+#define SFI_FILEVERSION "\\StringFileInfo\\%02X%02X%02X%02X\\FileVersion"
+#define SFI_INTERNALNAME "\\StringFileInfo\\%02X%02X%02X%02X\\InternalName"
+#define SFI_LEGALCOPYRIGHT "\\StringFileInfo\\%02X%02X%02X%02X\\LegalCopyright"
+#define SFI_LEGALTRADEMARKS "\\StringFileInfo\\%02X%02X%02X%02X\\LegalTrademarks"
+#define SFI_ORIGINALFILENAME "\\StringFileInfo\\%02X%02X%02X%02X\\OriginalFilename"
+#define SFI_PRIVATEBUILD "\\StringFileInfo\\%02X%02X%02X%02X\\PrivateBuild"
+#define SFI_PRODUCTNAME "\\StringFileInfo\\%02X%02X%02X%02X\\ProductName"
+#define SFI_PRODUCTVERSION "\\StringFileInfo\\%02X%02X%02X%02X\\ProductVersion"
+#define SFI_SPECIALBUILD "\\StringFileInfo\\%02X%02X%02X%02X\\SpecialBuild"
-/* Returns a pointer to the byte following the Var block */
-static gpointer process_read_var_block (MonoObject *filever, gpointer data_ptr,
- guint16 data_len)
+static void process_module_string_read (MonoObject *filever, gpointer data,
+ const gchar *fieldname,
+ guchar lang_hi, guchar lang_lo,
+ const gchar *key)
{
- /* Not currently interested in the VarFileInfo block. This
- * might change if language support is needed for file version
- * strings (VarFileInfo contains lists of supported
- * languages.)
- */
- version_data block;
+ gchar *lang_key_utf8;
+ gunichar2 *lang_key, *buffer;
+ UINT bytes;
- /* data_ptr is pointing at a Var block of length data_len */
- data_ptr=process_get_versioninfo_block (data_ptr, &block);
- data_ptr=((guchar *)data_ptr)+block.value_len;
-
- return(data_ptr);
-}
-
-/* Returns a pointer to the byte following the String block, or NULL
- * if the data read hits padding. We can't recover from this because
- * the data length does not include padding bytes, so it's not
- * possible to just return the start position + length.
- */
-static gpointer process_read_string_block (MonoObject *filever,
- gpointer data_ptr,
- guint16 data_len,
- gboolean store)
-{
- version_data block;
- guint16 string_len=28; /* Length of the StringTable block */
+ lang_key_utf8 = g_strdup_printf (key, lang_lo, lang_hi, 0x04, 0xb0);
- /* data_ptr is pointing at an array of one or more String
- * blocks with total length (not including alignment padding)
- * of data_len.
- */
- while(string_len<data_len) {
- gunichar2 *value;
-
- /* align on a 32-bit boundary */
- data_ptr=(gpointer)((char *)data_ptr + 3);
- data_ptr=(gpointer)((char *)data_ptr -
- (GPOINTER_TO_INT(data_ptr) & 3));
-
- data_ptr=process_get_versioninfo_block (data_ptr, &block);
- if(block.data_len==0) {
- /* We must have hit padding, so give up
- * processing now
- */
#ifdef DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION
- ": Hit 0-length block, giving up");
+ g_message ("%s: asking for [%s]", __func__, lang_key_utf8);
#endif
- return(NULL);
- }
-
- string_len=string_len+block.data_len;
- value=unicode_get ((gunichar2 *)data_ptr);
- /* Skip over the value */
- data_ptr=((gunichar2 *)data_ptr)+block.value_len;
-
- if(store==TRUE) {
- if (unicode_string_equals (block.key, "Comments")) {
- process_set_field_string (filever, "comments", value, unicode_chars (value));
- } else if (unicode_string_equals (block.key, "CompanyName")) {
- process_set_field_string (filever, "companyname", value, unicode_chars (value));
- } else if (unicode_string_equals (block.key, "FileDescription")) {
- process_set_field_string (filever, "filedescription", value, unicode_chars (value));
- } else if (unicode_string_equals (block.key, "FileVersion")) {
- process_set_field_string (filever, "fileversion", value, unicode_chars (value));
- } else if (unicode_string_equals (block.key, "InternalName")) {
- process_set_field_string (filever, "internalname", value, unicode_chars (value));
- } else if (unicode_string_equals (block.key, "LegalCopyright")) {
- process_set_field_string (filever, "legalcopyright", value, unicode_chars (value));
- } else if (unicode_string_equals (block.key, "LegalTrademarks")) {
- process_set_field_string (filever, "legaltrademarks", value, unicode_chars (value));
- } else if (unicode_string_equals (block.key, "OriginalFilename")) {
- process_set_field_string (filever, "originalfilename", value, unicode_chars (value));
- } else if (unicode_string_equals (block.key, "PrivateBuild")) {
- process_set_field_string (filever, "privatebuild", value, unicode_chars (value));
- } else if (unicode_string_equals (block.key, "ProductName")) {
- process_set_field_string (filever, "productname", value, unicode_chars (value));
- } else if (unicode_string_equals (block.key, "ProductVersion")) {
- process_set_field_string (filever, "productversion", value, unicode_chars (value));
- } else if (unicode_string_equals (block.key, "SpecialBuild")) {
- process_set_field_string (filever, "specialbuild", value, unicode_chars (value));
- } else {
- /* Not an error, just not interesting
- * in this case
- */
- }
- }
- g_free (value);
- }
-
- return(data_ptr);
-}
-/* returns a pointer to the byte following the Stringtable block, or
- * NULL if the data read hits padding. We can't recover from this
- * because the data length does not include padding bytes, so it's not
- * possible to just return the start position + length
- */
-static gpointer process_read_stringtable_block (MonoObject *filever,
- gpointer data_ptr,
- guint16 data_len)
-{
- version_data block;
- gunichar2 *value;
- gchar *language;
- guint16 string_len=36; /* length of the StringFileInfo block */
-
- /* data_ptr is pointing at an array of StringTable blocks,
- * with total length (not including alignment padding) of
- * data_len.
- */
-
- while(string_len<data_len) {
- /* align on a 32-bit boundary */
- data_ptr=(gpointer)((char *)data_ptr + 3);
- data_ptr=(gpointer)((char *)data_ptr -
- (GPOINTER_TO_INT(data_ptr) & 3));
-
- data_ptr=process_get_versioninfo_block (data_ptr, &block);
- if(block.data_len==0) {
- /* We must have hit padding, so give up
- * processing now
- */
-#ifdef DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION
- ": Hit 0-length block, giving up");
-#endif
- return(NULL);
- }
- string_len=string_len+block.data_len;
-
- value = unicode_get (block.key);
- language = g_utf16_to_utf8 (value, unicode_bytes (block.key), NULL, NULL, NULL);
- g_strdown (language);
-
- /* Kludge: treat en_US as neutral too */
- if (!strcmp (language, "007f04b0") ||
- !strcmp (language, "000004b0") ||
- !strcmp (language, "040904b0")) {
- /* Got the one we're interested in */
- process_set_field_string_utf8 (filever, "language",
- "Language Neutral");
-
- data_ptr=process_read_string_block (filever, data_ptr,
- block.data_len,
- TRUE);
- } else {
- /* Some other language. We might want to do
- * something with this in the future.
- */
- data_ptr=process_read_string_block (filever, data_ptr,
- block.data_len,
- FALSE);
- }
- g_free (language);
- g_free (value);
-
- if(data_ptr==NULL) {
- /* Child block hit padding */
-#ifdef DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION ": Child block hit 0-length block, giving up");
-#endif
- return(NULL);
- }
- }
-
- return(data_ptr);
-}
+ lang_key = g_utf8_to_utf16 (lang_key_utf8, -1, NULL, NULL, NULL);
-static void process_read_fixedfileinfo_block (MonoObject *filever,
- VS_FIXEDFILEINFO *ffi)
-{
+ if (VerQueryValue (data, lang_key, (gpointer *)&buffer, &bytes)) {
#ifdef DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION ": ffi: sig 0x%x, strucver 0x%x, fileverm 0x%x, fileverl 0x%x, prodverm 0x%x, prodverl 0x%x, ffmask 0x%x, ff 0x%x, os 0x%x, type 0x%x, subtype 0x%x, datem 0x%x, datel 0x%x", ffi->dwSignature, ffi->dwStrucVersion, ffi->dwFileVersionMS, ffi->dwFileVersionLS, ffi->dwProductVersionMS, ffi->dwProductVersionLS, ffi->dwFileFlagsMask, ffi->dwFileFlags, ffi->dwFileOS, ffi->dwFileType, ffi->dwFileSubtype, ffi->dwFileDateMS, ffi->dwFileDateLS);
+ g_message ("%s: found %d bytes of [%s]", __func__, bytes,
+ g_utf16_to_utf8 (buffer, bytes, NULL, NULL, NULL));
#endif
-
- process_set_field_int (filever, "filemajorpart",
- HIWORD (ffi->dwFileVersionMS));
- process_set_field_int (filever, "fileminorpart",
- LOWORD (ffi->dwFileVersionMS));
- process_set_field_int (filever, "filebuildpart",
- HIWORD (ffi->dwFileVersionLS));
- process_set_field_int (filever, "fileprivatepart",
- LOWORD (ffi->dwFileVersionLS));
-
- process_set_field_int (filever, "productmajorpart",
- HIWORD (ffi->dwProductVersionMS));
- process_set_field_int (filever, "productminorpart",
- LOWORD (ffi->dwProductVersionMS));
- process_set_field_int (filever, "productbuildpart",
- HIWORD (ffi->dwProductVersionLS));
- process_set_field_int (filever, "productprivatepart",
- LOWORD (ffi->dwProductVersionLS));
-
- process_set_field_bool (filever, "isdebug",
- ffi->dwFileFlags&VS_FF_DEBUG);
- process_set_field_bool (filever, "isprerelease",
- ffi->dwFileFlags&VS_FF_PRERELEASE);
- process_set_field_bool (filever, "ispatched",
- ffi->dwFileFlags&VS_FF_PATCHED);
- process_set_field_bool (filever, "isprivatebuild",
- ffi->dwFileFlags&VS_FF_PRIVATEBUILD);
- process_set_field_bool (filever, "isspecialbuild",
- ffi->dwFileFlags&VS_FF_SPECIALBUILD);
-}
-
-static void process_get_fileversion (MonoObject *filever, MonoImage *image)
-{
- MonoPEResourceDataEntry *version_info;
- gpointer data;
+ process_set_field_string (filever, fieldname, buffer, bytes);
+ }
+
+ g_free (lang_key);
+ g_free (lang_key_utf8);
+}
+
+static void process_module_stringtable (MonoObject *filever, gpointer data,
+ guchar lang_hi, guchar lang_lo)
+{
+ process_module_string_read (filever, data, "comments", lang_hi, lang_lo,
+ SFI_COMMENTS);
+ process_module_string_read (filever, data, "companyname", lang_hi,
+ lang_lo, SFI_COMPANYNAME);
+ process_module_string_read (filever, data, "filedescription", lang_hi,
+ lang_lo, SFI_FILEDESCRIPTION);
+ process_module_string_read (filever, data, "fileversion", lang_hi,
+ lang_lo, SFI_FILEVERSION);
+ process_module_string_read (filever, data, "internalname", lang_hi,
+ lang_lo, SFI_INTERNALNAME);
+ process_module_string_read (filever, data, "legalcopyright", lang_hi,
+ lang_lo, SFI_LEGALCOPYRIGHT);
+ process_module_string_read (filever, data, "legaltrademarks", lang_hi,
+ lang_lo, SFI_LEGALTRADEMARKS);
+ process_module_string_read (filever, data, "originalfilename", lang_hi,
+ lang_lo, SFI_ORIGINALFILENAME);
+ process_module_string_read (filever, data, "privatebuild", lang_hi,
+ lang_lo, SFI_PRIVATEBUILD);
+ process_module_string_read (filever, data, "productname", lang_hi,
+ lang_lo, SFI_PRODUCTNAME);
+ process_module_string_read (filever, data, "productversion", lang_hi,
+ lang_lo, SFI_PRODUCTVERSION);
+ process_module_string_read (filever, data, "specialbuild", lang_hi,
+ lang_lo, SFI_SPECIALBUILD);
+}
+
+static void process_get_fileversion (MonoObject *filever, gunichar2 *filename)
+{
+ DWORD verinfohandle;
VS_FIXEDFILEINFO *ffi;
- gpointer data_ptr;
- version_data block;
- gint32 data_len; /* signed to guard against underflow */
-
- version_info=mono_image_lookup_resource (image,
- MONO_PE_RESOURCE_ID_VERSION,
- 0, NULL);
+ gpointer data;
+ DWORD datalen;
+ guchar *trans_data;
+ gunichar2 *query;
+ UINT ffi_size, trans_size;
+ BOOL ok;
+ int i;
+
+ datalen = GetFileVersionInfoSize (filename, &verinfohandle);
+ if (datalen) {
+ data = g_malloc0 (datalen);
+ ok = GetFileVersionInfo (filename, verinfohandle, datalen,
+ data);
+ if (ok) {
+ query = g_utf8_to_utf16 ("\\", -1, NULL, NULL, NULL);
+ if (query == NULL) {
+ g_free (data);
+ return;
+ }
+
+ if (VerQueryValue (data, query, (gpointer *)&ffi,
+ &ffi_size)) {
#ifdef DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION ": image_lookup returned %p",
- version_info);
+ g_message (G_GNUC_PRETTY_FUNCTION ": recording assembly: FileName [%s] FileVersionInfo [%d.%d.%d.%d]", g_utf16_to_utf8 (filename, -1, NULL, NULL, NULL), HIWORD (ffi->dwFileVersionMS), LOWORD (ffi->dwFileVersionMS), HIWORD (ffi->dwFileVersionLS), LOWORD (ffi->dwFileVersionLS));
#endif
-
- if(version_info==NULL) {
- return;
- }
- data=mono_image_rva_map (image,
- version_info->rde_data_offset);
- g_free (version_info);
- if(data==NULL) {
- return;
- }
-
- /* See io-layer/versioninfo.h for the gory details on how this
- * data is laid out. (data should be pointing to
- * VS_VERSIONINFO data).
- */
-
- data_ptr=process_get_versioninfo_block (data, &block);
-
- data_len=block.data_len;
-
- if(block.value_len!=sizeof(VS_FIXEDFILEINFO)) {
-#ifdef DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION
- ": FIXEDFILEINFO size mismatch");
-#endif
- return;
- }
-
- if (!unicode_string_equals (block.key, "VS_VERSION_INFO")) {
-#ifdef DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION
- ": VS_VERSION_INFO mismatch");
-#endif
- return;
- }
+ process_set_field_int (filever, "filemajorpart", HIWORD (ffi->dwFileVersionMS));
+ process_set_field_int (filever, "fileminorpart", LOWORD (ffi->dwFileVersionMS));
+ process_set_field_int (filever, "filebuildpart", HIWORD (ffi->dwFileVersionLS));
+ process_set_field_int (filever, "fileprivatepart", LOWORD (ffi->dwFileVersionLS));
+
+ process_set_field_int (filever, "productmajorpart", HIWORD (ffi->dwProductVersionMS));
+ process_set_field_int (filever, "productminorpart", LOWORD (ffi->dwProductVersionMS));
+ process_set_field_int (filever, "productbuildpart", HIWORD (ffi->dwProductVersionLS));
+ process_set_field_int (filever, "productprivatepart", LOWORD (ffi->dwProductVersionLS));
+
+ process_set_field_bool (filever, "isdebug", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_DEBUG);
+ process_set_field_bool (filever, "isprerelease", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PRERELEASE);
+ process_set_field_bool (filever, "ispatched", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PATCHED);
+ process_set_field_bool (filever, "isprivatebuild", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_PRIVATEBUILD);
+ process_set_field_bool (filever, "isspecialbuild", (ffi->dwFileFlags & ffi->dwFileFlagsMask) & VS_FF_SPECIALBUILD);
+ }
+ g_free (query);
- ffi=((VS_FIXEDFILEINFO *)data_ptr);
- data_ptr = (char *)data_ptr + sizeof(VS_FIXEDFILEINFO);
- if((ffi->dwSignature!=VS_FFI_SIGNATURE) ||
- (ffi->dwStrucVersion!=VS_FFI_STRUCVERSION)) {
-#ifdef DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION
- ": FIXEDFILEINFO bad signature");
-#endif
- return;
- }
- process_read_fixedfileinfo_block (filever, ffi);
-
- /* Subtract the 92 bytes we've already seen */
- data_len -= 92;
-
- /* There now follow zero or one StringFileInfo blocks and zero
- * or one VarFileInfo blocks
- */
- while(data_len > 0) {
- /* align on a 32-bit boundary */
- data_ptr=(gpointer)((char *)data_ptr + 3);
- data_ptr=(gpointer)((char *)data_ptr -
- (GPOINTER_TO_INT(data_ptr) & 3));
-
- data_ptr=process_get_versioninfo_block (data_ptr, &block);
- if(block.data_len==0) {
- /* We must have hit padding, so give up
- * processing now
- */
-#ifdef DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION
- ": Hit 0-length block, giving up");
-#endif
- return;
- }
-
- data_len=data_len-block.data_len;
-
- if (unicode_string_equals (block.key, "VarFileInfo")) {
- data_ptr=process_read_var_block (filever, data_ptr,
- block.data_len);
- } else if (unicode_string_equals (block.key, "StringFileInfo")) {
- data_ptr=process_read_stringtable_block (filever, data_ptr, block.data_len);
- } else {
- /* Bogus data */
+ query = g_utf8_to_utf16 ("\\VarFileInfo\\Translation", -1, NULL, NULL, NULL);
+ if (query == NULL) {
+ g_free (data);
+ return;
+ }
+
+ if (VerQueryValue (data, query,
+ (gpointer *)&trans_data,
+ &trans_size)) {
+ /* Look for neutral or en_US language data
+ * (or should we use the first language ID we
+ * see?)
+ */
+ for (i = 0; i < trans_size; i += 4) {
#ifdef DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION
- ": Not a valid VERSIONINFO child block");
- return;
+ 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[i], trans_data[i+1], trans_data[i+2], trans_data[i+3]);
#endif
- }
- if(data_ptr==NULL) {
- /* Child block hit padding */
-#ifdef DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION ": Child block hit 0-length block, giving up");
-#endif
- return;
+ if ((trans_data[i] == 0x09 &&
+ trans_data[i+1] == 0x04 &&
+ trans_data[i+2] == 0xb0 &&
+ trans_data[i+3] == 0x04) ||
+ (trans_data[i] == 0x00 &&
+ trans_data[i+1] == 0x00 &&
+ trans_data[i+2] == 0xb0 &&
+ trans_data[i+3] == 0x04) ||
+ (trans_data[i] == 0x7f &&
+ trans_data[i+1] == 0x00 &&
+ trans_data[i+2] == 0xb0 &&
+ trans_data[i+3] == 0x04)) {
+ gunichar2 lang_buf[128];
+ guint32 lang, lang_count;
+
+ lang = (trans_data[i]) |
+ (trans_data[i+1] << 8) |
+ (trans_data[i+2] << 16) |
+ (trans_data[i+3] << 24);
+ lang_count = VerLanguageName (lang, lang_buf, 128);
+ if (lang_count) {
+ process_set_field_string (filever, "language", lang_buf, lang_count);
+ }
+ process_module_stringtable (filever, data, trans_data[i], trans_data[i+1]);
+ }
+ }
+ }
+ g_free (query);
}
+ g_free (data);
}
}
-static void process_add_module (GPtrArray *modules, MonoAssembly *ass)
+static void process_add_module (GPtrArray *modules, HANDLE process, HMODULE mod,
+ gunichar2 *filename, gunichar2 *modulename)
{
MonoClass *proc_class, *filever_class;
MonoObject *item, *filever;
MonoDomain *domain=mono_domain_get ();
- gchar *modulename;
- const char* filename;
+ MODULEINFO modinfo;
+ BOOL ok;
/* Build a System.Diagnostics.ProcessModule with the data.
- * Leave BaseAddress and EntryPointAddress set to NULL,
- * FileName is ass->image->name, FileVersionInfo is an object
- * constructed from the PE image data referenced by
- * ass->image, ModuleMemorySize set to 0, ModuleName the last
- * component of FileName.
*/
proc_class=mono_class_from_name (system_assembly, "System.Diagnostics",
"ProcessModule");
"System.Diagnostics",
"FileVersionInfo");
filever=mono_object_new (domain, filever_class);
-
-#ifdef DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION ": recording assembly: FileName [%s] FileVersionInfo [%d.%d.%d.%d], ModuleName [%s]", ass->image->name, ass->aname.major, ass->aname.minor, ass->aname.build, ass->aname.revision, ass->image->name);
-#endif
- process_get_fileversion (filever, mono_assembly_get_image (ass));
+ process_get_fileversion (filever, filename);
- filename = mono_image_get_filename (mono_assembly_get_image (ass));
- process_set_field_string_utf8 (filever, "filename", filename);
- process_set_field_string_utf8 (item, "filename", filename);
- process_set_field_object (item, "version_info", filever);
+ process_set_field_string (filever, "filename", filename,
+ unicode_chars (filename));
- modulename=g_path_get_basename (filename);
- process_set_field_string_utf8 (item, "modulename", modulename);
- g_free (modulename);
+ ok = GetModuleInformation (process, mod, &modinfo, sizeof(MODULEINFO));
+ if (ok) {
+ process_set_field_intptr (item, "baseaddr",
+ modinfo.lpBaseOfDll);
+ process_set_field_intptr (item, "entryaddr",
+ modinfo.EntryPoint);
+ process_set_field_int (item, "memory_size",
+ modinfo.SizeOfImage);
+ }
+ process_set_field_string (item, "filename", filename,
+ unicode_chars (filename));
+ process_set_field_string (item, "modulename", modulename,
+ unicode_chars (modulename));
+ process_set_field_object (item, "version_info", filever);
/* FIXME: moving GC */
g_ptr_array_add (modules, item);
}
-static void process_scan_modules (gpointer data, gpointer user_data)
-{
- MonoAssembly *ass=data;
- GPtrArray *modules=user_data;
-
- /* The main assembly is already in the list */
- if(mono_assembly_get_main () != ass) {
- process_add_module (modules, ass);
- }
-}
-
-
/* Returns an array of System.Diagnostics.ProcessModule */
-MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this)
+MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this, HANDLE process)
{
- /* I was going to use toolhelp for this, but then realised I
- * was being an idiot :)
- *
- * (Toolhelp would give shared libraries open by the runtime,
- * as well as open assemblies. On windows my tests didnt find
- * the assemblies loaded by mono either.)
- */
GPtrArray *modules_list=g_ptr_array_new ();
MonoArray *arr;
+ HMODULE mods[1024];
+ gunichar2 filename[MAX_PATH];
+ gunichar2 modname[MAX_PATH];
+ DWORD needed;
+ guint32 count;
guint32 i;
MONO_ARCH_SAVE_REGS;
STASH_SYS_ASS (this);
-
- /* Make sure the first entry is the main module */
- process_add_module (modules_list, mono_assembly_get_main ());
-
- mono_assembly_foreach (process_scan_modules, modules_list);
+
+ if (EnumProcessModules (process, mods, sizeof(mods), &needed)) {
+ count = needed / sizeof(HMODULE);
+ for (i = 0; i < count; i++) {
+ if (GetModuleBaseName (process, mods[i], modname,
+ MAX_PATH) &&
+ GetModuleFileNameEx (process, mods[i], filename,
+ MAX_PATH)) {
+ process_add_module (modules_list, process,
+ mods[i], filename, modname);
+ }
+ }
+ }
/* Build a MonoArray out of modules_list */
arr=mono_array_new (mono_domain_get (), mono_get_object_class (),
void ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject *this, MonoString *filename)
{
- MonoImage *image;
- gchar *filename_utf8;
-
MONO_ARCH_SAVE_REGS;
STASH_SYS_ASS (this);
- filename_utf8 = mono_string_to_utf8 (filename);
- image = mono_pe_file_open (filename_utf8, NULL);
- g_free (filename_utf8);
-
- if(image==NULL) {
- /* FIXME: an exception might be appropriate here */
-#ifdef DEBUG
- g_message (G_GNUC_PRETTY_FUNCTION ": Failed to load image");
-#endif
-
- return;
- }
-
- process_get_fileversion (this, image);
- process_set_field_string_utf8 (this, "filename", mono_image_get_filename (image));
-
- mono_image_close (image);
+ process_get_fileversion (this, mono_string_chars (filename));
+ process_set_field_string (this, "filename",
+ mono_string_chars (filename),
+ mono_string_length (filename));
}
/* Only used when UseShellExecute is false */
gint32 ves_icall_System_Diagnostics_Process_ExitCode_internal (HANDLE process)
{
- guint32 code;
+ DWORD code;
MONO_ARCH_SAVE_REGS;
gboolean ok;
HMODULE mod;
gunichar2 name[MAX_PATH];
- guint32 needed;
+ DWORD needed;
guint32 len;
MONO_ARCH_SAVE_REGS;
{
MonoArray *procs;
gboolean ret;
- guint32 needed, count, i;
- guint32 pids[1024];
+ DWORD needed;
+ guint32 count, i;
+ DWORD pids[1024];
MONO_ARCH_SAVE_REGS;
return(NULL);
}
- count=needed/sizeof(guint32);
+ count=needed/sizeof(DWORD);
procs=mono_array_new (mono_domain_get (), mono_get_int32_class (),
count);
for(i=0; i<count; i++) {
MonoBoolean ves_icall_System_Diagnostics_Process_GetWorkingSet_internal (HANDLE process, guint32 *min, guint32 *max)
{
gboolean ret;
- size_t ws_min, ws_max;
+ SIZE_T ws_min, ws_max;
MONO_ARCH_SAVE_REGS;
MonoBoolean ves_icall_System_Diagnostics_Process_SetWorkingSet_internal (HANDLE process, guint32 min, guint32 max, MonoBoolean use_min)
{
gboolean ret;
- size_t ws_min;
- size_t ws_max;
+ SIZE_T ws_min;
+ SIZE_T ws_max;
MONO_ARCH_SAVE_REGS;
}
if(use_min==TRUE) {
- ws_min=min;
+ ws_min=(SIZE_T)min;
} else {
- ws_max=max;
+ ws_max=(SIZE_T)max;
}
ret=SetProcessWorkingSetSize (process, ws_min, ws_max);
MonoArray *ves_icall_System_Diagnostics_Process_GetProcesses_internal (void) MONO_INTERNAL;
guint32 ves_icall_System_Diagnostics_Process_GetPid_internal (void) MONO_INTERNAL;
void ves_icall_System_Diagnostics_Process_Process_free_internal (MonoObject *this, HANDLE process) MONO_INTERNAL;
-MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this) MONO_INTERNAL;
+MonoArray *ves_icall_System_Diagnostics_Process_GetModules_internal (MonoObject *this, HANDLE process) MONO_INTERNAL;
void ves_icall_System_Diagnostics_FileVersionInfo_GetVersionInfo_internal (MonoObject *this, MonoString *filename) MONO_INTERNAL;
MonoBoolean ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoProcessStartInfo *proc_start_info, MonoProcInfo *process_handle) MONO_INTERNAL;
MonoBoolean ves_icall_System_Diagnostics_Process_CreateProcess_internal (MonoProcessStartInfo *proc_start_info, HANDLE stdin_handle, HANDLE stdout_handle, HANDLE stderr_handle, MonoProcInfo *process_handle) MONO_INTERNAL;