| 1 | | = Reusable parts of the project / solutions to common problems = |
| 2 | | |
| 3 | | Libwdi implements reusable solutions to the following problems: |
| 4 | | |
| 5 | | == Creating a Windows application that uses the exact same codebase for MSVC, WDK/DDK, MinGW and cygwin == |
| 6 | | 3 sets of files are being used for that: |
| 7 | | * For MinGW/cygwin, autotools scripts, that automatically generate the {{{config.h}}} and Makefiles used during compilation |
| 8 | | * For MSVC/Visual Studio, a regular set of project files, depending on the manually edited {{{/msvc/config.h}}} |
| 9 | | * For DDK/WDK, a batch script, depending on the manually edited {{{/msvc/config.h}}} |
| 10 | | |
| 11 | | ||=File(s): (MinGW/cygwin) =||[http://git.libusb.org/?p=libwdi.git;a=blob;f=autogen.sh /autogen.sh], [http://git.libusb.org/?p=libwdi.git;a=blob;f=configure.ac /configure.ac], [http://git.libusb.org/?p=libwdi.git;a=blob;f=Makefile.am /Makefile.am], [http://git.libusb.org/?p=libwdi.git;a=blob;f=libwdi/Makefile.am /libwdi/Makefile.am], [http://git.libusb.org/?p=libwdi.git;a=blob;f=examples/Makefile.am /examples/Makefile.am] || |
| 12 | | ||=File(s): (MSVC) =||{{{*.sln}}}, {{{*.vcproj}}} || |
| 13 | | ||=File(s): (DDK) =||[http://git.libusb.org/?p=libwdi.git;a=blob;f=ddk_build.cmd /ddk_build.cmd], {{{*_sources}}} || |
| 14 | | == Creating a Windows application that is compatible with all versions of Windows from 2000 == |
| 15 | | The trick is to use MinGW as the compiler along with the {{{-DWINVER=0x500}}} flag. |
| 16 | | ||=File(s): =||[http://git.libusb.org/?p=libwdi.git;a=blob;f=configure.ac /configure.ac] || |
| 17 | | == Creating a Windows GUI application that use the latest Windows visual enhancements (Aero Glass look on Vista or Windows 7), even if compiled from MinGW/cygwin == |
| 18 | | The default from all compilers, including Microsoft ones, is NOT to use the default look and feel from each platform, but to force the XP/2k one.[[BR]] |
| 19 | | To enable a more up to date look and feel in standard GUI applications, you need to manually embed the [http://git.libusb.org/?p=libwdi.git;a=blob;f=examples/common_controls.manifest common-controls manifest], as well as include {{{<commctrl.h>}}} in your source. |
| 20 | | * For MSVC projects, just adding the manifest to your resources files in the project will do.[[BR]] |
| 21 | | * For DDK/WDK, as documented [http://jpassing.com/2008/02/01/how-to-use-manifests-with-buildexe here], you need to add an {{{SXS_APPLICATION_MANIFEST}}} line with the name of your manifest |
| 22 | | * For MinGW/cywgin, it's a bit more tricky. What you want to do is link to the manifest in your .rc file, with something like {{{CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "common_controls.manifest"}}}. The problem is that, this needs to appear at the end, else MinGW will ignore it, and you want to be able to edit your .rc in Visual Studio, without destroying the additional data you add to the .rc. The solution is to have the following at the beginning of your .rc |
| 23 | | {{{ |
| 24 | | 3 TEXTINCLUDE |
| 25 | | BEGIN |
| 26 | | " |
| 27 | | " |
| 28 | | "// Must reference a manifest for visual styles |
| 29 | | " |
| 30 | | "// Oh, and it must happen at the end, or MinGW will ignore it! |
| 31 | | " |
| 32 | | "#if defined(__GNUC__) |
| 33 | | " |
| 34 | | "CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST ""common_controls.manifest"" |
| 35 | | " |
| 36 | | "#endif |
| 37 | | " |
| 38 | | "" |
| 39 | | END |
| 40 | | }}} |
| 41 | | And of course, you need a matching section at the end. |
| 42 | | |
| 43 | | ||=File(s): =||[http://git.libusb.org/?p=libwdi.git;a=blob;f=examples/common_controls.manifest common-controls manifest], [http://git.libusb.org/?p=libwdi.git;a=blob;f=examples/zadig_sources example DDK sources (Zadig)], [http://git.libusb.org/?p=libwdi.git;a=blob;f=examples/zadig.rc example MinGW manifest from .rc (Zadig)] || |
| 44 | | == Creating Windows application from MSVC, WDK/DDK, MinGW and cygwin that require UAC elevation, even if compiled from MinGW/cygwin == |
| 45 | | The solution to that is similar to the above one, in that you need to use [http://git.libusb.org/?p=libwdi.git;a=blob;f=examples/elevation.manifest a manifest for elevation].[[BR]] |
| 46 | | In our project, you will see that we combine multiple manifests were needed (this is the case for the Zadig example above for instance), as DDK is not able to merge separate manifests files. Also, recent versions of Visual Studio don't require an explicit manifest file for elevation, as this can be taken care of in Linker -> Manifest File -> UAC Execution Level. |
| 47 | | |
| 48 | | ||=File(s): =||[http://git.libusb.org/?p=libwdi.git;a=blob;f=examples/elevation.manifest /examples/elevation.manifest]|| |
| 49 | | == Detecting if an application is running on a 32 or 64 bit Windows OS == |
| 50 | | Depending on the OS being run, libwdi requires a 32 or 64 bit version of the driver installer to be run. The following section of code can be used to detect if a process is running as 32 or 64 bit code: |
| 51 | | {{{ |
| 52 | | #!C |
| 53 | | BOOL is_x64 = FALSE; |
| 54 | | // Detect whether if we should run the 64 bit installer, without |
| 55 | | // relying on external libs |
| 56 | | if (sizeof(uintptr_t) < 8) { |
| 57 | | // This application is not 64 bit, but it might be 32 bit |
| 58 | | // running in WOW64 |
| 59 | | pIsWow64Process = (BOOL (__stdcall *)(HANDLE, PBOOL)) |
| 60 | | GetProcAddress(GetModuleHandle("KERNEL32"), "IsWow64Process"); |
| 61 | | if (pIsWow64Process != NULL) { |
| 62 | | (*pIsWow64Process)(GetCurrentProcess(), &is_x64); |
| 63 | | } |
| 64 | | } else { |
| 65 | | is_x64 = TRUE; |
| 66 | | } |
| 67 | | }}} |
| 68 | | ||=File(s): =||[http://git.libusb.org/?p=libwdi.git;a=blob;f=libwdi/libwdi.c /libwdi/libwdi.c] -> install_driver_internal() || |
| 69 | | == Compiling an project for both 32 and 64 bit == |
| 70 | | When possible, because of the need for an embedded driver installer that matches the bitsize of the Windows OS, we need to compile both 32 bit and a 64 bit installer at the same time. This is how we addressed that problem: |
| 71 | | * Visual Studio is pretty straightforward, so we won't document it. |
| 72 | | * For DDK, switching between 32 and 64 bit can be done on the fly in the batch file. The environment variables you need to modify to switch between 32 and 64 bit are: {{{_BUILDARCH}}}, {{{386}}}, {{{AMD64}}} and {{{BUILD_DEFAULT_TARGETS}}}, as well as the {{{PATH}}}. |
| 73 | | * For MinGW, provided that you use a multilib MinGW-w64 for compilation, switching between 32 and 64 bit can be easily achieved on the fly as well with the {{{-m32}}}/{{{-m64}}} {{{CFLAGS}}}/{{{LDFLAGS}}} options. You can also do configure time 32/64 bit support detection or enabling/disabling through configure.ac, or to set the default to be 32 bit, with only the required files as 64. |
| 74 | | |
| 75 | | ||=File(s): (MinGW) =||[http://git.libusb.org/?p=libwdi.git;a=blob;f=libwdi/Makefile.am /Makefile.am] [http://git.libusb.org/?p=libwdi.git;a=blob;f=configure.ac /configure.ac] || |
| 76 | | ||=File(s): (DDK) =||[http://git.libusb.org/?p=libwdi.git;a=blob;f=ddk_build.cmd /ddk_build.cmd] || |
| 77 | | == Folder selection / File open/save Windows dialogs that uses to new enhanced Vista or Windows 7 controls when available (and default to the regular version on older platforms) == |
| 78 | | This is all done in the {{{file_dialog()}}} and {{{browse_for_folder()}}} calls from {{{zadig_stdlg.c}}}. |
| 79 | | ||=File(s): (DDK) =||[http://git.libusb.org/?p=libwdi.git;a=blob;f=examples/zadig_stdlg.c /examples/zadig_stdlg.c] || |
| 80 | | == USB hotplug detection == |
| 81 | | 1. When not using {{{RegisterDeviceNotification()}}}, Windows sends an undefined number of {{{WM_DEVICECHANGE}}} events in rapid sequence, all with the '''exact same''' {{{wParam}}}/{{{lParam}}} so that we cannot differentiate between them. Notifying on each of those would bother the user too much. |
| 82 | | 1. When using {{{RegisterDeviceNotification()}}}, it is possible to get unique {{{WM_DEVICECHANGE}}} events but only for devices that already have a driver, because there is no device interface class for unknown/driverless devices and Microsoft has not publicized any way of doing so, it is NOT possible to get a single notification event for insertion/removal of devices that don't have a driver.[[BR]]Our solution then is to initiate delayed notification thread on the first {{{WM_DEVICECHANGE}}} message we receive, and wait for this thread to send a user defined event back to our main callback. |
| 83 | | |
| 84 | | ||=File(s): =||[http://git.libusb.org/?p=libwdi.git;a=blob;f=examples/zadig.c /examples/zadig.c] -> {{{main_callback()}}} & {{{notification_delay_thread()}}} || |
| 85 | | == USB device listing, including driverless devices == |
| 86 | | ||=File(s): =||[http://git.libusb.org/?p=libwdi.git;a=blob;f=libwdi/libwdi.c /libwdi/libwdi.c] -> {{{wdi_create_list()}}} || |
| 87 | | == Bumping the executable/DLL version number automatically using git tags == |
| 88 | | If you are using versioning in .rc files, it would obviously be nice to bump the version minor according to major commits that you push into your git repository.[[BR]] |
| 89 | | In libwdi's case, we use incremental git tags to identify minor versions ("w123", "w124", ...) and we use the numeric part of that tag as the minor for the exe/DLL (eg. "1, 0, 0, 123"). The shell script linked below will: |
| 90 | | 1. retrieve and increment the current git tag |
| 91 | | 2. update the version in the .rc files using sed |
| 92 | | 3. commit the changes in git |
| 93 | | Additionally, the script also updates the USB VID -> Vendor ID string source (see below). |
| 94 | | |
| 95 | | ||=File(s): =||[http://git.libusb.org/?p=libwdi.git;a=blob;f=_bump.sh /_bump.sh] || |
| 96 | | == Windows GUI !MouseOver/Hover Tooltip == |
| 97 | | A simple call that creates and displays a tooltip when hovering over a dialog item (eg. text field) |
| 98 | | |
| 99 | | ||=File(s): =||[http://git.libusb.org/?p=libwdi.git;a=blob;f=examples/zadig_stdlg.c /examples/zadig_stdlg.c] -> create_tooltip() || |
| 100 | | == Sed script to convert the USB VIDs into a C source that will resolve then to Vendor ID strings == |
| 101 | | In our applications, this is used to display the Vendor name of a specific USB device when hovering over the VID hex string.[[BR]] |
| 102 | | http://www.linux-usb.org/usb.ids does a great job at maintaining known Vendor IDs, but the list still needs to be converted into something [http://git.libusb.org/?p=libwdi.git;a=blob;f=libwdi/vid_data.c a bit more C-friendly]. For that purpose we built a shell script that, when executed: |
| 103 | | 1. uses wget to retrieve the latest version of the list |
| 104 | | 2. uses sed to convert it to a C source with a function call that will take a VID and return the matching string. |
| 105 | | |
| 106 | | ||=File(s): =||[http://git.libusb.org/?p=libwdi.git;a=blob;f=libwdi/vid_data.sh /libwdi/vid_data.sh] || |
| 107 | | == UTF-8 wrappers to Wide Char (UTF-16) MS API == |
| 108 | | This is what Microsoft should have done a long time ago. Along with the A (Ansi) and W (!WideChar/Unicode) versions of an API call, it should have implemented U (UTF-8) as well, as it usually makes more sense to use UTF-8 than UTF-16 in an application, especially one that is not localized but still wants to support localized user input. For instance, wouldn't it be nice if there existed a [http://msdn.microsoft.com/en-us/library/aa363858.aspx CreateFile] version that accepted an UTF-8 char * for the file name?[[BR]] |
| 109 | | Our UTF-8 wrapper API provides just that, for a selected subset of the Windows API calls, with: |
| 110 | | * {{{FormatMessage}}} ({{{FormatMessageU}}}) |
| 111 | | * {{{SendMessage}}} ({{{SendMessageLU}}}: {{{lParam}}} as UTF-8) |
| 112 | | * {{{SHGetPathFromIDList}}} ({{{SHGetPathFromIDListU}}}) |
| 113 | | * {{{CreateWindow}}} ({{{CreateWindowU}}}) |
| 114 | | * {{{GetWindowText}}}/{{{SetWindowText}}} ({{{GetWindowTextU}}}/{{{SetWindowTextU}}}) |
| 115 | | * {{{GetWindowTextLength}}} ({{{GetWindowTextLengthU}}}) |
| 116 | | * {{{GetDlgItemText}}}/{{{SetDlgItemText}}} ({{{GetDlgItemTextU}}}/{{{SetDlgItemTextU}}}) |
| 117 | | * {{{GetTextExtentPoint}}} ({{{GetTextExtentPointU}}}) |
| 118 | | * {{{CreateFile}}} ({{{CreateFileU}}}) |
| 119 | | * {{{GetCurrentDirectory}}}/{{{GetFullPathName}}}/{{{GetFileAttributes}}} ({{{GetCurrentDirectoryU}}}/{{{GetFullPathNameU}}}/{{{GetFileAttributesU}}}) |
| 120 | | * {{{SHCreateDirectoryEx}}} ({{{SHCreateDirectoryExU}}}) |
| 121 | | * {{{ShellExecuteEx}}}/{{{CreateProcess}}} ({{{ShellExecuteExU}}}/{{{CreateProcessU}}}) |
| 122 | | * {{{GetOpenFileName}}}/{{{GetSaveFileName}}} ({{{GetOpenSaveFileNameU}}}) |
| 123 | | * {{{UpdateDriverForPlugAndPlayDevices}}}/{{{SetupCopyOEMInf}}} ({{{UpdateDriverForPlugAndPlayDevicesU}}}/{{{SetupCopyOEMInfU}}}) |
| 124 | | |
| 125 | | ||=File(s): =||[http://git.libusb.org/?p=libwdi.git;a=blob;f=libwdi/msapi_utf8.h /libwdi/msapi_utf8.h] || |
| 126 | | == Installing a certificate into the Trusted Publisher repository == |
| 127 | | If you want your signed drivers to be installed without security prompts, but can't go through WHQL, you will need to install your signed driver certificate in the Trusted Publisher repository. The following code does just that, through the use of the Crypt32.dll, which is available on Windows 2000 and later.[[BR]] |
| 128 | | The code will also detect whether the certificate is about to be effectively written to the store, so as to let end-users with the possibility to refuse the certificate installation if they so choose.[[BR]] |
| 129 | | The code must be executed from a process running in elevated mode. |
| 130 | | |
| 131 | | ||=File(s): =||[http://git.libusb.org/?p=libwdi.git;a=blob;f=libwdi/libwdi.c /libwdi/libwdi.c] -> wdi_install_trusted_certificate() || |