/* libusb-win32, Generic Windows USB Library * Copyright (c) 2002-2005 Stephan Meyer * Copyright (c) 2010 Travis Robinson * Parts of the code from libwdi by Pete Batard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #ifdef __GNUC__ #if defined(_WIN64) #include #else #include #endif #else #include #define strlwr(p) _strlwr(p) #endif #include "lusb0_usb.h" #include "registry.h" #include "error.h" #include "driver_api.h" #include "libusb-win32_version.h" #define LIBUSB_DRIVER_PATH "system32\\drivers\\libusb0.sys" #define LIBUSB_OLD_SERVICE_NAME_NT "libusbd" #define INSTALLFLAG_FORCE 0x00000001 #define IDC_PROGRESS_TEXT 10001 #define IDC_PROGRESS_BAR 10002 // #define IDC_INFO_PANE 10003 #define UM_PROGRESS_STOP (0x7000 - 1) #define UM_PROGRESS_START (0x7000 - 2) #define DISPLAY_NAME "libusb-win32 installer" #define DISPLAY_RUNNING "libusb-win32 installer running.." #define DISPLAY_DONE "libusb-win32 installer finished" #define DISPLAY_DONE_WITH_ERRORS "libusb-win32 installer errors!" #define COLOR_RED RGB(128,0,0) #define COLOR_LTRED RGB(255,64,64) #define COLOR_ORANGE RGB(255, 102, 0) #define safe_free(p) do { if (p) free(p); p = NULL; }while(0) #define safe_strlen(p) (p ? strlen(p) : 0) #define MOVE_CONTROL(ParentHwnd, ControlID, Rect, Repaint, TopZOrder) \ SetWindowPos(GetDlgItem(ParentHwnd, ControlID), HWND_TOP, Rect.left, Rect.top, abs(Rect.right - Rect.left), abs(Rect.bottom - Rect.top), \ (Repaint ? 0 : SWP_NOREDRAW) | (TopZOrder ? 0 : SWP_NOZORDER)) // transform coordinates in a RECT from client to screen coordiantes #define ClientToScreenRC(hwnd, prect) do{ClientToScreen(hwnd,(LPPOINT)&prect->left); ClientToScreen(hwnd,(LPPOINT)&prect->right); }while(0) // transform coordinates in a RECT from screen to client coordiantes #define ScreenToClientRC(hwnd, prect) do{ScreenToClient(hwnd,(LPPOINT)&prect->left); ScreenToClient(hwnd,(LPPOINT)&prect->right); }while(0) // fills a pointer to a TRIVERTEX structure #define TRIVERTEX_FILL(TriVertex, XPos, YPos, RedC16, GreenC16, BlueC16, AlphaC16) do { \ (TriVertex)->x = XPos; \ (TriVertex)->y = YPos; \ (TriVertex)->Red = RedC16; \ (TriVertex)->Green = GreenC16; \ (TriVertex)->Blue = BlueC16; \ (TriVertex)->Alpha = AlphaC16; \ }while(0) #define COLOR16_MAX 0xff00 #define RBG_TO_C16(rgb, add, RField, GField, BField) do { \ if (((int)((GetRValue(rgb) + (add)) * 256)) > COLOR16_MAX) \ RField = COLOR16_MAX; \ else if (((int)((GetRValue(rgb) + (add)) * 256)) < 0) \ RField = 0; \ else \ RField = ((GetRValue(rgb) + (add)) * 256); \ \ \ if (((int)((GetGValue(rgb) + (add)) * 256)) > COLOR16_MAX) \ GField = COLOR16_MAX; \ else if (((int)((GetGValue(rgb) + (add)) * 256)) < 0) \ GField = 0; \ else \ GField = ((GetGValue(rgb) + (add)) * 256); \ \ \ if (((int)((GetBValue(rgb) + (add)) * 256)) > COLOR16_MAX) \ BField = COLOR16_MAX; \ else if (((int)((GetBValue(rgb) + (add)) * 256)) < 0) \ BField = 0; \ else \ BField = ((GetBValue(rgb) + (add)) * 256); \ \ \ }while(0) LPCSTR install_lock_sem_name = "libusb-win32-installer-{1298B356-F6E3-4455-9FEC-3932714AF49B}"; LPCSTR install_warning = "This will add libusb-win32 as a driver for all usb devices on the PC. " "This function is for development purposes only. Improper use of the " "libusb-win32 filter driver can cause devices to malfunction and in some " "cases complete system failure."; LPCSTR install_wait = "Press any key to exit.."; /* commands */ LPCWSTR paramcmd_list[] = { L"list", L"-l", L"l", 0 }; LPCWSTR paramcmd_install[] = { L"install", L"-i", L"i", 0 }; LPCWSTR paramcmd_uninstall[] = { L"uninstall", L"-u", L"u", 0 }; LPCWSTR paramcmd_help[] = { L"--help", L"help", L"-h", L"/?", L"-?", L"h", 0 }; /* switches */ LPCWSTR paramsw_all_classes[] = { L"--all-classes", L"-ac", 0 }; LPCWSTR paramsw_all_devices[] = { L"--all-devices", L"-ad", 0 }; LPCWSTR paramsw_device_classes[] = { L"--device-classes", L"-dc", 0 }; LPCWSTR paramsw_class[] = { L"--class=", L"-c=", 0 }; LPCWSTR paramsw_device_upper[] = { L"--device=", L"-d=", 0 }; LPCWSTR paramsw_device_upper_by_devinst[] = { L"--device-id=", L"-di=", 0 }; LPCWSTR paramsw_inf[] = { L"--inf=", L"-f=", 0 }; LPCWSTR paramsw_prompt[] = { L"--prompt=", L"-p=", L"-p", 0 }; LPCWSTR paramsw_wait[] = { L"--wait=", L"-w=", L"-w", 0 }; typedef struct _LONGGUID { unsigned int Data1; unsigned int Data2; unsigned int Data3; unsigned int Data4[ 8 ]; } LONGGUID; typedef struct _install_progress_context_t { HINSTANCE hInstance; HWND progress_hwnd; bool_t progress_window_ready; filter_context_t* filter_context; uintptr_t thread_id; int ret; DWORD progress_textlength; LONG progress_ind_ofs; bool_t stopped; int error_count; } install_progress_context_t; static install_progress_context_t g_install_progress_context; static bool_t usb_install_get_argument(LPWSTR param_value, LPCWSTR* out_param, LPCWSTR* out_value, LPCWSTR* param_names); void usb_install_report(filter_context_t* filter_context); void usage(void); /* riched32.dll */ #define INIT_RICHED32() if (riched32_dll == NULL) riched32_dll = LoadLibrary("riched32") /* msimg32.dll exports */ typedef BOOL (WINAPI * gradient_fill_t)(HDC, PTRIVERTEX, ULONG, PVOID, ULONG, ULONG); #define INIT_MSIMG32() if (msimg32_dll == NULL) msimg32_dll = LoadLibrary("msimg32") #define INIT_GRADIENTFILL() if (gradient_fill == NULL) do { \ INIT_MSIMG32(); if (msimg32_dll) gradient_fill = (gradient_fill_t) GetProcAddress(msimg32_dll, "GradientFill"); \ }while(0) /* shell32.dll exports */ typedef LPWSTR* (WINAPI * commandline_to_argvw_t)(LPCWSTR, int*); #define INIT_SHELL32() if (shell32_dll == NULL) shell32_dll = LoadLibrary("shell32") #define INIT_COMMANDLINE_TO_ARGVW() if (commandline_to_argvw == NULL) do { \ INIT_SHELL32(); if (shell32_dll) commandline_to_argvw = (commandline_to_argvw_t) GetProcAddress(shell32_dll, "CommandLineToArgvW"); \ }while(0) /* kernel32.dll exports */ typedef BOOL (WINAPI * is_wow64_process_t)(HANDLE, PBOOL); /* newdev.dll exports */ typedef BOOL (WINAPI * update_driver_for_plug_and_play_devices_t)(HWND, LPCSTR, LPCSTR, DWORD, PBOOL); typedef BOOL (WINAPI * rollback_driver_t)(HDEVINFO, PSP_DEVINFO_DATA, HWND, DWORD, PBOOL); typedef BOOL (WINAPI * uninstall_device_t)(HWND, HDEVINFO, PSP_DEVINFO_DATA, DWORD, PBOOL); /* setupapi.dll exports */ typedef BOOL (WINAPI * setup_copy_oem_inf_t)(PCSTR, PCSTR, DWORD, DWORD, PSTR, DWORD, PDWORD, PSTR*); /* advapi32.dll exports */ typedef SC_HANDLE (WINAPI * open_sc_manager_t)(LPCTSTR, LPCTSTR, DWORD); typedef SC_HANDLE (WINAPI * open_service_t)(SC_HANDLE, LPCTSTR, DWORD); typedef BOOL (WINAPI * change_service_config_t)(SC_HANDLE, DWORD, DWORD, DWORD, LPCTSTR, LPCTSTR, LPDWORD, LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR); typedef BOOL (WINAPI * close_service_handle_t)(SC_HANDLE); typedef SC_HANDLE (WINAPI * create_service_t)(SC_HANDLE, LPCTSTR, LPCTSTR, DWORD, DWORD, DWORD, DWORD, LPCTSTR, LPCTSTR, LPDWORD, LPCTSTR, LPCTSTR, LPCTSTR); typedef BOOL (WINAPI * delete_service_t)(SC_HANDLE); typedef BOOL (WINAPI * start_service_t)(SC_HANDLE, DWORD, LPCTSTR); typedef BOOL (WINAPI * query_service_status_t)(SC_HANDLE, LPSERVICE_STATUS); typedef BOOL (WINAPI * control_service_t)(SC_HANDLE, DWORD, LPSERVICE_STATUS); static HINSTANCE riched32_dll = NULL; static HINSTANCE shell32_dll = NULL; static HINSTANCE msimg32_dll = NULL; static HINSTANCE gdi32_dll = NULL; static HINSTANCE advapi32_dll = NULL; static commandline_to_argvw_t commandline_to_argvw = NULL; static gradient_fill_t gradient_fill = NULL; static open_sc_manager_t open_sc_manager = NULL; static open_service_t open_service = NULL; static change_service_config_t change_service_config = NULL; static close_service_handle_t close_service_handle = NULL; static create_service_t create_service = NULL; static delete_service_t delete_service = NULL; static start_service_t start_service = NULL; static query_service_status_t query_service_status = NULL; static control_service_t control_service = NULL; static bool_t usb_service_load_dll(void); static bool_t usb_service_free_dll(void); static bool_t usb_service_create(const char *name, const char *display_name, const char *binary_path, unsigned long type, unsigned long start_type); static bool_t usb_service_stop(const char *name); static bool_t usb_service_delete(const char *name); static bool_t usb_install_iswow64(void); static BOOL usb_install_admin_check(void); static BOOL usb_install_log_handler(enum USB_LOG_LEVEL level, const char* app_name, const char* prefix, const char* func, int app_prefix_func_end, char* message, int message_length); int usb_install_window(HWND hWnd, HINSTANCE instance, filter_context_t* filter_context); int usb_install_console(filter_context_t* filter_context); void usb_install_destroy_filter_context(filter_context_t** filter_context); bool_t usb_progress_context_create(install_progress_context_t* install_progress_context, HINSTANCE hInstance, filter_context_t* filter_context); void usb_progress_context_destroy(install_progress_context_t* install_progress_context); bool_t usb_progress_add_text(HWND hwnd, const char* message, COLORREF crNewColor, bool_t bold, bool_t italic); BOOL sem_create_lock(HANDLE* sem_handle_out, LPCSTR unique_name, LONG remaining, LONG max); BOOL sem_destroy_lock(HANDLE* sem_handle_in); BOOL sem_release_lock(HANDLE sem_handle); BOOL sem_try_lock(HANDLE sem_handle, DWORD dwMilliseconds); static void detect_version(void); // Detect Windows version /* * Windows versions */ enum windows_version { WINDOWS_UNDEFINED, WINDOWS_UNSUPPORTED, WINDOWS_2K, WINDOWS_XP, WINDOWS_2003_XP64, WINDOWS_VISTA, WINDOWS_7 }; enum windows_version windows_version = WINDOWS_UNDEFINED; #define GET_WINDOWS_VERSION do{ if (windows_version == WINDOWS_UNDEFINED) detect_version(); } while(0) static void detect_version(void) { OSVERSIONINFO os_version; memset(&os_version, 0, sizeof(OSVERSIONINFO)); os_version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); windows_version = WINDOWS_UNSUPPORTED; if ((GetVersionEx(&os_version) != 0) && (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT)) { if ((os_version.dwMajorVersion == 5) && (os_version.dwMinorVersion == 0)) { windows_version = WINDOWS_2K; } else if ((os_version.dwMajorVersion == 5) && (os_version.dwMinorVersion == 1)) { windows_version = WINDOWS_XP; } else if ((os_version.dwMajorVersion == 5) && (os_version.dwMinorVersion == 2)) { windows_version = WINDOWS_2003_XP64; } else if (os_version.dwMajorVersion >= 6) { if (os_version.dwBuildNumber < 7000) { windows_version = WINDOWS_VISTA; } else { windows_version = WINDOWS_7; } } } } BOOL sem_create_lock(HANDLE* sem_handle_out, LPCSTR unique_name, LONG remaining, LONG max) { SECURITY_ATTRIBUTES sem_attributes; memset(&sem_attributes, 0, sizeof(sem_attributes)); sem_attributes.nLength = sizeof(sem_attributes); sem_attributes.bInheritHandle = TRUE; *sem_handle_out = CreateSemaphoreA(&sem_attributes, remaining, max, unique_name); return *sem_handle_out != NULL; } BOOL sem_destroy_lock(HANDLE* sem_handle_in) { if (sem_handle_in && *sem_handle_in) { return CloseHandle(*sem_handle_in); *sem_handle_in = NULL; } return FALSE; } BOOL sem_release_lock(HANDLE sem_handle) { if (!sem_handle) return FALSE; return ReleaseSemaphore(sem_handle, 1, NULL); } BOOL sem_try_lock(HANDLE sem_handle, DWORD dwMilliseconds) { if (!sem_handle) return FALSE; return WaitForSingleObject(sem_handle, dwMilliseconds) == WAIT_OBJECT_0; } void CALLBACK usb_touch_inf_file_rundll(HWND wnd, HINSTANCE instance, LPSTR cmd_line, int cmd_show); void CALLBACK usb_install_service_np_rundll(HWND wnd, HINSTANCE instance, LPSTR cmd_line, int cmd_show) { usb_install_service_np(); } void CALLBACK usb_uninstall_service_np_rundll(HWND wnd, HINSTANCE instance, LPSTR cmd_line, int cmd_show) { usb_uninstall_service_np(); } void CALLBACK usb_install_driver_np_rundll(HWND wnd, HINSTANCE instance, LPSTR cmd_line, int cmd_show) { usb_install_driver_np(cmd_line); } int usb_install_service(filter_context_t* filter_context) { char display_name[MAX_PATH]; int ret = 0; const char* driver_name = LIBUSB_DRIVER_NAME_NT; /* stop devices that are handled by libusb's device driver */ usb_registry_stop_libusb_devices(); /* the old driver is unloaded now */ if (usb_registry_is_nt()) { memset(display_name, 0, sizeof(display_name)); /* create the Display Name */ _snprintf(display_name, sizeof(display_name) - 1, "libusb-win32 - Kernel Driver, Version %d.%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO, VERSION_NANO); /* create the kernel service */ USBMSG("creating %s service..\n", driver_name); if (!usb_service_create(driver_name, display_name, LIBUSB_DRIVER_PATH, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START)) { USBERR("failed creating service %s\n", driver_name); ret = -1; return ret; } } /* restart devices that are handled by libusb's device driver */ usb_registry_start_libusb_devices(); /* insert device filter drivers */ usb_registry_insert_device_filters(filter_context); /* insert class filter driver */ usb_registry_insert_class_filter(filter_context); if (filter_context->class_filters_modified) { /* restart the whole USB system so that the new drivers will be loaded */ usb_registry_restart_all_devices(); filter_context->class_filters_modified = FALSE; } return ret; } int usb_install_service_np(void) { return usb_install_npA(NULL, NULL, "i -dc", 0); } int usb_uninstall_service(filter_context_t* filter_context) { if (!usb_registry_is_nt()) return -1; /* older version of libusb used a system service, just remove it */ usb_service_stop(LIBUSB_OLD_SERVICE_NAME_NT); usb_service_delete(LIBUSB_OLD_SERVICE_NAME_NT); /* remove user specified device filters */ if (filter_context->device_filters || filter_context->remove_all_device_filters) { usb_registry_remove_device_filter(filter_context); } /* remove class filter driver */ usb_registry_remove_class_filter(filter_context); /* unload filter drivers */ if (filter_context->class_filters_modified) { usb_registry_restart_all_devices(); filter_context->class_filters_modified = FALSE; } return 0; } int usb_uninstall_service_np(void) { return usb_install_npA(NULL, NULL, "u -ac", 0); } BOOL usb_install_find_model_section(HINF inf_handle, PINFCONTEXT inf_context) { // find the .inf file's model-section. This is normally a [Devices] // section, but could be anything. char tmp[MAX_PATH]; char* model[8]; BOOL success = FALSE; DWORD field_index; // first find [Manufacturer]. The models are listeed in this section. if (SetupFindFirstLine(inf_handle, "Manufacturer", NULL, inf_context)) { memset(model, 0, sizeof(model)); for (field_index = 1; field_index < ( sizeof(model) / sizeof(model[0]) ); field_index++) { success = SetupGetStringField(inf_context, field_index, tmp, sizeof(tmp), NULL); if (!success) break; model[(field_index-1)] = _strdup(tmp); switch(field_index) { case 1: // The first field is the base model-section-name, "Devices" for example. USBDBG("model-section-name=%s\n", tmp); break; default: // These are the target OS field(s), "NT" or "NTX86" for example. USBDBG("target-os-version=%s\n", tmp); } } // if the base model-section-name was found.. if (field_index > 1) { // find the model-section field_index = 0; strcpy(tmp, model[0]); while (!(success = SetupFindFirstLine(inf_handle, tmp, NULL, inf_context))) { field_index++; if (!model[field_index]) break; sprintf(tmp, "%s.%s", model[0], model[field_index]); } } // these were allocated with _strdup above. for (field_index = 0; model[field_index] != NULL; field_index++) free(model[field_index]); // model-section-name or model-section not found if (!success) { USBERR0(".inf file does not contain a valid model-section-name\n"); } } else { USBERR0(".inf file does not contain a valid Manufacturer section\n"); } return success; } int usb_install_inf_np(const char *inf_file, bool_t remove_mode, bool_t copy_oem_inf_mode) { HDEVINFO dev_info; SP_DEVINFO_DATA dev_info_data; INFCONTEXT inf_context; HINF inf_handle; DWORD config_flags, problem, status; BOOL reboot; char inf_path[MAX_PATH]; char id[MAX_PATH]; char tmp_id[MAX_PATH]; char *p; int dev_index; HINSTANCE newdev_dll = NULL; HMODULE setupapi_dll = NULL; CONFIGRET cr; update_driver_for_plug_and_play_devices_t UpdateDriverForPlugAndPlayDevices; rollback_driver_t RollBackDriver; uninstall_device_t UninstallDevice; bool_t usb_reset_required = FALSE; int field_index; setup_copy_oem_inf_t SetupCopyOEMInf; if (usb_install_iswow64()) { USBERR0("This is a 64bit operating system and requires the 64bit " DISPLAY_NAME " application.\n"); return -1; } newdev_dll = LoadLibrary("newdev.dll"); if (!newdev_dll) { USBERR0("loading newdev.dll failed\n"); return -1; } UpdateDriverForPlugAndPlayDevices = (update_driver_for_plug_and_play_devices_t)GetProcAddress(newdev_dll, "UpdateDriverForPlugAndPlayDevicesA"); if (!UpdateDriverForPlugAndPlayDevices) { USBERR0("loading newdev.dll failed\n"); return -1; } UninstallDevice = (uninstall_device_t)GetProcAddress(newdev_dll, "DiUninstallDevice"); RollBackDriver = (rollback_driver_t)GetProcAddress(newdev_dll, "DiRollbackDriver"); setupapi_dll = GetModuleHandle("setupapi.dll"); if (!setupapi_dll) { USBERR0("loading setupapi.dll failed\n"); return -1; } SetupCopyOEMInf = (setup_copy_oem_inf_t)GetProcAddress(setupapi_dll, "SetupCopyOEMInfA"); if (!SetupCopyOEMInf) { USBERR0("loading setupapi.dll failed\n"); return -1; } dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA); /* retrieve the full .inf file path */ if (!GetFullPathName(inf_file, MAX_PATH, inf_path, NULL)) { USBERR(".inf file %s not found\n", inf_file); return -1; } /* open the .inf file */ inf_handle = SetupOpenInfFile(inf_path, NULL, INF_STYLE_WIN4, NULL); if (inf_handle == INVALID_HANDLE_VALUE) { USBERR("unable to open .inf file %s\n", inf_file); return -1; } if (!usb_install_find_model_section(inf_handle, &inf_context)) { SetupCloseInfFile(inf_handle); return -1; } do { /* get the device ID from the .inf file */ field_index = 2; while(SetupGetStringField(&inf_context, field_index++, id, sizeof(id), NULL)) { /* convert the string to lowercase */ strlwr(id); if (strncmp(id, "usb\\", 4) != 0) { USBERR("invalid hardware id %s\n", id); SetupCloseInfFile(inf_handle); return -1; } USBMSG("%s device %s..\n", remove_mode ? "removing" : "installing", id + 4); reboot = FALSE; if (!remove_mode) { if (copy_oem_inf_mode) { /* copy the .inf file to the system directory so that is will be found */ /* when new devices are plugged in */ if (SetupCopyOEMInf(inf_path, NULL, SPOST_PATH, 0, NULL, 0, NULL, NULL)) { USBDBG("SetupCopyOEMInf failed for %s\n", inf_path); } else { USBDBG(".inf file %s copied to system directory\n", inf_path); copy_oem_inf_mode = FALSE; } } /* force an update of all connected devices matching this ID */ UpdateDriverForPlugAndPlayDevices(NULL, id, inf_path, INSTALLFLAG_FORCE, &reboot); } /* now search the registry for device nodes representing currently */ /* unattached devices or remove devices depending on the mode */ /* get all USB device nodes from the registry, present and non-present */ /* devices */ dev_info = SetupDiGetClassDevs(NULL, "USB", NULL, DIGCF_ALLCLASSES); if (dev_info == INVALID_HANDLE_VALUE) { SetupCloseInfFile(inf_handle); return -1; } dev_index = 0; /* enumerate the device list to find all attached and unattached */ /* devices */ while (SetupDiEnumDeviceInfo(dev_info, dev_index, &dev_info_data)) { /* get the harware ID from the registry, this is a multi-zero string */ if (SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, SPDRP_HARDWAREID, NULL, (BYTE *)tmp_id, sizeof(tmp_id), NULL)) { /* check all possible IDs contained in that multi-zero string */ for (p = tmp_id; *p; p += (strlen(p) + 1)) { /* convert the string to lowercase */ strlwr(p); /* found a match? */ if (strstr(p, id)) { if (remove_mode) { if (RollBackDriver) { if (RollBackDriver(dev_info, &dev_info_data, NULL, 0, &reboot)) { break; } else { USBERR("failed RollBackDriver for device %s\n", p); } } if (UninstallDevice) { if (UninstallDevice(NULL, dev_info, &dev_info_data, 0, &reboot)) { usb_reset_required = TRUE; break; } else { USBERR("failed UninstallDevice for device %s\n", p); } } if (SetupDiRemoveDevice(dev_info, &dev_info_data)) { usb_reset_required = TRUE; break; } else { USBERR("failed RemoveDevice for device %s\n", p); } } else { cr = CM_Get_DevNode_Status(&status, &problem, dev_info_data.DevInst, 0); /* is this device disconnected? */ if (cr == CR_NO_SUCH_DEVINST) { /* found a device node that represents an unattached */ /* device */ if (SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, SPDRP_CONFIGFLAGS, NULL, (BYTE *)&config_flags, sizeof(config_flags), NULL)) { /* mark the device to be reinstalled the next time it is */ /* plugged in */ config_flags |= CONFIGFLAG_REINSTALL; /* write the property back to the registry */ SetupDiSetDeviceRegistryProperty(dev_info, &dev_info_data, SPDRP_CONFIGFLAGS, (BYTE *)&config_flags, sizeof(config_flags)); } } } /* a match was found, skip the rest */ break; } } } /* check the next device node */ dev_index++; } SetupDiDestroyDeviceInfoList(dev_info); /* get the next device ID from the .inf file */ } } while (SetupFindNextLine(&inf_context, &inf_context)); /* we are done, close the .inf file */ SetupCloseInfFile(inf_handle); if (usb_reset_required) { usb_registry_restart_all_devices(); } else if (!remove_mode) { usb_registry_stop_libusb_devices(); /* stop all libusb devices */ usb_registry_start_libusb_devices(); /* restart all libusb devices */ } return 0; } int usb_install_driver_np(const char *inf_file) { return usb_install_inf_np(inf_file, FALSE, TRUE); } bool_t usb_service_load_dll() { if (usb_registry_is_nt()) { advapi32_dll = LoadLibrary("advapi32.dll"); if (!advapi32_dll) { USBERR0("loading DLL advapi32.dll failed\n"); return FALSE; } open_sc_manager = (open_sc_manager_t) GetProcAddress(advapi32_dll, "OpenSCManagerA"); open_service = (open_service_t) GetProcAddress(advapi32_dll, "OpenServiceA"); change_service_config = (change_service_config_t) GetProcAddress(advapi32_dll, "ChangeServiceConfigA"); close_service_handle = (close_service_handle_t) GetProcAddress(advapi32_dll, "CloseServiceHandle"); create_service = (create_service_t) GetProcAddress(advapi32_dll, "CreateServiceA"); delete_service = (delete_service_t) GetProcAddress(advapi32_dll, "DeleteService"); start_service = (start_service_t) GetProcAddress(advapi32_dll, "StartServiceA"); query_service_status = (query_service_status_t) GetProcAddress(advapi32_dll, "QueryServiceStatus"); control_service = (control_service_t) GetProcAddress(advapi32_dll, "ControlService"); if (!open_sc_manager || !open_service || !change_service_config || !close_service_handle || !create_service || !delete_service || !start_service || !query_service_status || !control_service) { FreeLibrary(advapi32_dll); USBERR0("loading exported functions of advapi32.dll failed"); return FALSE; } } return TRUE; } bool_t usb_service_free_dll() { if (advapi32_dll) { FreeLibrary(advapi32_dll); } return TRUE; } bool_t usb_service_create(const char *name, const char *display_name, const char *binary_path, unsigned long type, unsigned long start_type) { SC_HANDLE scm = NULL; SC_HANDLE service = NULL; bool_t ret = FALSE; if (!usb_service_load_dll()) { return FALSE; } do { scm = open_sc_manager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS); if (!scm) { USBERR("opening service control " "manager failed: %s", usb_win_error_to_string()); break; } service = open_service(scm, name, SERVICE_ALL_ACCESS); if (service) { if (!change_service_config(service, type, start_type, SERVICE_ERROR_NORMAL, binary_path, NULL, NULL, NULL, NULL, NULL, display_name)) { USBERR("changing config of " "service '%s' failed: %s", name, usb_win_error_to_string()); break; } ret = TRUE; break; } if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) { service = create_service(scm, name, display_name, GENERIC_EXECUTE, type, start_type, SERVICE_ERROR_NORMAL, binary_path, NULL, NULL, NULL, NULL, NULL); if (!service) { USBERR("creating " "service '%s' failed: %s", name, usb_win_error_to_string()); } ret = TRUE; } } while (0); if (service) { close_service_handle(service); } if (scm) { close_service_handle(scm); } usb_service_free_dll(); return ret; } bool_t usb_service_stop(const char *name) { bool_t ret = FALSE; SC_HANDLE scm = NULL; SC_HANDLE service = NULL; SERVICE_STATUS status; if (!usb_service_load_dll()) { return FALSE; } USBMSG("stopping %s service..\n", name); do { scm = open_sc_manager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS); if (!scm) { USBERR("opening service control " "manager failed: %s", usb_win_error_to_string()); break; } service = open_service(scm, name, SERVICE_ALL_ACCESS); if (!service) { ret = TRUE; break; } if (!query_service_status(service, &status)) { USBERR("getting status of " "service '%s' failed: %s", name, usb_win_error_to_string()); break; } if (status.dwCurrentState == SERVICE_STOPPED) { ret = TRUE; break; } if (!control_service(service, SERVICE_CONTROL_STOP, &status)) { USBERR("stopping service '%s' failed: %s", name, usb_win_error_to_string()); break; } do { int wait = 0; if (!query_service_status(service, &status)) { USBERR("getting status of " "service '%s' failed: %s", name, usb_win_error_to_string()); break; } Sleep(500); wait += 500; if (wait > 20000) { USBERR("stopping " "service '%s' failed, timeout", name); ret = FALSE; break; } ret = TRUE; } while (status.dwCurrentState != SERVICE_STOPPED); } while (0); if (service) { close_service_handle(service); } if (scm) { close_service_handle(scm); } usb_service_free_dll(); return ret; } bool_t usb_service_delete(const char *name) { bool_t ret = FALSE; SC_HANDLE scm = NULL; SC_HANDLE service = NULL; if (!usb_service_load_dll()) { return FALSE; } USBMSG("deleting %s service..\n", name); do { scm = open_sc_manager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS); if (!scm) { USBERR("opening service control " "manager failed: %s", usb_win_error_to_string()); break; } service = open_service(scm, name, SERVICE_ALL_ACCESS); if (!service) { ret = TRUE; break; } if (!delete_service(service)) { USBERR("deleting " "service '%s' failed: %s", name, usb_win_error_to_string()); break; } ret = TRUE; } while (0); if (service) { close_service_handle(service); } if (scm) { close_service_handle(scm); } usb_service_free_dll(); return ret; } void CALLBACK usb_touch_inf_file_np_rundll(HWND wnd, HINSTANCE instance, LPSTR cmd_line, int cmd_show) { usb_touch_inf_file_np(cmd_line); } int usb_touch_inf_file_np(const char *inf_file) { const char inf_comment[] = ";added by libusb to break this file's digital " "signature"; const wchar_t inf_comment_uni[] = L";added by libusb to break this file's " L"digital signature"; char buf[1024]; wchar_t wbuf[1024]; int found = 0; OSVERSIONINFO version; FILE *f; version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (!GetVersionEx(&version)) return -1; /* XP system */ if ((version.dwMajorVersion == 5) && (version.dwMinorVersion >= 1)) { f = fopen(inf_file, "rb"); if (!f) return -1; while (fgetws(wbuf, sizeof(wbuf) / 2, f)) { if (wcsstr(wbuf, inf_comment_uni)) { found = 1; break; } } fclose(f); if (!found) { f = fopen(inf_file, "ab"); /* fputwc(0x000d, f); */ /* fputwc(0x000d, f); */ fputws(inf_comment_uni, f); fclose(f); } } else { f = fopen(inf_file, "r"); if (!f) return -1; while (fgets(buf, sizeof(buf), f)) { if (strstr(buf, inf_comment)) { found = 1; break; } } fclose(f); if (!found) { f = fopen(inf_file, "a"); fputs("\n", f); fputs(inf_comment, f); fputs("\n", f); fclose(f); } } return 0; } int usb_install_needs_restart_np(void) { HDEVINFO dev_info; SP_DEVINFO_DATA dev_info_data; int dev_index = 0; SP_DEVINSTALL_PARAMS install_params; int ret = FALSE; dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA); dev_info = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT); SetEnvironmentVariable("LIBUSB_NEEDS_REBOOT", "1"); if (dev_info == INVALID_HANDLE_VALUE) { USBERR0("getting device info set failed\n"); return ret; } while (SetupDiEnumDeviceInfo(dev_info, dev_index, &dev_info_data)) { memset(&install_params, 0, sizeof(SP_PROPCHANGE_PARAMS)); install_params.cbSize = sizeof(SP_DEVINSTALL_PARAMS); if (SetupDiGetDeviceInstallParams(dev_info, &dev_info_data, &install_params)) { if (install_params.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT)) { USBMSG0("restart needed\n"); ret = TRUE; } } dev_index++; } SetupDiDestroyDeviceInfoList(dev_info); return ret; } bool_t usb_install_parse_filter_context(filter_context_t* filter_context, LPCWSTR cmd_line, int arg_start, int* arg_cnt) { #define GET_ARG(Params) usb_install_get_argument(argv[arg_pos], &arg_param, &arg_value, Params) LONGGUID class_guid; int arg_pos; LPWSTR* argv = NULL; LPCWSTR arg_param, arg_value; bool_t success = TRUE; size_t length; char tmp[MAX_PATH+1]; filter_device_t* found_device; filter_class_t* found_class; filter_file_t* found_inf; *arg_cnt = 0; INIT_COMMANDLINE_TO_ARGVW(); if (!commandline_to_argvw) { USBERR("failed CommandLineToArgvW:%X", GetLastError()); success = FALSE; goto Done; } if (!(argv = commandline_to_argvw(cmd_line, arg_cnt))) { USBERR("failed CommandLineToArgvW:%X", GetLastError()); success = FALSE; goto Done; } if (*arg_cnt <= arg_start) { success = FALSE; goto Done; } for(arg_pos = arg_start; arg_pos < *arg_cnt; arg_pos++) { if (GET_ARG(paramcmd_help)) { filter_context->show_help_only = TRUE; break; } else if (GET_ARG(paramcmd_list)) { if (filter_context->filter_mode) { USBERR("multiple commands not allowed: %ls\n", argv[arg_pos]); success = FALSE; break; } filter_context->filter_mode = FM_LIST; } else if (GET_ARG(paramcmd_install)) { if (filter_context->filter_mode) { USBERR("multiple commands not allowed: %ls\n", argv[arg_pos]); success = FALSE; break; } filter_context->filter_mode = FM_INSTALL; } else if (GET_ARG(paramcmd_uninstall)) { if (filter_context->filter_mode) { USBERR("multiple commands not allowed: %ls\n", argv[arg_pos]); success = FALSE; break; } filter_context->filter_mode = FM_REMOVE; } else if (GET_ARG(paramsw_all_classes)) { filter_context->switches.add_all_classes = TRUE; } else if (GET_ARG(paramsw_device_classes)) { filter_context->switches.add_device_classes = TRUE; } else if (GET_ARG(paramsw_all_devices)) { filter_context->remove_all_device_filters = TRUE; } else if ( GET_ARG(paramsw_device_upper) || GET_ARG(paramsw_device_upper_by_devinst) ) { length = wcstombs(tmp, arg_value, MAX_PATH); if (length < 1) { success = FALSE; USBERR("invalid argument %ls\n", argv[arg_pos]); break; } tmp[length] = 0; if (GET_ARG(paramsw_device_upper)) { usb_registry_add_filter_device_keys(&filter_context->device_filters, "", tmp, "", "", "", "", &found_device); } else if (GET_ARG(paramsw_device_upper_by_devinst)) { usb_registry_add_filter_device_keys(&filter_context->device_filters, tmp, "", "", "", "", "", &found_device); } // upper device filter if (!found_device) { success = FALSE; USBERR("failed adding device upper filter key %ls\n", argv[arg_pos]); break; } else { found_device->action |= FT_DEVICE_UPPERFILTER; } } else if (GET_ARG(paramsw_class)) { memset(tmp, 0, sizeof(tmp)); length = wcstombs(tmp, arg_value, MAX_PATH); if (length < 1) { success = FALSE; USBERR("invalid argument %ls\n", argv[arg_pos]); break; } tmp[length] = 0; if ((length != 38 || tmp[0] != '{' || tmp[length-1] != '}') || sscanf(tmp, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", &class_guid.Data1, &class_guid.Data2, &class_guid.Data3, &class_guid.Data4[0], &class_guid.Data4[1], &class_guid.Data4[2], &class_guid.Data4[3], &class_guid.Data4[4], &class_guid.Data4[5], &class_guid.Data4[6], &class_guid.Data4[7]) != 11) { // assume arg_value is a class name success = usb_registry_add_class_key(&filter_context->class_filters, "", tmp, "", &found_class, FALSE); if (!success) { USBERR("failed adding class name at argument %ls\n", argv[arg_pos]); break; } } else { sprintf(tmp, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", class_guid.Data1, class_guid.Data2, class_guid.Data3, class_guid.Data4[0], class_guid.Data4[1], class_guid.Data4[2], class_guid.Data4[3], class_guid.Data4[4], class_guid.Data4[5], class_guid.Data4[6], class_guid.Data4[7]); // assume arg_value is a class guid success = usb_registry_add_usb_class_key(filter_context, tmp); if (!success) { USBERR("failed adding class guid at argument %ls\n", argv[arg_pos]); break; } } } else if (GET_ARG(paramsw_inf)) { memset(tmp, 0, sizeof(tmp)); length = wcstombs(tmp, arg_value, MAX_PATH); if (length < 1) { success = FALSE; USBERR("invalid argument %ls\n", argv[arg_pos]); break; } tmp[length] = 0; usb_registry_add_filter_file_keys(&filter_context->inf_files, tmp, &found_inf); if (!found_inf) { success = FALSE; USBERR("failed adding inf %ls\n", argv[arg_pos]); break; } } else if (GET_ARG(paramsw_prompt)) { if (arg_value && wcslen(arg_value)) { length = wcstombs(NULL, arg_value, 4096); if (length < 1) { success = FALSE; USBERR("failed wcstombs %ls\n", argv[arg_pos]); break; } length+=3; filter_context->prompt_string = (char*)malloc(length); length = wcstombs(filter_context->prompt_string, arg_value, length); if (length < 1) { success = FALSE; USBERR("failed wcstombs %ls\n", argv[arg_pos]); break; } filter_context->prompt_string[length] = 0; } else { filter_context->prompt_string = (char*)malloc(strlen(install_warning)+1); strcpy(filter_context->prompt_string, install_warning); } } else if (GET_ARG(paramsw_wait)) { if (arg_value && wcslen(arg_value)) { length = wcstombs(NULL, arg_value, 4096); if (length < 1) { success = FALSE; USBERR("failed wcstombs %ls\n", argv[arg_pos]); break; } length+=3; filter_context->wait_string = (char*)malloc(length); length = wcstombs(filter_context->wait_string, arg_value, length); if (length < 1) { success = FALSE; USBERR("failed wcstombs %ls\n", argv[arg_pos]); break; } filter_context->wait_string[length] = 0; } else { filter_context->wait_string = (char*)malloc(strlen(install_wait)+1); strcpy(filter_context->wait_string, install_wait); } } else { USBERR("invalid argument %ls\n", argv[arg_pos]); success = FALSE; break; } } Done: if (success && filter_context->class_filters) { // find class keys (fill in the blanks) usb_registry_lookup_class_keys_by_name(&filter_context->class_filters); } if (argv) { LocalFree(argv); } return success; } int usb_install_npA(HWND hwnd, HINSTANCE instance, LPCSTR cmd_line, int starg_arg) { WCHAR* cmd_line_w; size_t length; int ret; if (!cmd_line || !strlen(cmd_line)) { return -1; } length = (strlen(cmd_line) * sizeof(WCHAR)) + sizeof(WCHAR); cmd_line_w = malloc(length); if (!cmd_line_w) return -1; memset(cmd_line_w, 0, length); if ((length = mbstowcs(cmd_line_w, cmd_line, length / sizeof(WCHAR))) < 1) { free(cmd_line_w); return -1; } cmd_line_w[length] = 0; ret = usb_install_npW(hwnd, instance, cmd_line_w, starg_arg); free(cmd_line_w); return ret; } int usb_install_npW(HWND hwnd, HINSTANCE instance, LPCWSTR cmd_line_w, int starg_arg) { filter_context_t* filter_context; int ret = ERROR_SUCCESS; HANDLE sem_handle = NULL; int arg_cnt; // create a named semaphore if (!sem_create_lock(&sem_handle, install_lock_sem_name, 1, 1)) return -1; // lock the semaphore if (!sem_try_lock(sem_handle, 0)) { sem_destroy_lock(&sem_handle); return -1; } // use the log handler to report progress status if (!usb_log_get_handler()) { usb_log_set_handler(usb_install_log_handler); usb_log_set_level(LOG_DEBUG); } else { ret = -1; goto Done; } // allocate the filter context filter_context = (filter_context_t*)malloc(sizeof(filter_context_t)); if (!filter_context) { USBERR0("memory allocation failure\n"); ret = -1; goto Done; } memset(filter_context, 0, sizeof(filter_context_t)); // Fill the filter context from the command line arguments. if (!(usb_install_parse_filter_context(filter_context, cmd_line_w, starg_arg, &arg_cnt))) { if (arg_cnt <= starg_arg) { usage(); } ret = -1; if (hwnd) { MessageBoxA(hwnd, usb_strerror(), DISPLAY_NAME " error", MB_OK | MB_ICONERROR); } goto Done; } if (filter_context->show_help_only) { usage(); ret = -1; goto Done; } if (hwnd && instance) { // Using windowed install mode. if (safe_strlen(filter_context->prompt_string)) { const char* msg_title = DISPLAY_NAME; if (MessageBox(hwnd, filter_context->prompt_string, msg_title, MB_OKCANCEL) != IDOK) { ret = -1; } } if (ret == ERROR_SUCCESS) { ret = usb_install_window(hwnd, instance, filter_context); } } else { // Using console install mode. if (ret == ERROR_SUCCESS) { ret = usb_install_console(filter_context); if (safe_strlen(filter_context->wait_string)) { printf(filter_context->wait_string); _getch(); } } } Done: // Free the filter context. usb_install_destroy_filter_context(&filter_context); // Restore the default log handler. usb_log_set_handler(NULL); // Close (release) the semaphore lock. sem_destroy_lock(&sem_handle); return ret; } void CALLBACK usb_install_np_rundll(HWND wnd, HINSTANCE instance, LPSTR cmd_line, int cmd_show) { usb_install_npA(wnd, instance, cmd_line, 0); } void usb_install_destroy_filter_context(filter_context_t** filter_context) { filter_context_t* p = *filter_context; if (filter_context && p) { usb_registry_free_class_keys(&p->class_filters); usb_registry_free_filter_devices(&p->device_filters); usb_registry_free_filter_files(&p->inf_files); safe_free(p->prompt_string); safe_free(p->wait_string); free(p); *filter_context = NULL; } } static void center_dialog(HWND hWndToCenterOn, HWND hWndSubDialog) { RECT rectToCenterOn; RECT rectSubDialog; int xLeft, yTop; if (hWndSubDialog == NULL) return; if (hWndToCenterOn == NULL) hWndToCenterOn = GetDesktopWindow(); GetWindowRect(hWndToCenterOn, &rectToCenterOn); GetWindowRect(hWndSubDialog, &rectSubDialog); if ((rectToCenterOn.right - rectToCenterOn.left) < (rectSubDialog.right - rectSubDialog.left) || rectToCenterOn.left < 0 || rectToCenterOn.top < 0 || (rectToCenterOn.bottom - rectToCenterOn.top) < (rectSubDialog.bottom - rectSubDialog.top)) { hWndToCenterOn = GetDesktopWindow(); GetWindowRect(hWndToCenterOn, &rectToCenterOn); } xLeft = (rectToCenterOn.left + rectToCenterOn.right) / 2 - (rectSubDialog.right - rectSubDialog.left) / 2; yTop = (rectToCenterOn.top + rectToCenterOn.bottom) / 2 - (rectSubDialog.bottom - rectSubDialog.top) / 2; // Move the window to the correct coordinates with SetWindowPos() SetWindowPos(hWndSubDialog, HWND_TOP, xLeft, yTop, -1, -1, SWP_NOSIZE | SWP_SHOWWINDOW); } static BOOL usb_install_log_handler(enum USB_LOG_LEVEL level, const char* app_name, const char* prefix, const char* func, int app_prefix_func_end, char* message, int message_length) { HANDLE std_handle; DWORD req_length; HWND hDlg, hWnd; if (g_install_progress_context.progress_window_ready && g_install_progress_context.progress_hwnd) { hDlg = g_install_progress_context.progress_hwnd; hWnd = GetDlgItem(hDlg, IDC_PROGRESS_TEXT); if (!hWnd) { // use default log handler return FALSE; } //strip the app, func, and prefix message += app_prefix_func_end; message_length -= app_prefix_func_end; // trim right '\n' while(message_length > 0 && message[message_length-1] == '\n') { message[message_length-1] = 0; message_length--; } // trim left '\n' while(message_length > 0 && message[0] == '\n') { message++; message_length--; } switch(level & LOG_LEVEL_MASK) { case LOG_ERROR: g_install_progress_context.error_count++; usb_progress_add_text(hWnd, message, COLOR_RED, FALSE, FALSE); break; case LOG_WARNING: usb_progress_add_text(hWnd, message, COLOR_ORANGE, FALSE, TRUE); break; default: usb_progress_add_text(hWnd, message, GetSysColor(COLOR_BTNTEXT), FALSE, FALSE); break; } return TRUE; } else if (((std_handle = GetStdHandle(STD_OUTPUT_HANDLE)) != INVALID_HANDLE_VALUE) && std_handle) { if ((level & LOG_LEVEL_MASK) == LOG_ERROR) std_handle = GetStdHandle(STD_ERROR_HANDLE); if (std_handle && std_handle != INVALID_HANDLE_VALUE) { WriteFile(std_handle, message, (DWORD)message_length, &req_length, NULL); return TRUE; } // use default log handler return FALSE; } // use default log handler return FALSE; } int usb_install_console(filter_context_t* filter_context) { filter_file_t* filter_file; int ret = 0; if (!usb_install_admin_check()) { USBERR0(DISPLAY_NAME " requires administrative privileges.\n"); ret = -1; goto Done; } if (usb_install_iswow64()) { USBERR0("This is a 64bit operating system and requires the 64bit " DISPLAY_NAME " application.\n"); ret = -1; goto Done; } if (!filter_context->filter_mode) { USBERR("command not specified. Use %ls, %ls, or %ls.\n", paramcmd_list[0], paramcmd_install[0], paramcmd_uninstall[0]); ret = -1; goto Done; } /* only add the default class keys if there is nothing else to do. */ if (filter_context->class_filters || filter_context->device_filters || filter_context->inf_files || filter_context->switches.add_all_classes || filter_context->switches.add_device_classes || filter_context->remove_all_device_filters) { filter_context->switches.add_default_classes = FALSE; } else { filter_context->switches.add_default_classes = TRUE; } USBRAWMSG("\n" DISPLAY_NAME " (v%u.%u.%u.%u)\n", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO, VERSION_NANO); do { bool_t refresh_only; // if the user specified class keys and the -ac or -dc switch, remove the users keys. if (filter_context->class_filters && (filter_context->switches.add_all_classes || filter_context->switches.add_device_classes)) { USBWRN0("-ac and -dc switches are incompatible with -c= switch\n"); usb_registry_free_class_keys(&filter_context->class_filters); } if (filter_context->filter_mode == FM_REMOVE) { if (filter_context->switches.switches_value || filter_context->class_filters || filter_context->device_filters) { if (filter_context->switches.add_all_classes || filter_context->switches.add_device_classes) refresh_only = FALSE; else refresh_only = TRUE; if (!usb_registry_get_usb_class_keys(filter_context, refresh_only)) { ret = -1; break; } if (!usb_registry_get_all_class_keys(filter_context, refresh_only)) { ret = -1; break; } } // uninstall class & device filters ret = usb_uninstall_service(filter_context); if (ret < 0) { break; } // rollback/uninstall devices using inf files filter_file = filter_context->inf_files; while (filter_file) { USBMSG("uninstalling inf %s..\n", filter_file->name); if (usb_install_inf_np(filter_file->name, TRUE, TRUE) < 0) { ret = -1; break; } filter_file = filter_file->next; } if (ret == -1) break; } else if (filter_context->filter_mode == FM_INSTALL) { if (filter_context->remove_all_device_filters) { USBWRN("invalid switch for install operation (%ls)\n",paramsw_all_devices[0]); } if (filter_context->switches.switches_value || filter_context->class_filters || filter_context->device_filters) { if (filter_context->switches.add_all_classes || filter_context->switches.add_device_classes) refresh_only = FALSE; else refresh_only = TRUE; if (!usb_registry_get_usb_class_keys(filter_context, refresh_only)) { ret = -1; break; } if (!usb_registry_get_all_class_keys(filter_context, refresh_only)) { ret = -1; break; } ret = usb_install_service(filter_context); if (ret < 0) { break; } } filter_file = filter_context->inf_files; while (filter_file) { USBMSG("installing inf %s..\n", filter_file->name); if (usb_install_inf_np(filter_file->name, FALSE, TRUE) < 0) { ret = -1; break; } filter_file = filter_file->next; } if (ret == -1) break; } else if (filter_context->filter_mode == FM_LIST) { if (!usb_registry_get_usb_class_keys(filter_context, TRUE)) { ret = -1; break; } if (!usb_registry_get_all_class_keys(filter_context, TRUE)) { ret = -1; break; } usb_install_report(filter_context); } else { USBERR0("unknown filter_mode command\n"); } }while(FALSE); Done: return ret; } void __cdecl usb_progress_thread(void* param) { install_progress_context_t* context = (install_progress_context_t*)param; context->ret = usb_install_console(context->filter_context); PostMessage(context->progress_hwnd, UM_PROGRESS_STOP, (WPARAM)context->ret, 0); _endthread(); } bool_t usb_progress_add_text(HWND hwnd, const char* message, COLORREF crNewColor, bool_t bold, bool_t italic) { static const char crlf[] = "\r\n"; CHARFORMAT cf; char* text = (char *)malloc(strlen(message) + sizeof(crlf)); int iTotalTextLength = GetWindowTextLength(hwnd); if (!text) { printf("%s\n", "memory allocation failure"); return FALSE; } if (iTotalTextLength > 0) { strcpy(text, crlf); strcpy(&text[strlen(crlf)], message); } else { strcpy(text, message); } // set the selection to the end SendMessage(hwnd, EM_SETSEL, (WPARAM)(int)iTotalTextLength, (LPARAM)(int)iTotalTextLength); // set the color cf.cbSize = sizeof(CHARFORMAT); cf.dwMask = CFM_COLOR | CFM_UNDERLINE | CFM_BOLD; cf.dwEffects = (unsigned long)~(CFE_AUTOCOLOR | CFE_UNDERLINE | CFE_BOLD); cf.crTextColor = crNewColor; if (bold) cf.dwEffects |= CFE_BOLD; if (italic) cf.dwEffects |= CFE_ITALIC; SendMessage(hwnd, EM_SETCHARFORMAT, (WPARAM)(UINT)SCF_SELECTION, (LPARAM)&cf); // set the text SendMessage(hwnd, EM_REPLACESEL, (WPARAM)(BOOL)FALSE, (LPARAM)(LPCSTR)text); free(text); // keep it scrolled to the bottom SendMessage(hwnd, WM_VSCROLL, (WPARAM)LOWORD(SB_BOTTOM), (LPARAM)0); return TRUE; } bool_t usb_progress_size(HWND hDlg) { RECT rect_client, rect_text, rect_bar; int pad_x = GetSystemMetrics(SM_CXEDGE) * 2; int pad_y = GetSystemMetrics(SM_CYEDGE) * 2; GetClientRect(hDlg, &rect_client); // adjust the bar label rect_bar = rect_client; rect_bar.top = rect_bar.bottom - 4; OffsetRect(&rect_bar, 0, -pad_y); InflateRect(&rect_bar, -pad_x, 0); // adjust the textbox rect_text.top = pad_y; rect_text.left = pad_x; rect_text.bottom = rect_bar.top - pad_y; rect_text.right = rect_client.right - pad_y; // resize the progress textbox MOVE_CONTROL(hDlg, IDC_PROGRESS_TEXT, rect_text, TRUE, FALSE); // resize the bar label MOVE_CONTROL(hDlg, IDC_PROGRESS_BAR, rect_bar, TRUE, FALSE); // keep it scrolled to the bottom SendMessage(GetDlgItem(hDlg, IDC_PROGRESS_TEXT), WM_VSCROLL, (WPARAM)LOWORD(SB_BOTTOM), (LPARAM)0); // repaint // InvalidateRect(hDlg, NULL, TRUE); // PostMessageA(hDlg, WM_PAINT, (WPARAM)NULL, (LPARAM)0); return TRUE; } bool_t usb_progress_init_children(install_progress_context_t* progress_context, HWND hDlg) { HWND hWnd; CHARFORMAT cf; DWORD style; style = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | ES_MULTILINE | ES_LEFT | ES_READONLY | ES_AUTOVSCROLL | ES_AUTOHSCROLL; if (progress_context->filter_context->filter_mode == FM_LIST) { style |= WS_VSCROLL | WS_HSCROLL; } // create rich textbox hWnd = CreateWindowEx(WS_EX_STATICEDGE, RICHEDIT_CLASS, NULL, style, 5, 5, 5, 5, hDlg, (HMENU)((UINT_PTR)IDC_PROGRESS_TEXT), g_install_progress_context.hInstance, NULL); memset(&cf, 0 , sizeof(cf)); cf.cbSize = sizeof(cf); // set rich textbox font and size cf.dwMask = CFM_FACE | CFM_SIZE; cf.yHeight = 170; strcpy(cf.szFaceName, "Tahoma"); SendMessage(hWnd, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf); SendMessage(hWnd, EM_SETWORDBREAKPROCEX, (WPARAM)0, (LPARAM)NULL); SendMessage(hWnd, EM_SETBKGNDCOLOR, (WPARAM)0, (LPARAM)GetSysColor(COLOR_BTNFACE)); // create custom progress bar hWnd = CreateWindowA("STATIC", NULL, WS_CHILD | WS_VISIBLE | SS_OWNERDRAW, 5, 5, 5, 5, hDlg, (HMENU)((UINT_PTR)IDC_PROGRESS_BAR), g_install_progress_context.hInstance, NULL); usb_progress_size(hDlg); return TRUE; } LRESULT CALLBACK usb_progress_wndproc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { #define PROGRESS_BLOCK_SIZE 66 CREATESTRUCT* cs; DRAWITEMSTRUCT* ds; int wmId, wmEvent; MINMAXINFO* minmaxinfo; HWND hwnd; switch (message) { case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); if (g_install_progress_context.stopped && wmId == IDCLOSE && wmEvent == BN_CLICKED) { PostMessageA(hDlg, UM_PROGRESS_STOP, 0, 0); } break; case WM_CREATE: cs = (LPCREATESTRUCT)lParam; g_install_progress_context.progress_hwnd = hDlg; g_install_progress_context.hInstance = cs->hInstance; usb_progress_init_children(&g_install_progress_context, hDlg); center_dialog(GetParent(hDlg), hDlg); if (InterlockedIncrement(&g_install_progress_context.progress_ind_ofs) == 1) { PostMessage(hDlg, UM_PROGRESS_START, 0, 0); g_install_progress_context.progress_window_ready = TRUE; // Send a WM_TIMER message every 1/10th second SetTimer(hDlg, 1, 10, NULL); } return (INT_PTR)0; case WM_TIMER: InterlockedIncrement(&g_install_progress_context.progress_ind_ofs); InvalidateRect(GetDlgItem(hDlg, IDC_PROGRESS_BAR), NULL, FALSE); return (INT_PTR)TRUE; break; case UM_PROGRESS_START: g_install_progress_context.stopped = FALSE; if (g_install_progress_context.thread_id == 0) { // Using a thread prevents application freezout on security warning if ( (hwnd = GetDlgItem(hDlg, IDOK)) != NULL) EnableWindow(hwnd, FALSE); SetWindowTextA(hDlg, DISPLAY_RUNNING); g_install_progress_context.error_count = 0; g_install_progress_context.thread_id = _beginthread(usb_progress_thread, 0, &g_install_progress_context); if (g_install_progress_context.thread_id != -1L) { return (INT_PTR)TRUE; } } // Fall through and return an error wParam = (WPARAM) - 1; case UM_PROGRESS_STOP: if (!g_install_progress_context.stopped) { g_install_progress_context.stopped = TRUE; if ( (hwnd = GetDlgItem(hDlg, IDC_PROGRESS_TEXT)) != NULL) { if (g_install_progress_context.error_count != 0) { usb_progress_add_text(hwnd,"", COLOR_RED, TRUE, FALSE); usb_progress_add_text(hwnd, "Operation completed with errors!", COLOR_RED, TRUE, FALSE); } else { usb_progress_add_text(hwnd, "Operation completed successfuly.", GetSysColor(COLOR_BTNTEXT), TRUE, FALSE); } } // enable the close button SetWindowLongPtr(hDlg, GWL_STYLE, (GetWindowLongPtr(hDlg, GWL_STYLE) | WS_SYSMENU)); // enable ok button if ( (hwnd = GetDlgItem(hDlg, IDOK)) != NULL) EnableWindow(hwnd, TRUE); if (g_install_progress_context.error_count == 0) { SetWindowTextA(hDlg, DISPLAY_DONE); } else { SetWindowTextA(hDlg, DISPLAY_DONE_WITH_ERRORS); } if (safe_strlen(g_install_progress_context.filter_context->wait_string)) { const char* wait_string = g_install_progress_context.filter_context->wait_string; if (strcmp(install_wait, wait_string) == 0) { wait_string = "Click the [X] close button to exit."; } usb_progress_add_text(GetDlgItem(hDlg, IDC_PROGRESS_TEXT), wait_string, GetSysColor(COLOR_BTNTEXT), TRUE, FALSE); } else if (g_install_progress_context.error_count == 0) { PostQuitMessage((int)wParam); DestroyWindow(hDlg); return (INT_PTR)TRUE; } // redraw everything RedrawWindow(hDlg, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INTERNALPAINT | RDW_INVALIDATE); } else { PostQuitMessage((int)wParam); DestroyWindow(hDlg); } return (INT_PTR)TRUE; case WM_CLOSE: if (!g_install_progress_context.stopped) { // prevent closure using Alt-F4 while running return (INT_PTR)TRUE; } PostQuitMessage((int)wParam); DestroyWindow(hDlg); break; case WM_DESTROY: // close application g_install_progress_context.progress_window_ready = FALSE; return (INT_PTR)FALSE; case WM_SIZE: if (wParam == SIZE_RESTORED) { usb_progress_size(hDlg); } break; case WM_GETMINMAXINFO: minmaxinfo = (MINMAXINFO*)lParam; if (minmaxinfo != NULL) { // set min extent minmaxinfo->ptMinTrackSize.x = 320; minmaxinfo->ptMinTrackSize.y = 140; // set max extent minmaxinfo->ptMaxTrackSize.x = 800; minmaxinfo->ptMaxTrackSize.y = 600; } break; case WM_DRAWITEM: wmId = (int)wParam; switch (wmId) { case IDC_PROGRESS_BAR: ds = (DRAWITEMSTRUCT*)lParam; if ( (ds != NULL) ) { TRIVERTEX vertex[4] ; GRADIENT_RECT gRect; COLORREF clr, clr2; USHORT r, g, b; WORD offset_pos; DWORD offset; double actual_offset; HDC hdc; HBITMAP memBM; RECT rect; rect = ds->rcItem; hdc = CreateCompatibleDC(ds->hDC); memBM = CreateCompatibleBitmap (ds->hDC, rect.right - rect.left, rect.bottom - rect.top ); SelectObject(hdc, memBM); DrawEdge(hdc, &rect, EDGE_ETCHED, BF_RECT); InflateRect(&rect, -1, -1); if (g_install_progress_context.stopped) { if (g_install_progress_context.error_count) { HBRUSH hbr_fill = CreateSolidBrush(COLOR_LTRED); FillRect(hdc, &rect, hbr_fill); DeleteObject(hbr_fill); } else { FillRect(hdc, &rect, GetSysColorBrush(COLOR_INACTIVEBORDER)); } // Copy back buffer into screen device context for display. BitBlt(ds->hDC, ds->rcItem.left, ds->rcItem.top, ds->rcItem.right, ds->rcItem.bottom, hdc, 0, 0, SRCCOPY); DeleteDC(hdc); // Free resources. DeleteObject(memBM); // Free resources. return (INT_PTR)TRUE; } offset = (DWORD)g_install_progress_context.progress_ind_ofs; clr = GetSysColor(COLOR_ACTIVECAPTION); clr2 = GetSysColor(COLOR_WINDOW); if (offset & 0x40) offset_pos = (WORD)(((64 - (offset & 63)))); else offset_pos = (WORD)(offset & 63); actual_offset = offset_pos; actual_offset /= 64.0; offset_pos = (WORD)(actual_offset * rect.right); RBG_TO_C16(clr2, 0, r, g, b); TRIVERTEX_FILL(&vertex[0], rect.left, rect.top, r, g, b, 0x0000); RBG_TO_C16(clr, 0, r, g, b); TRIVERTEX_FILL(&vertex[1], offset_pos, rect.bottom, r, g, b, 0x0000); RBG_TO_C16(clr, 0, r, g, b); TRIVERTEX_FILL(&vertex[2], offset_pos, rect.top, r, g, b, 0x0000); RBG_TO_C16(clr2, 0, r, g, b); TRIVERTEX_FILL(&vertex[3], rect.right, rect.bottom, r, g, b, 0x0000); // Create a GRADIENT_RECT structure that // references the TRIVERTEX vertices. gRect.UpperLeft = 0; gRect.LowerRight = 1; INIT_GRADIENTFILL(); if (gradient_fill) { // Draw a shaded rectangle. gradient_fill(hdc, &vertex[0], 2, &gRect, 1, GRADIENT_FILL_RECT_H); gradient_fill(hdc, &vertex[2], 2, &gRect, 1, GRADIENT_FILL_RECT_H); // Copy back buffer into screen device context for display. BitBlt(ds->hDC, ds->rcItem.left, ds->rcItem.top, ds->rcItem.right, ds->rcItem.bottom, hdc, 0, 0, SRCCOPY); DeleteDC(hdc); // Free resources. DeleteObject(memBM); // Free resources. return (INT_PTR)TRUE; } else { // fallback for no GradientFill api FillRect(hdc, &rect, GetSysColorBrush(COLOR_ACTIVECAPTION)); DeleteDC(hdc); // Free resources. DeleteObject(memBM); // Free resources. return (INT_PTR)TRUE; } } break; } break; } return DefWindowProc(hDlg, message, wParam, lParam); } // initialize progress context bool_t usb_progress_context_create(install_progress_context_t* install_progress_context, HINSTANCE hInstance, filter_context_t* filter_context) { memset(install_progress_context, 0, sizeof(*install_progress_context)); install_progress_context->hInstance = hInstance; install_progress_context->filter_context = filter_context; return TRUE; } // deinitialize progress context void usb_progress_context_destroy(install_progress_context_t* install_progress_context) { memset(install_progress_context, 0, sizeof(*install_progress_context)); } // run the installer through a progress window int usb_install_window(HWND hWnd, HINSTANCE instance, filter_context_t* filter_context) { MSG msg; WNDCLASSEX wc; BOOL bRet; HWND hDlg; int ret; if (!hWnd || !instance) return -1; INIT_RICHED32(); if (!usb_progress_context_create(&g_install_progress_context, instance, filter_context)) { return -1; } memset(&wc, 0, sizeof(wc)); // First we create Window class if it doesn't already exist if (!GetClassInfoExA(instance, "libusbwin32_progress_class", &wc)) { wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_DBLCLKS | CS_SAVEBITS; wc.lpfnWndProc = usb_progress_wndproc; wc.cbClsExtra = wc.cbWndExtra = 0; wc.hInstance = GetModuleHandle(NULL); wc.hIcon = wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.lpszClassName = "libusbwin32_progress_class"; wc.lpszMenuName = NULL; wc.hbrBackground = GetSysColorBrush(COLOR_BTNFACE); if (!RegisterClassExA(&wc)) { USBERR0("can't register class\n"); usb_progress_context_destroy(&g_install_progress_context); return -1; } } // Then we create the dialog base hDlg = CreateWindowExA(WS_EX_TOOLWINDOW | WS_EX_CONTROLPARENT, "libusbwin32_progress_class", DISPLAY_NAME, WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_CAPTION | WS_POPUP | WS_THICKFRAME, 100, 100, 320, 140, hWnd, NULL, instance, NULL); if (hDlg == NULL) { USBERR0("Unable to create progress dialog\n"); usb_progress_context_destroy(&g_install_progress_context); return -1; } g_install_progress_context.progress_hwnd = hDlg; if (GetDesktopWindow() != hWnd) EnableWindow(hWnd, FALSE); // Start modal (disable main Window) UpdateWindow(hDlg); // ...and handle the message processing loop while( (bRet = GetMessage(&msg, NULL, 0, 0)) != 0) { if (bRet == -1) { //wdi_err("GetMessage error"); } else { TranslateMessage(&msg); DispatchMessage(&msg); } } if (GetDesktopWindow() != hWnd) EnableWindow(hWnd, TRUE); // end modal (enable main Window) ret = g_install_progress_context.ret; usb_progress_context_destroy(&g_install_progress_context); return ret; } static bool_t usb_install_get_argument(LPWSTR param_value, LPCWSTR* out_param, LPCWSTR* out_value, LPCWSTR* param_names) { LPCWSTR param_name; int param_name_length; int param_name_pos = 0; int ret; if (!param_value || !out_param || !out_value) return FALSE; *out_param = 0; *out_value = 0; while((param_name = param_names[param_name_pos])) { if (!(param_name_length = (int)wcslen(param_name))) return FALSE; if (param_name[param_name_length-1] == '=') { ret = _wcsnicmp(param_value, param_name, param_name_length); } else { ret = _wcsicmp(param_value, param_name); } if (ret == 0) { *out_param = param_names[param_name_pos]; *out_value = param_value + param_name_length; return TRUE; } param_name_pos++; } return FALSE; } void usb_install_report(filter_context_t* filter_context) { filter_class_t* next_class; filter_device_t* next_device; next_class = filter_context->class_filters; while (next_class) { if (next_class->class_filter_devices) { USBRAWMSG0("\n"); } USBRAWMSG("%s (%s)\n", next_class->class_guid, next_class->class_name); if (strlen(next_class->class_uppers)) { if (usb_registry_mz_to_sz(next_class->class_uppers, ',')) { USBRAWMSG(" class upper filters:%s\n", next_class->class_uppers); } } if (strlen(next_class->class_lowers)) { if (usb_registry_mz_to_sz(next_class->class_lowers, ',')) { USBRAWMSG(" class lower filters:%s\n", next_class->class_lowers); } } next_device = next_class->class_filter_devices; while(next_device) { USBRAWMSG(" %s - %s (%s)\n", next_device->device_hwid, next_device->device_name, next_device->device_mfg); USBRAWMSG(" %s\n", next_device->device_id); if (strlen(next_device->device_uppers)) { if (usb_registry_mz_to_sz(next_device->device_uppers, ',')) { USBRAWMSG(" device upper filters:%s\n", next_device->device_uppers); } } if (strlen(next_device->device_lowers)) { if (usb_registry_mz_to_sz(next_device->device_lowers, ',')) { USBRAWMSG(" device lower filters:%s\n", next_device->device_lowers); } } next_device = next_device->next; } next_class = next_class->next; } } static bool_t usb_install_iswow64(void) { HMODULE kernel_dll; is_wow64_process_t IsWow64Process; BOOL IsWow64 = FALSE; kernel_dll = GetModuleHandleA("kernel32.dll"); if (!kernel_dll) { USBERR0("loading kernel32.dll failed\n"); return FALSE; } IsWow64Process = (is_wow64_process_t) GetProcAddress(kernel_dll, "IsWow64Process"); if (IsWow64Process) { if (!IsWow64Process(GetCurrentProcess(), &IsWow64)) { // handle error IsWow64 = FALSE; } } return IsWow64; } static BOOL usb_install_admin_check(void) /*++ Routine Description: This routine returns TRUE if the caller's process is a member of the Administrators local group. Caller is NOT expected to be impersonating anyone and is expected to be able to open its own process and process token. Arguments: None. Return Value: TRUE - Caller has Administrators local group. FALSE - Caller does not have Administrators local group. -- */ { BOOL b; SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; PSID AdministratorsGroup; GET_WINDOWS_VERSION; if (windows_version <= WINDOWS_XP) { return TRUE; } b = AllocateAndInitializeSid( &NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); if(b) { if (!CheckTokenMembership( NULL, AdministratorsGroup, &b)) { b = FALSE; } FreeSid(AdministratorsGroup); } return(b); } void usage(void) { #define ID_HELP_TEXT 10020 #define ID_DOS_TEXT 300 CONST CHAR* src; DWORD src_count, charsWritten; HGLOBAL res_data; HANDLE handle; HRSRC hSrc; if ((handle = GetStdHandle(STD_ERROR_HANDLE)) == INVALID_HANDLE_VALUE) return; hSrc = FindResourceA(NULL, MAKEINTRESOURCEA(ID_HELP_TEXT), MAKEINTRESOURCEA(ID_DOS_TEXT)); if (!hSrc) return; src_count = SizeofResource(NULL, hSrc); res_data = LoadResource(NULL, hSrc); if (!res_data) return; src = (char*) LockResource(res_data); if (!src) return; WriteFile(handle, src, src_count, &charsWritten, NULL); }