From 98ffdf9b1b87e24929bcfdfa7b0334e3c0574c94 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Fri, 12 Mar 2010 21:39:37 +0000 Subject: installer that works from start to finish at last! (WIP) 2 way communication between the processes device_id for specific driver installation --- driver-install_2008.vcproj | 4 + driver-installer_2008.vcproj | 40 +++++----- examples/lsusb.c | 4 +- libusb/os/driver_install.c | 144 ++++++++++++++++++----------------- libusb/os/driver_install.h | 3 +- libusb/os/driver_installer.c | 175 +++++++++++++++++++++++++++++++++++++++++-- libusb/os/windows_usb.h | 13 ---- libusb_2008.sln | 77 ++++++++++--------- 8 files changed, 318 insertions(+), 142 deletions(-) diff --git a/driver-install_2008.vcproj b/driver-install_2008.vcproj index a2182c1..4e9b1b4 100644 --- a/driver-install_2008.vcproj +++ b/driver-install_2008.vcproj @@ -270,6 +270,10 @@ Filter="h;hpp;hxx;hm;inl;inc;xsd" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > + + diff --git a/driver-installer_2008.vcproj b/driver-installer_2008.vcproj index 99ca40a..d9d34e1 100644 --- a/driver-installer_2008.vcproj +++ b/driver-installer_2008.vcproj @@ -93,12 +93,11 @@ /> + + diff --git a/examples/lsusb.c b/examples/lsusb.c index 3b0495f..28e4b51 100644 --- a/examples/lsusb.c +++ b/examples/lsusb.c @@ -55,10 +55,10 @@ main(void) drv_info = list_driverless(); for (; drv_info != NULL; drv_info = drv_info->next) { if (create_inf(drv_info, "C:\\test") == 0) { - run_installer("C:\\test"); + run_installer("C:\\test", drv_info->device_id); } } - update_drivers(); +// update_drivers(); return 0; r = libusb_init(NULL); diff --git a/libusb/os/driver_install.c b/libusb/os/driver_install.c index 83b7bde..dbbfcd5 100644 --- a/libusb/os/driver_install.c +++ b/libusb/os/driver_install.c @@ -11,6 +11,7 @@ #include "libusbi.h" #include "windows_usb.h" #include "driver_install.h" +#include "driver-installer.h" #define INF_NAME "libusb-device.inf" @@ -79,7 +80,6 @@ const char inf[] = "Date = \"03/08/2010\"\n\n" \ "WdfCoInstaller01009.dll=2\n\n" \ "[SourceDisksFiles.ia64]\n"; - struct res { char* id; char* subdir; @@ -91,8 +91,11 @@ const struct res resource[] = { {"AMD64_DLL1" , "amd64", "WdfCoInstaller01009.dl {"X86_DLL1", "x86", "WdfCoInstaller01009.dll"}, {"X86_DLL2", "x86", "winusbcoinstaller2.dll"} }; const int nb_resources = sizeof(resource)/sizeof(resource[0]); - extern char* sanitize_path(const char* path); + +HANDLE pipe = INVALID_HANDLE_VALUE; +char* req_device_id; + char* guid_to_string(const GUID guid) { static char guid_string[MAX_GUID_STRING_LENGTH]; @@ -123,8 +126,6 @@ static int init_cfgmgr32(void) DLL_LOAD(Cfgmgr32.dll, CM_Get_Sibling, TRUE); DLL_LOAD(Cfgmgr32.dll, CM_Get_Device_IDA, TRUE); DLL_LOAD(Cfgmgr32.dll, CM_Get_Device_IDW, TRUE); - DLL_LOAD(Cfgmgr32.dll, CM_Locate_DevNode, TRUE); - DLL_LOAD(Cfgmgr32.dll, CM_Reenumerate_DevNode, TRUE); return LIBUSB_SUCCESS; } @@ -226,6 +227,9 @@ struct driver_info* list_driverless(void) } cur = drv_info; + // sanitized path should NOT be used as device id + drv_info->device_id = strdup(path); + safe_strcpy(drv_info->desc, sizeof(drv_info->desc), desc); token = strtok (sanitized_short, "#&"); @@ -363,21 +367,50 @@ int create_inf(struct driver_info* drv_info, char* path) return 0; } + +int process_message(char* buffer, DWORD size) +{ + DWORD junk; + + if (size <= 0) + return -1; + + switch(buffer[0]) + { + case IC_GET_DEVICE_ID: + usbi_dbg("got request for device_id"); + WriteFile(pipe, req_device_id, strlen(req_device_id), &junk, NULL); + break; + case IC_PRINT_MESSAGE: + if (size < 2) { + usbi_err(NULL, "print_message: no data"); + return -1; + } + usbi_dbg("[installer process] %s", buffer+1); + break; + default: + usbi_err(NULL, "unrecognized installer message"); + return -1; + } + return 0; +} + // TODO: extract driver-installer.exe into the dest dir -int run_installer(char* path) +int run_installer(char* path, char* device_id) { SHELLEXECUTEINFO shExecInfo; char exename[MAX_PATH_LENGTH]; HANDLE handle[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; - HANDLE pipe = INVALID_HANDLE_VALUE; OVERLAPPED overlapped; - int r, ret; + int r; DWORD rd_count; - char buffer[256]; +#define BUFSIZE 256 + char buffer[BUFSIZE]; + req_device_id = device_id; // Use a pipe to communicate with our installer - pipe = CreateNamedPipe("\\\\.\\pipe\\libusb-installer", PIPE_ACCESS_INBOUND|FILE_FLAG_OVERLAPPED, + pipe = CreateNamedPipe("\\\\.\\pipe\\libusb-installer", PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE, 1, 4096, 4096, 0, NULL); if (pipe == INVALID_HANDLE_VALUE) { usbi_err(NULL, "could not create read pipe: errcode %d", (int)GetLastError()); @@ -387,7 +420,7 @@ int run_installer(char* path) // Set the overlapped for messaging memset(&overlapped, 0, sizeof(OVERLAPPED)); handle[0] = CreateEvent(NULL, TRUE, FALSE, NULL); - if(!handle[0]) { + if(handle[0] == NULL) { r = -1; goto out; } overlapped.hEvent = handle[0]; @@ -404,8 +437,9 @@ int run_installer(char* path) // if INF_NAME ever has a space, it will be seen as multiple parameters shExecInfo.lpParameters = INF_NAME; shExecInfo.lpDirectory = path; + // TODO: hide +// shExecInfo.nShow = SW_NORMAL; shExecInfo.nShow = SW_HIDE; - // SW_NORMAL; shExecInfo.hInstApp = NULL; ShellExecuteEx(&shExecInfo); @@ -417,54 +451,53 @@ int run_installer(char* path) handle[1] = shExecInfo.hProcess; while (1) { - if (!ReadFile(pipe, buffer, 256, &rd_count, &overlapped)) { + if (ReadFile(pipe, buffer, 256, &rd_count, &overlapped)) { + // Message was read synchronously + process_message(buffer, rd_count); + } else { switch(GetLastError()) { case ERROR_BROKEN_PIPE: // The pipe has been ended - wait for installer to finish WaitForSingleObject(handle[1], INFINITE); - usbi_dbg("hProcess1 = %p terminated", shExecInfo.hProcess); r = 0; goto out; case ERROR_PIPE_LISTENING: // Wait for installer to open the pipe Sleep(100); continue; case ERROR_IO_PENDING: - break; - default: - usbi_err(NULL, "pipe read message failed: %d", (int)GetLastError()); - continue; - } - - // We have IO_PENDING - switch(WaitForMultipleObjects(2, handle, FALSE, INFINITE)) { - case WAIT_OBJECT_0: // Pipe event - if (!GetOverlappedResult(pipe, &overlapped, &rd_count, FALSE)) { - switch(GetLastError()) { - case ERROR_BROKEN_PIPE: - // The pipe has been ended - wait for installer to finish - WaitForSingleObject(handle[1], INFINITE); - usbi_dbg("hProcess3 = %p terminated", shExecInfo.hProcess); - r = 0; goto out; - case ERROR_MORE_DATA: - usbi_warn(NULL, "message overflow"); - break; - default: - usbi_warn(NULL, "message async read error: %d", (int)GetLastError()); - break; + switch(WaitForMultipleObjects(2, handle, FALSE, INFINITE)) { + case WAIT_OBJECT_0: // Pipe event + if (GetOverlappedResult(pipe, &overlapped, &rd_count, FALSE)) { + // Message was read asynchronously + process_message(buffer, rd_count); + } else { + switch(GetLastError()) { + case ERROR_BROKEN_PIPE: + // The pipe has been ended - wait for installer to finish + WaitForSingleObject(handle[1], INFINITE); + r = 0; goto out; + case ERROR_MORE_DATA: + usbi_warn(NULL, "program assertion failed: message overflow"); + process_message(buffer, rd_count); + break; + default: + usbi_err(NULL, "could not read from pipe (async): %d", (int)GetLastError()); + break; + } } - } else { - usbi_dbg("async: %s", buffer); + break; + case WAIT_OBJECT_0+1: + // installer process terminated + r = 0; goto out; + default: + usbi_err(NULL, "could not read from pipe (wait): %d", (int)GetLastError()); + break; } break; - case WAIT_OBJECT_0+1: - usbi_dbg("hProcess2 = %p terminated", shExecInfo.hProcess); - r = 0; goto out; default: - usbi_dbg("are?"); + usbi_err(NULL, "could not read from pipe (sync): %d", (int)GetLastError()); break; } - } else { - usbi_dbg(" sync: %s", buffer); } } out: @@ -474,27 +507,4 @@ out: return r; } -// TODO: use devinst from setupdi and pass it as param to driver-installer.exe -int update_drivers(void) -{ - DEVINST devInst; - CONFIGRET status; - - usbi_dbg("updating drivers, please wait..."); - // Get the root devnode. - status = CM_Locate_DevNode(&devInst, NULL, 0); - if (status != CR_SUCCESS) { - printf("failed to locate root devnode: %x\n", status); - return -1; - } - - // Needs admin privileges, but even with admin and the - // http://support.microsoft.com/kb/259697 doesn't work!!! - status = CM_Reenumerate_DevNode(devInst, 0); - if (status != CR_SUCCESS) { - printf("failed to re-enumerate nodes: %x\n", status); - return -1; - } - - return 0; -} \ No newline at end of file +//TODO: add a call to free strings & list \ No newline at end of file diff --git a/libusb/os/driver_install.h b/libusb/os/driver_install.h index 3a3b6a8..5aebbef 100644 --- a/libusb/os/driver_install.h +++ b/libusb/os/driver_install.h @@ -5,6 +5,7 @@ struct driver_info { struct driver_info *next; + char* device_id; char desc[MAX_DESC_LENGTH]; char vid[9]; char pid[9]; @@ -14,7 +15,7 @@ struct driver_info { struct driver_info *list_driverless(void); char* guid_to_string(const GUID guid); int create_inf(struct driver_info* drv_info, char* path); -int run_installer(char* path); +int run_installer(char *path, char *dev_inst); int update_drivers(void); diff --git a/libusb/os/driver_installer.c b/libusb/os/driver_installer.c index c6f7d63..5b66b48 100644 --- a/libusb/os/driver_installer.c +++ b/libusb/os/driver_installer.c @@ -8,13 +8,71 @@ #include #include #include +#include #include #include #include #include +#include "driver-installer.h" + +/* + * API macros - from libusb-win32 1.x + */ +#define DLL_DECLARE(api, ret, name, args) \ + typedef ret (api * __dll_##name##_t)args; __dll_##name##_t name + +#define DLL_LOAD(dll, name, ret_on_failure) \ + do { \ + HMODULE h = GetModuleHandle(#dll); \ + if (!h) \ + h = LoadLibrary(#dll); \ + if (!h) { \ + if (ret_on_failure) { return -1; } \ + else { break; } \ + } \ + name = (__dll_##name##_t)GetProcAddress(h, #name); \ + if (name) break; \ + name = (__dll_##name##_t)GetProcAddress(h, #name "A"); \ + if (name) break; \ + name = (__dll_##name##_t)GetProcAddress(h, #name "W"); \ + if (name) break; \ + if(ret_on_failure) \ + return -1; \ + } while(0) + +/* + * Cfgmgr32.dll interface + */ +typedef DWORD DEVNODE, DEVINST; +typedef DEVNODE *PDEVNODE, *PDEVINST; +typedef DWORD RETURN_TYPE; +typedef RETURN_TYPE CONFIGRET; + +#define CR_SUCCESS 0x00000000 + +#define CM_REENUMERATE_NORMAL 0x00000000 +#define CM_REENUMERATE_SYNCHRONOUS 0x00000001 +#define CM_REENUMERATE_RETRY_INSTALLATION 0x00000002 +#define CM_REENUMERATE_ASYNCHRONOUS 0x00000004 +#define CM_REENUMERATE_BITS 0x00000007 + +typedef CHAR *DEVNODEID_A, *DEVINSTID_A; +typedef WCHAR *DEVNODEID_W, *DEVINSTID_W; +#ifdef UNICODE +typedef DEVNODEID_W DEVNODEID; +typedef DEVINSTID_W DEVINSTID; +#else +typedef DEVNODEID_A DEVNODEID; +typedef DEVINSTID_A DEVINSTID; +#endif + +DLL_DECLARE(WINAPI, CONFIGRET, CM_Locate_DevNode, (PDEVINST, DEVINSTID, ULONG)); +DLL_DECLARE(WINAPI, CONFIGRET, CM_Reenumerate_DevNode, (DEVINST, ULONG)); + #define INF_NAME "libusb-device.inf" #define MAX_PATH_LENGTH 128 +#define REQUEST_TIMEOUT 5000 #define safe_strncpy(dst, dst_max, src, count) strncpy(dst, src, min(count, dst_max - 1)) #define safe_strcpy(dst, dst_max, src) safe_strncpy(dst, dst_max, src, strlen(src)+1) #define safe_strncat(dst, dst_max, src, count) strncat(dst, src, min(count, dst_max - strlen(dst) - 1)) @@ -22,6 +80,13 @@ HANDLE pipe = INVALID_HANDLE_VALUE; +static int init_cfgmgr32(void) +{ + DLL_LOAD(Cfgmgr32.dll, CM_Locate_DevNode, TRUE); + DLL_LOAD(Cfgmgr32.dll, CM_Reenumerate_DevNode, TRUE); + return 0; +} + // TODO: return a status byte along with the message void plog_v(const char *format, va_list args) { @@ -31,12 +96,13 @@ void plog_v(const char *format, va_list args) if (pipe == INVALID_HANDLE_VALUE) return; - size = vsnprintf_s(buffer, 256, _TRUNCATE, format, args); + buffer[0] = IC_PRINT_MESSAGE; + size = vsnprintf_s(buffer+1, 255, _TRUNCATE, format, args); if (size < 0) { buffer[255] = 0; - size = 255; + size = 254; } - WriteFile(pipe, buffer, size+1, &size, NULL); + WriteFile(pipe, buffer, size+2, &size, NULL); } void plog(const char *format, ...) @@ -48,6 +114,72 @@ void plog(const char *format, ...) va_end (args); } +int request_data(unsigned char req, void *buffer, int size) +{ + OVERLAPPED overlapped; + DWORD rd_count; + DWORD r, count = (DWORD)size; + + if ((buffer == NULL) || (size <= 0)) { + return -1; + } + + // Set the overlapped for messaging + memset(&overlapped, 0, sizeof(OVERLAPPED)); + overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (overlapped.hEvent == NULL) { + plog("failed to create overlapped"); + return -1; + } + + if (ReadFile(pipe, buffer, count, &rd_count, &overlapped)) { + // Message was read synchronously + plog("received unexpected data"); + CloseHandle(overlapped.hEvent); + return -1; + } + + if (GetLastError() != ERROR_IO_PENDING) { + plog("failure to initiate read (%d)", (int)GetLastError()); + CloseHandle(overlapped.hEvent); + return -1; + } + + // Now that we're set to receive data, let's send our request + WriteFile(pipe, &req, 1, &r, NULL); + + // Wait for the response + r = WaitForSingleObject(overlapped.hEvent, REQUEST_TIMEOUT); + if ( (r == WAIT_OBJECT_0) && (GetOverlappedResult(pipe, &overlapped, &rd_count, FALSE)) ) { + CloseHandle(overlapped.hEvent); + return (int)rd_count; + } + + if (r == WAIT_TIMEOUT) { + plog("message request: timed out"); + } else { + plog("read error: %d", (int)GetLastError()); + } + CloseHandle(overlapped.hEvent); + return -1; +} + +char* req_device_id(void) +{ + int size; + static char device_id[MAX_PATH_LENGTH]; + + memset(device_id, 0, MAX_PATH_LENGTH); + size = request_data(IC_GET_DEVICE_ID, (void*)device_id, sizeof(device_id)); + if (size > 0) { + plog("got device_id: %s", device_id); + return device_id; + } + + plog("failed to read device_id"); + return NULL; +} + void __cdecl log_callback(DIFXAPI_LOG Event, DWORD Error, const TCHAR * pEventDescription, PVOID CallbackContext) { if (Error == 0){ @@ -57,24 +189,51 @@ void __cdecl log_callback(DIFXAPI_LOG Event, DWORD Error, const TCHAR * pEventDe } } +// TODO: allow root re-enum +int update_driver(char* device_id) +{ + DEVINST dev_inst; + CONFIGRET status; + + plog("updating driver node %s...", device_id); + status = CM_Locate_DevNode(&dev_inst, device_id, 0); + if (status != CR_SUCCESS) { + plog("failed to locate device_id %s: %x\n", device_id, status); + return -1; + } + + status = CM_Reenumerate_DevNode(dev_inst, CM_REENUMERATE_RETRY_INSTALLATION); + if (status != CR_SUCCESS) { + plog("failed to re-enumerate device node: CR code %X", status); + return -1; + } + + plog("final installation succeeded..."); + return 0; +} int main(int argc, char** argv) { DWORD r; - BOOL reboot_needed; + char* device_id; + BOOL reboot_needed = FALSE; char path[MAX_PATH_LENGTH]; char log[MAX_PATH_LENGTH]; FILE *fd; // Connect to the messaging pipe - pipe = CreateFile("\\\\.\\pipe\\libusb-installer", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + pipe = CreateFile("\\\\.\\pipe\\libusb-installer", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, NULL); if (pipe == INVALID_HANDLE_VALUE) { - // Try to write on console printf("could not open pipe for writing: errcode %d\n", (int)GetLastError()); return -1; } + if (init_cfgmgr32()) { + plog("could not access dfmgr32 DLL"); + return -1; + } + safe_strcpy(log, MAX_PATH_LENGTH, argv[0]); // TODO - seek for terminal '.exe' and change extension if needed safe_strcat(log, MAX_PATH_LENGTH, ".log"); @@ -95,6 +254,8 @@ int main(int argc, char** argv) safe_strcat(path, MAX_PATH_LENGTH, "\\"); safe_strcat(path, MAX_PATH_LENGTH, INF_NAME); + device_id = req_device_id(); + plog("Installing driver - please wait..."); DIFXAPISetLogCallback(log_callback, NULL); // TODO: set app dependency? @@ -151,6 +312,8 @@ int main(int argc, char** argv) goto out; } + update_driver(device_id); + out: CloseHandle(pipe); return 0; diff --git a/libusb/os/windows_usb.h b/libusb/os/windows_usb.h index 7a181b9..9e04705 100644 --- a/libusb/os/windows_usb.h +++ b/libusb/os/windows_usb.h @@ -395,24 +395,11 @@ typedef enum _USB_HUB_NODE { } USB_HUB_NODE; /* Cfgmgr32.dll interface */ -typedef CHAR *DEVNODEID_A, *DEVINSTID_A; -typedef WCHAR *DEVNODEID_W, *DEVINSTID_W; - -#ifdef UNICODE -typedef DEVNODEID_W DEVNODEID; -typedef DEVINSTID_W DEVINSTID; -#else -typedef DEVNODEID_A DEVNODEID; -typedef DEVINSTID_A DEVINSTID; -#endif - DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG)); DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG)); DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Sibling, (PDEVINST, DEVINST, ULONG)); DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Device_IDA, (DEVINST, PCHAR, ULONG, ULONG)); DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Device_IDW, (DEVINST, PWCHAR, ULONG, ULONG)); -DLL_DECLARE(WINAPI, CONFIGRET, CM_Locate_DevNode, (PDEVINST, DEVINSTID, ULONG)); -DLL_DECLARE(WINAPI, CONFIGRET, CM_Reenumerate_DevNode, (DEVINST, ULONG)); #ifdef UNICODE #define CM_Get_Device_ID CM_Get_Device_IDW diff --git a/libusb_2008.sln b/libusb_2008.sln index fed9d0b..7465f74 100644 --- a/libusb_2008.sln +++ b/libusb_2008.sln @@ -7,12 +7,12 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lsusb", "examples\lsusb_2008.vcproj", "{F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}" ProjectSection(ProjectDependencies) = postProject {9AA0E745-1A0A-4700-8ECB-6A6DE9DBF8B9} = {9AA0E745-1A0A-4700-8ECB-6A6DE9DBF8B9} - {BA852F82-4329-4CBE-82AA-9947FB48AD83} = {BA852F82-4329-4CBE-82AA-9947FB48AD83} {349EE8F9-7D25-4909-AAF5-FF3FADE72187} = {349EE8F9-7D25-4909-AAF5-FF3FADE72187} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xusb", "examples\xusb_2008.vcproj", "{3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}" ProjectSection(ProjectDependencies) = postProject + {9AA0E745-1A0A-4700-8ECB-6A6DE9DBF8B9} = {9AA0E745-1A0A-4700-8ECB-6A6DE9DBF8B9} {349EE8F9-7D25-4909-AAF5-FF3FADE72187} = {349EE8F9-7D25-4909-AAF5-FF3FADE72187} EndProjectSection EndProject @@ -33,51 +33,58 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.ActiveCfg = Debug|Win32 - {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.Build.0 = Debug|Win32 - {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.ActiveCfg = Debug|x64 - {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.Build.0 = Debug|x64 - {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.ActiveCfg = Release|Win32 - {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.Build.0 = Release|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.ActiveCfg = Release|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.Build.0 = Release|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.ActiveCfg = Release|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.Build.0 = Release|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.ActiveCfg = Release|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.Build.0 = Release|x64 {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.ActiveCfg = Release|x64 {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.Build.0 = Release|x64 - {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.ActiveCfg = Debug|Win32 - {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.ActiveCfg = Debug|x64 - {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.ActiveCfg = Release|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.ActiveCfg = Release|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.ActiveCfg = Release|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.ActiveCfg = Release|x64 {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release|x64.ActiveCfg = Release|x64 - {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug|Win32.ActiveCfg = Debug|Win32 - {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug|Win32.Build.0 = Debug|Win32 - {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug|x64.ActiveCfg = Debug|x64 - {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug|x64.Build.0 = Debug|x64 - {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release|Win32.ActiveCfg = Release|Win32 - {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release|Win32.Build.0 = Release|Win32 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug|Win32.ActiveCfg = Release|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug|Win32.Build.0 = Release|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug|x64.ActiveCfg = Release|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Debug|x64.Build.0 = Release|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release|Win32.ActiveCfg = Release|x64 + {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release|Win32.Build.0 = Release|x64 {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release|x64.ActiveCfg = Release|x64 {F4938DB0-3DE7-4737-9C5A-EAD1BE819F87}.Release|x64.Build.0 = Release|x64 - {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug|Win32.ActiveCfg = Debug|Win32 - {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug|Win32.Build.0 = Debug|Win32 - {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug|x64.ActiveCfg = Debug|x64 - {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug|x64.Build.0 = Debug|x64 - {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release|Win32.ActiveCfg = Release|Win32 - {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release|Win32.Build.0 = Release|Win32 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug|Win32.ActiveCfg = Release|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug|Win32.Build.0 = Release|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug|x64.ActiveCfg = Release|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Debug|x64.Build.0 = Release|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release|Win32.ActiveCfg = Release|x64 + {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release|Win32.Build.0 = Release|x64 {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release|x64.ActiveCfg = Release|x64 {3F3138D0-7AB7-4268-9BF3-1A3EA5503A11}.Release|x64.Build.0 = Release|x64 - {9AA0E745-1A0A-4700-8ECB-6A6DE9DBF8B9}.Debug|Win32.ActiveCfg = Debug|Win32 - {9AA0E745-1A0A-4700-8ECB-6A6DE9DBF8B9}.Debug|x64.ActiveCfg = Debug|x64 - {9AA0E745-1A0A-4700-8ECB-6A6DE9DBF8B9}.Release|Win32.ActiveCfg = Release|Win32 - {9AA0E745-1A0A-4700-8ECB-6A6DE9DBF8B9}.Release|Win32.Build.0 = Release|Win32 + {9AA0E745-1A0A-4700-8ECB-6A6DE9DBF8B9}.Debug|Win32.ActiveCfg = Release|x64 + {9AA0E745-1A0A-4700-8ECB-6A6DE9DBF8B9}.Debug|Win32.Build.0 = Release|x64 + {9AA0E745-1A0A-4700-8ECB-6A6DE9DBF8B9}.Debug|x64.ActiveCfg = Release|x64 + {9AA0E745-1A0A-4700-8ECB-6A6DE9DBF8B9}.Debug|x64.Build.0 = Release|x64 + {9AA0E745-1A0A-4700-8ECB-6A6DE9DBF8B9}.Release|Win32.ActiveCfg = Release|x64 + {9AA0E745-1A0A-4700-8ECB-6A6DE9DBF8B9}.Release|Win32.Build.0 = Release|x64 {9AA0E745-1A0A-4700-8ECB-6A6DE9DBF8B9}.Release|x64.ActiveCfg = Release|x64 {9AA0E745-1A0A-4700-8ECB-6A6DE9DBF8B9}.Release|x64.Build.0 = Release|x64 - {BA852F82-4329-4CBE-82AA-9947FB48AD83}.Debug|Win32.ActiveCfg = Debug|Win32 - {BA852F82-4329-4CBE-82AA-9947FB48AD83}.Debug|x64.ActiveCfg = Debug|x64 - {BA852F82-4329-4CBE-82AA-9947FB48AD83}.Release|Win32.ActiveCfg = Release|Win32 - {BA852F82-4329-4CBE-82AA-9947FB48AD83}.Release|Win32.Build.0 = Release|Win32 + {BA852F82-4329-4CBE-82AA-9947FB48AD83}.Debug|Win32.ActiveCfg = Release|x64 + {BA852F82-4329-4CBE-82AA-9947FB48AD83}.Debug|Win32.Build.0 = Release|x64 + {BA852F82-4329-4CBE-82AA-9947FB48AD83}.Debug|x64.ActiveCfg = Release|x64 + {BA852F82-4329-4CBE-82AA-9947FB48AD83}.Debug|x64.Build.0 = Release|x64 + {BA852F82-4329-4CBE-82AA-9947FB48AD83}.Release|Win32.ActiveCfg = Release|x64 + {BA852F82-4329-4CBE-82AA-9947FB48AD83}.Release|Win32.Build.0 = Release|x64 {BA852F82-4329-4CBE-82AA-9947FB48AD83}.Release|x64.ActiveCfg = Release|x64 - {9B1C561E-F95B-4849-A7AA-A4350E227C20}.Debug|Win32.ActiveCfg = Debug|Win32 - {9B1C561E-F95B-4849-A7AA-A4350E227C20}.Debug|Win32.Build.0 = Debug|Win32 - {9B1C561E-F95B-4849-A7AA-A4350E227C20}.Debug|x64.ActiveCfg = Debug|Win32 - {9B1C561E-F95B-4849-A7AA-A4350E227C20}.Release|Win32.ActiveCfg = Release|Win32 - {9B1C561E-F95B-4849-A7AA-A4350E227C20}.Release|Win32.Build.0 = Release|Win32 + {BA852F82-4329-4CBE-82AA-9947FB48AD83}.Release|x64.Build.0 = Release|x64 + {9B1C561E-F95B-4849-A7AA-A4350E227C20}.Debug|Win32.ActiveCfg = Release|x64 + {9B1C561E-F95B-4849-A7AA-A4350E227C20}.Debug|Win32.Build.0 = Release|x64 + {9B1C561E-F95B-4849-A7AA-A4350E227C20}.Debug|x64.ActiveCfg = Release|x64 + {9B1C561E-F95B-4849-A7AA-A4350E227C20}.Debug|x64.Build.0 = Release|x64 + {9B1C561E-F95B-4849-A7AA-A4350E227C20}.Release|Win32.ActiveCfg = Release|x64 + {9B1C561E-F95B-4849-A7AA-A4350E227C20}.Release|Win32.Build.0 = Release|x64 {9B1C561E-F95B-4849-A7AA-A4350E227C20}.Release|x64.ActiveCfg = Release|x64 + {9B1C561E-F95B-4849-A7AA-A4350E227C20}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE -- cgit v1.2.1