|
| 1 | +#define WIN32_LEAN_AND_MEAN |
| 2 | + |
| 3 | +#include <stdio.h> |
| 4 | +#include <stdlib.h> |
| 5 | +#include <stdbool.h> |
| 6 | +#include <string.h> |
| 7 | +#include <windows.h> |
| 8 | +#include <devguid.h> |
| 9 | +#include <setupapi.h> |
| 10 | +#include <shlwapi.h> |
| 11 | +#include <newdev.h> |
| 12 | + |
| 13 | +#define LGIDD_CLASS_GUID GUID_DEVCLASS_DISPLAY |
| 14 | +#define LGIDD_CLASS_NAME L"Display" |
| 15 | +#define LGIDD_HWID L"Root\\LGIdd" |
| 16 | +#define LGIDD_HWID_MULTI_SZ (LGIDD_HWID "\0") |
| 17 | +#define LGIDD_INF_NAME L"LGIdd.inf" |
| 18 | + |
| 19 | +void usage(wchar_t *program) |
| 20 | +{ |
| 21 | + wprintf(L"Usage: %s <install|uninstall>\n", program); |
| 22 | + exit(2); |
| 23 | +} |
| 24 | + |
| 25 | +void debugWinError(const wchar_t *desc, HRESULT status) |
| 26 | +{ |
| 27 | + wchar_t *buffer; |
| 28 | + if (!FormatMessageW( |
| 29 | + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, |
| 30 | + NULL, |
| 31 | + status, |
| 32 | + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
| 33 | + (LPWSTR) &buffer, |
| 34 | + 1024, |
| 35 | + NULL |
| 36 | + )) |
| 37 | + { |
| 38 | + fwprintf(stderr, L"%s: 0x%08lx: FormatMessage failed with code 0x%08lx\n", desc, status, GetLastError()); |
| 39 | + return; |
| 40 | + } |
| 41 | + |
| 42 | + for (size_t i = wcslen(buffer) - 1; i > 0; --i) |
| 43 | + if (buffer[i] == L'\n' || buffer[i] == L'\r') |
| 44 | + buffer[i] = 0; |
| 45 | + |
| 46 | + fwprintf(stderr, L"%s: 0x%08lx: %s\n", desc, status, buffer); |
| 47 | + LocalFree(buffer); |
| 48 | +} |
| 49 | + |
| 50 | +typedef bool (*IDD_FOUND_PROC)(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDevInfo, void *pContext); |
| 51 | + |
| 52 | +bool findIddDevice(IDD_FOUND_PROC procFound, void *pContext) |
| 53 | +{ |
| 54 | + HDEVINFO hDevInfo = SetupDiGetClassDevsW(&LGIDD_CLASS_GUID, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT); |
| 55 | + if (hDevInfo == INVALID_HANDLE_VALUE) |
| 56 | + { |
| 57 | + debugWinError(L"SetupDiGetClassDevsW", GetLastError()); |
| 58 | + return false; |
| 59 | + } |
| 60 | + |
| 61 | + SP_DEVINFO_DATA devInfo = { .cbSize = sizeof devInfo, 0 }; |
| 62 | + for (DWORD dwIndex = 0; SetupDiEnumDeviceInfo(hDevInfo, dwIndex, &devInfo); ++dwIndex) |
| 63 | + { |
| 64 | + DWORD dwSizeRequired; |
| 65 | + DWORD dwPropertyType; |
| 66 | + SetupDiGetDeviceRegistryPropertyW(hDevInfo, &devInfo, SPDRP_HARDWAREID, &dwPropertyType, NULL, 0, &dwSizeRequired); |
| 67 | + |
| 68 | + DWORD dwLastError = GetLastError(); |
| 69 | + if (dwLastError == ERROR_INVALID_DATA) |
| 70 | + continue; |
| 71 | + else if (dwLastError != ERROR_INSUFFICIENT_BUFFER) |
| 72 | + { |
| 73 | + debugWinError(L"SetupDiGetDeviceRegistryPropertyW(SPDRP_HARDWAREID) size calculation", dwLastError); |
| 74 | + goto fail; |
| 75 | + } |
| 76 | + |
| 77 | + if (dwPropertyType != REG_MULTI_SZ) |
| 78 | + { |
| 79 | + fwprintf(stderr, L"SetupDiGetDeviceRegistryPropertyW(SPDRP_HARDWAREID) returned wrong type\n"); |
| 80 | + goto fail; |
| 81 | + } |
| 82 | + |
| 83 | + LPWSTR lpBuffer = malloc(dwSizeRequired); |
| 84 | + if (!lpBuffer) |
| 85 | + { |
| 86 | + fwprintf(stderr, L"failed to allocate memory for SetupDiGetDeviceRegistryPropertyW(SPDRP_HARDWAREID)\n"); |
| 87 | + goto fail; |
| 88 | + } |
| 89 | + |
| 90 | + if (!SetupDiGetDeviceRegistryPropertyW(hDevInfo, &devInfo, SPDRP_HARDWAREID, &dwPropertyType, (PBYTE)lpBuffer, dwSizeRequired, NULL)) |
| 91 | + { |
| 92 | + debugWinError(L"SetupDiGetDeviceRegistryPropertyW(SPDRP_HARDWAREID) for real", GetLastError()); |
| 93 | + free(lpBuffer); |
| 94 | + goto fail; |
| 95 | + } |
| 96 | + |
| 97 | + bool found = false; |
| 98 | + |
| 99 | + for (LPWSTR lpHwId = lpBuffer; *lpHwId; lpHwId += wcslen(lpBuffer) + 1) |
| 100 | + { |
| 101 | + if (!lstrcmpiW(lpHwId, LGIDD_HWID)) |
| 102 | + { |
| 103 | + found = true; |
| 104 | + break; |
| 105 | + } |
| 106 | + } |
| 107 | + |
| 108 | + free(lpBuffer); |
| 109 | + |
| 110 | + if (found && !procFound(hDevInfo, &devInfo, pContext)) |
| 111 | + break; |
| 112 | + } |
| 113 | + |
| 114 | + SetupDiDestroyDeviceInfoList(hDevInfo); |
| 115 | + return true; |
| 116 | + |
| 117 | +fail: |
| 118 | + SetupDiDestroyDeviceInfoList(hDevInfo); |
| 119 | + return false; |
| 120 | +} |
| 121 | + |
| 122 | +enum DeviceCreated { |
| 123 | + DEVICE_CREATED, |
| 124 | + DEVICE_NOT_CREATED, |
| 125 | + DEVICE_UNKNOWN, |
| 126 | +}; |
| 127 | + |
| 128 | +bool isIddDeviceCreatedEnum(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDevInfo, void *pContext) |
| 129 | +{ |
| 130 | + enum DeviceCreated *result = pContext; |
| 131 | + *result = DEVICE_CREATED; |
| 132 | + return false; |
| 133 | +} |
| 134 | + |
| 135 | +enum DeviceCreated isIddDeviceCreated() |
| 136 | +{ |
| 137 | + enum DeviceCreated result = DEVICE_UNKNOWN; |
| 138 | + if (findIddDevice(isIddDeviceCreatedEnum, &result) && result == DEVICE_UNKNOWN) |
| 139 | + result = DEVICE_NOT_CREATED; |
| 140 | + return result; |
| 141 | +} |
| 142 | + |
| 143 | +bool createIddDevice(void) |
| 144 | +{ |
| 145 | + HDEVINFO hDevInfo = SetupDiCreateDeviceInfoList(&LGIDD_CLASS_GUID, NULL); |
| 146 | + if (hDevInfo == INVALID_HANDLE_VALUE) |
| 147 | + { |
| 148 | + debugWinError(L"SetupDiCreateDeviceInfoList", GetLastError()); |
| 149 | + return false; |
| 150 | + } |
| 151 | + |
| 152 | + SP_DEVINFO_DATA devInfo = { .cbSize = sizeof devInfo, 0 }; |
| 153 | + if (!SetupDiCreateDeviceInfoW(hDevInfo, LGIDD_CLASS_NAME, &LGIDD_CLASS_GUID, NULL, NULL, DICD_GENERATE_ID, &devInfo)) |
| 154 | + { |
| 155 | + debugWinError(L"SetupDiCreateDeviceInfoW", GetLastError()); |
| 156 | + goto fail; |
| 157 | + } |
| 158 | + |
| 159 | + if (!SetupDiSetDeviceRegistryPropertyW(hDevInfo, &devInfo, SPDRP_HARDWAREID, (PBYTE) LGIDD_HWID_MULTI_SZ, sizeof LGIDD_HWID_MULTI_SZ)) |
| 160 | + { |
| 161 | + debugWinError(L"SetupDiSetDeviceRegistryPropertyW", GetLastError()); |
| 162 | + goto fail; |
| 163 | + } |
| 164 | + |
| 165 | + if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE, hDevInfo, &devInfo)) |
| 166 | + { |
| 167 | + debugWinError(L"SetupDiCallClassInstaller", GetLastError()); |
| 168 | + goto fail; |
| 169 | + } |
| 170 | + |
| 171 | + return true; |
| 172 | + |
| 173 | +fail: |
| 174 | + SetupDiDestroyDeviceInfoList(hDevInfo); |
| 175 | + return false; |
| 176 | +} |
| 177 | + |
| 178 | +bool destroyIddDeviceEnum(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDevInfo, void* pContext) |
| 179 | +{ |
| 180 | + LPBOOL pbNeedRestart = pContext; |
| 181 | + BOOL bNeedRestart; |
| 182 | + WCHAR szInfPath[MAX_PATH] = { 0 }; |
| 183 | + |
| 184 | + if (!SetupDiBuildDriverInfoList(hDevInfo, pDevInfo, SPDIT_COMPATDRIVER)) |
| 185 | + { |
| 186 | + debugWinError(L"SetupDiBuildDriverInfoList", GetLastError()); |
| 187 | + goto uninstall; |
| 188 | + } |
| 189 | + |
| 190 | + SP_DRVINFO_DATA_W drvInfo = { .cbSize = sizeof drvInfo }; |
| 191 | + if (!SetupDiEnumDriverInfoW(hDevInfo, pDevInfo, SPDIT_COMPATDRIVER, 0, &drvInfo)) |
| 192 | + { |
| 193 | + debugWinError(L"SetupDiEnumDriverInfoW", GetLastError()); |
| 194 | + goto uninstall; |
| 195 | + } |
| 196 | + |
| 197 | + SP_DRVINFO_DETAIL_DATA_W drvInfoDetail = { .cbSize = sizeof drvInfoDetail }; |
| 198 | + SetupDiGetDriverInfoDetailW(hDevInfo, pDevInfo, &drvInfo, &drvInfoDetail, sizeof drvInfoDetail, NULL); |
| 199 | + |
| 200 | + DWORD dwLastError = GetLastError(); |
| 201 | + if (dwLastError == ERROR_INSUFFICIENT_BUFFER) |
| 202 | + wcscpy_s(szInfPath, MAX_PATH, drvInfoDetail.InfFileName); |
| 203 | + else |
| 204 | + debugWinError(L"SetupDiEnumDriverInfoW", GetLastError()); |
| 205 | + |
| 206 | +uninstall: |
| 207 | + if (DiUninstallDevice(NULL, hDevInfo, pDevInfo, 0, &bNeedRestart)) |
| 208 | + *pbNeedRestart |= bNeedRestart; |
| 209 | + else |
| 210 | + { |
| 211 | + debugWinError(L"DiUninstallDevice", GetLastError()); |
| 212 | + return true; |
| 213 | + } |
| 214 | + |
| 215 | + if (*szInfPath) |
| 216 | + { |
| 217 | + if (DiUninstallDriverW(NULL, szInfPath, 0, &bNeedRestart)) |
| 218 | + *pbNeedRestart |= bNeedRestart; |
| 219 | + else |
| 220 | + debugWinError(L"DiUninstallDriverW", GetLastError()); |
| 221 | + } |
| 222 | + |
| 223 | + return true; |
| 224 | +} |
| 225 | + |
| 226 | +void destroyIddDevice(LPBOOL pbNeedRestart) |
| 227 | +{ |
| 228 | + findIddDevice(destroyIddDeviceEnum, pbNeedRestart); |
| 229 | +} |
| 230 | + |
| 231 | +bool getIddInfPath(LPWSTR lpszInf) |
| 232 | +{ |
| 233 | + WCHAR szDir[MAX_PATH]; |
| 234 | + WCHAR szInf[MAX_PATH]; |
| 235 | + |
| 236 | + if (!GetModuleFileNameW(NULL, szDir, MAX_PATH)) |
| 237 | + { |
| 238 | + debugWinError(L"GetModuleFileNameW", GetLastError()); |
| 239 | + return false; |
| 240 | + } |
| 241 | + |
| 242 | + *PathFindFileNameW(szDir) = 0; |
| 243 | + if (!PathCombineW(lpszInf, szDir, LGIDD_INF_NAME)) |
| 244 | + { |
| 245 | + debugWinError(L"PathCombineW", GetLastError()); |
| 246 | + return false; |
| 247 | + } |
| 248 | + |
| 249 | + if (!PathFileExistsW(lpszInf)) |
| 250 | + { |
| 251 | + fwprintf(stderr, L"INF file does not exist: %s\n", szInf); |
| 252 | + return false; |
| 253 | + } |
| 254 | + |
| 255 | + return true; |
| 256 | +} |
| 257 | + |
| 258 | +bool installIddInf(PBOOL pbNeedRestart) |
| 259 | +{ |
| 260 | + WCHAR szInf[MAX_PATH]; |
| 261 | + |
| 262 | + if (!getIddInfPath(szInf)) |
| 263 | + return false; |
| 264 | + |
| 265 | + if (!DiInstallDriverW(NULL, szInf, DIIRFLAG_FORCE_INF, pbNeedRestart)) |
| 266 | + { |
| 267 | + debugWinError(L"DiInstallDriverW", GetLastError()); |
| 268 | + return false; |
| 269 | + } |
| 270 | + |
| 271 | + return true; |
| 272 | +} |
| 273 | + |
| 274 | +void install() |
| 275 | +{ |
| 276 | + switch (isIddDeviceCreated()) |
| 277 | + { |
| 278 | + case DEVICE_NOT_CREATED: |
| 279 | + wprintf(L"Creating LGIdd device: %s...\n", LGIDD_HWID); |
| 280 | + if (!createIddDevice()) |
| 281 | + return; |
| 282 | + |
| 283 | + // fallthrough |
| 284 | + case DEVICE_CREATED: |
| 285 | + _putws(L"Installing INF..."); |
| 286 | + BOOL bNeedRestart; |
| 287 | + if (!installIddInf(&bNeedRestart)) |
| 288 | + return; |
| 289 | + |
| 290 | + if (bNeedRestart) |
| 291 | + { |
| 292 | + _putws(L"Restart required to complete installation"); |
| 293 | + exit(12); |
| 294 | + } |
| 295 | + break; |
| 296 | + case DEVICE_UNKNOWN: |
| 297 | + exit(1); |
| 298 | + } |
| 299 | +} |
| 300 | + |
| 301 | +void uninstall() |
| 302 | +{ |
| 303 | + BOOL bNeedRestart = 0; |
| 304 | + destroyIddDevice(&bNeedRestart); |
| 305 | + |
| 306 | + if (bNeedRestart) |
| 307 | + { |
| 308 | + _putws(L"Restart required to complete installation"); |
| 309 | + exit(12); |
| 310 | + } |
| 311 | +} |
| 312 | + |
| 313 | +int wmain(int argc, wchar_t **argv) |
| 314 | +{ |
| 315 | + if (argc != 2) |
| 316 | + usage(argv[0]); |
| 317 | + |
| 318 | + if (!wcscmp(argv[1], L"install")) |
| 319 | + install(); |
| 320 | + else if (!wcscmp(argv[1], L"uninstall")) |
| 321 | + uninstall(); |
| 322 | + else |
| 323 | + usage(argv[0]); |
| 324 | +} |
0 commit comments