├── .gitattributes ├── .gitignore ├── .gitmodules ├── DeviceLister ├── DIDeviceInputId │ ├── DIDeviceInputId.vcxproj │ ├── DIDeviceInputId.vcxproj.filters │ ├── dllmain.cpp │ ├── framework.h │ ├── pch.cpp │ └── pch.h ├── DeviceLister.sln └── DeviceLister │ ├── App.config │ ├── DeviceLister.csproj │ ├── DeviceListerForm.Designer.cs │ ├── DeviceListerForm.cs │ ├── DeviceListerForm.resx │ ├── Program.cs │ └── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── README.md ├── common ├── Common.h ├── Logger.h ├── NonCopyable.h ├── StringUtils.cpp ├── StringUtils.h ├── Types.h ├── Utils.cpp ├── Utils.h └── targetver.h ├── dinput8 ├── DirectInputModuleManager.h ├── dinput8.cpp ├── dinput8.def ├── dinput8.h ├── dinput8.sln ├── dinput8.vcxproj ├── dinput8.vcxproj.filters ├── dllmain.cpp ├── stdafx.cpp ├── stdafx.h └── targetver.h └── release ├── DeviceLister.exe ├── devreorder.ini ├── x64 └── dinput8.dll └── x86 └── dinput8.dll /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | \#untracked/ 3 | 4 | # Created by https://www.gitignore.io/api/visualstudio,windows 5 | 6 | ### Windows ### 7 | # Windows image file caches 8 | Thumbs.db 9 | ehthumbs.db 10 | 11 | # Folder config file 12 | Desktop.ini 13 | 14 | # Recycle Bin used on file shares 15 | $RECYCLE.BIN/ 16 | 17 | # Windows Installer files 18 | *.cab 19 | *.msi 20 | *.msm 21 | *.msp 22 | 23 | # Windows shortcuts 24 | *.lnk 25 | 26 | 27 | ### VisualStudio ### 28 | ## Ignore Visual Studio temporary files, build results, and 29 | ## files generated by popular Visual Studio add-ons. 30 | 31 | # User-specific files 32 | *.suo 33 | *.user 34 | *.userosscache 35 | *.sln.docstates 36 | 37 | # User-specific files (MonoDevelop/Xamarin Studio) 38 | *.userprefs 39 | 40 | # Build results 41 | [Dd]ebug/ 42 | [Dd]ebugPublic/ 43 | [Rr]elease/ 44 | [Rr]eleases/ 45 | x64/ 46 | x86/ 47 | bld/ 48 | [Bb]in/ 49 | [Oo]bj/ 50 | [Ll]og/ 51 | 52 | # Visual Studio 2015 cache/options directory 53 | .vs/ 54 | # Uncomment if you have tasks that create the project's static files in wwwroot 55 | #wwwroot/ 56 | 57 | # MSTest test Results 58 | [Tt]est[Rr]esult*/ 59 | [Bb]uild[Ll]og.* 60 | 61 | # NUNIT 62 | *.VisualState.xml 63 | TestResult.xml 64 | 65 | # Build Results of an ATL Project 66 | [Dd]ebugPS/ 67 | [Rr]eleasePS/ 68 | dlldata.c 69 | 70 | # DNX 71 | project.lock.json 72 | project.fragment.lock.json 73 | artifacts/ 74 | 75 | *_i.c 76 | *_p.c 77 | *_i.h 78 | *.ilk 79 | *.meta 80 | *.obj 81 | *.pch 82 | *.pdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *.log 93 | *.vspscc 94 | *.vssscc 95 | .builds 96 | *.pidb 97 | *.svclog 98 | *.scc 99 | 100 | # Chutzpah Test files 101 | _Chutzpah* 102 | 103 | # Visual C++ cache files 104 | ipch/ 105 | *.aps 106 | *.ncb 107 | *.opendb 108 | *.opensdf 109 | *.sdf 110 | *.cachefile 111 | *.VC.db 112 | *.VC.VC.opendb 113 | 114 | # Visual Studio profiler 115 | *.psess 116 | *.vsp 117 | *.vspx 118 | *.sap 119 | 120 | # TFS 2012 Local Workspace 121 | $tf/ 122 | 123 | # Guidance Automation Toolkit 124 | *.gpState 125 | 126 | # ReSharper is a .NET coding add-in 127 | _ReSharper*/ 128 | *.[Rr]e[Ss]harper 129 | *.DotSettings.user 130 | 131 | # JustCode is a .NET coding add-in 132 | .JustCode 133 | 134 | # TeamCity is a build add-in 135 | _TeamCity* 136 | 137 | # DotCover is a Code Coverage Tool 138 | *.dotCover 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # TODO: Comment the next line if you want to checkin your web deploy settings 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # The packages folder can be ignored because of Package Restore 188 | **/packages/* 189 | # except build/, which is used as an MSBuild target. 190 | !**/packages/build/ 191 | # Uncomment if necessary however generally it will be regenerated when needed 192 | #!**/packages/repositories.config 193 | # NuGet v3's project.json files produces more ignoreable files 194 | *.nuget.props 195 | *.nuget.targets 196 | 197 | # Microsoft Azure Build Output 198 | csx/ 199 | *.build.csdef 200 | 201 | # Microsoft Azure Emulator 202 | ecf/ 203 | rcf/ 204 | 205 | # Windows Store app package directories and files 206 | AppPackages/ 207 | BundleArtifacts/ 208 | Package.StoreAssociation.xml 209 | _pkginfo.txt 210 | 211 | # Visual Studio cache files 212 | # files ending in .cache can be ignored 213 | *.[Cc]ache 214 | # but keep track of directories ending in .cache 215 | !*.[Cc]ache/ 216 | 217 | # Others 218 | ClientBin/ 219 | ~$* 220 | *~ 221 | *.dbmdl 222 | *.dbproj.schemaview 223 | *.jfm 224 | *.pfx 225 | *.publishsettings 226 | node_modules/ 227 | orleans.codegen.cs 228 | 229 | # Since there are multiple workflows, uncomment next line to ignore bower_components 230 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 231 | #bower_components/ 232 | 233 | # RIA/Silverlight projects 234 | Generated_Code/ 235 | 236 | # Backup & report files from converting an old project file 237 | # to a newer Visual Studio version. Backup files are not needed, 238 | # because we have git ;-) 239 | _UpgradeReport_Files/ 240 | Backup*/ 241 | UpgradeLog*.XML 242 | UpgradeLog*.htm 243 | 244 | # SQL Server files 245 | *.mdf 246 | *.ldf 247 | 248 | # Business Intelligence projects 249 | *.rdl.data 250 | *.bim.layout 251 | *.bim_*.settings 252 | 253 | # Microsoft Fakes 254 | FakesAssemblies/ 255 | 256 | # GhostDoc plugin setting file 257 | *.GhostDoc.xml 258 | 259 | # Node.js Tools for Visual Studio 260 | .ntvs_analysis.dat 261 | 262 | # Visual Studio 6 build log 263 | *.plg 264 | 265 | # Visual Studio 6 workspace options file 266 | *.opt 267 | 268 | # Visual Studio LightSwitch build output 269 | **/*.HTMLClient/GeneratedArtifacts 270 | **/*.DesktopClient/GeneratedArtifacts 271 | **/*.DesktopClient/ModelManifest.xml 272 | **/*.Server/GeneratedArtifacts 273 | **/*.Server/ModelManifest.xml 274 | _Pvt_Extensions 275 | 276 | # Paket dependency manager 277 | .paket/paket.exe 278 | paket-files/ 279 | 280 | # FAKE - F# Make 281 | .fake/ 282 | 283 | # JetBrains Rider 284 | .idea/ 285 | *.sln.iml 286 | 287 | # CodeRush 288 | .cr/ 289 | 290 | # Python Tools for Visual Studio (PTVS) 291 | __pycache__/ 292 | *.pyc 293 | 294 | ### VisualStudio Patch ### 295 | build/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "minhook"] 2 | path = minhook 3 | url = https://github.com/TsudaKageyu/minhook.git 4 | [submodule "simpleini"] 5 | path = simpleini 6 | url = https://github.com/briankendall/simpleini.git 7 | -------------------------------------------------------------------------------- /DeviceLister/DIDeviceInputId/DIDeviceInputId.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {98d64f89-718f-4828-a906-c9499b4a8679} 25 | DIDeviceInputId 26 | 10.0 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | DynamicLibrary 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | DynamicLibrary 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | $(SolutionDir)\DeviceLister\bin\Debug\ 76 | 77 | 78 | false 79 | $(SolutionDir)\DeviceLister\bin\Release\ 80 | 81 | 82 | true 83 | 84 | 85 | false 86 | 87 | 88 | 89 | Level3 90 | true 91 | WIN32;_DEBUG;DIDEVICEINPUTID_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 92 | true 93 | Use 94 | pch.h 95 | 96 | 97 | Windows 98 | true 99 | false 100 | dinput8.lib;dxguid.lib;Cfgmgr32.lib;rpcrt4.lib;%(AdditionalDependencies) 101 | 102 | 103 | 104 | 105 | Level3 106 | true 107 | true 108 | true 109 | WIN32;NDEBUG;DIDEVICEINPUTID_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 110 | true 111 | Use 112 | pch.h 113 | 114 | 115 | Windows 116 | true 117 | true 118 | true 119 | false 120 | dinput8.lib;dxguid.lib;Cfgmgr32.lib;rpcrt4.lib;%(AdditionalDependencies) 121 | 122 | 123 | 124 | 125 | Level3 126 | true 127 | _DEBUG;DIDEVICEINPUTID_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 128 | true 129 | Use 130 | pch.h 131 | 132 | 133 | Windows 134 | true 135 | false 136 | 137 | 138 | 139 | 140 | Level3 141 | true 142 | true 143 | true 144 | NDEBUG;DIDEVICEINPUTID_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 145 | true 146 | Use 147 | pch.h 148 | 149 | 150 | Windows 151 | true 152 | true 153 | true 154 | false 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | Create 165 | Create 166 | Create 167 | Create 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /DeviceLister/DIDeviceInputId/DIDeviceInputId.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | 26 | 27 | Source Files 28 | 29 | 30 | Source Files 31 | 32 | 33 | -------------------------------------------------------------------------------- /DeviceLister/DIDeviceInputId/dllmain.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "pch.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | std::wstring getDeviceInstanceIdProperty(const DIPROPGUIDANDPATH &iap) 10 | { 11 | DEVPROPTYPE propertyType; 12 | ULONG propertySize = 0; 13 | CONFIGRET cr = CM_Get_Device_Interface_PropertyW(iap.wszPath, &DEVPKEY_Device_InstanceId, &propertyType, nullptr, &propertySize, 0); 14 | 15 | if (cr != CR_BUFFER_SMALL) { 16 | OutputDebugStringA("CM_Get_Device_Interface_PropertyW 1 failed"); 17 | return std::wstring(); 18 | } 19 | 20 | std::wstring deviceId; 21 | deviceId.resize(propertySize); 22 | cr = ::CM_Get_Device_Interface_PropertyW(iap.wszPath, &DEVPKEY_Device_InstanceId, &propertyType, (PBYTE)deviceId.data(), &propertySize, 0); 23 | 24 | if (cr != CR_SUCCESS) { 25 | OutputDebugStringA("CM_Get_Device_Interface_PropertyW 2 failed"); 26 | return std::wstring(); 27 | } 28 | 29 | return deviceId; 30 | } 31 | 32 | extern "C" { 33 | 34 | // Function that takes a string and returns a string 35 | __declspec(dllexport) const wchar_t * GetDeviceInstanceID(const wchar_t *instanceGUIDStr) 36 | { 37 | wchar_t *output = nullptr; 38 | HRESULT hr; 39 | LPDIRECTINPUT8 di; 40 | 41 | hr = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&di, nullptr); 42 | 43 | if (FAILED(hr)) { 44 | OutputDebugStringA("DirectInput8Create failed"); 45 | return nullptr; 46 | } 47 | 48 | GUID instanceGUID; 49 | 50 | if (UuidFromStringW((RPC_WSTR)instanceGUIDStr, &instanceGUID) != RPC_S_OK) { 51 | OutputDebugStringA("UuidFromStringA failed"); 52 | OutputDebugStringW(instanceGUIDStr); 53 | return nullptr; 54 | } 55 | 56 | IDirectInputDevice8W* device; 57 | IDirectInput_CreateDevice(di, instanceGUID, &device, NULL); 58 | 59 | DIPROPGUIDANDPATH iap = {}; 60 | iap.diph.dwSize = sizeof(DIPROPGUIDANDPATH); 61 | iap.diph.dwHeaderSize = sizeof(DIPROPHEADER); 62 | iap.diph.dwHow = DIPH_DEVICE; 63 | 64 | std::wstring deviceId; 65 | 66 | if (SUCCEEDED(IDirectInputDevice_GetProperty(device, DIPROP_GUIDANDPATH, &iap.diph))) { 67 | deviceId = getDeviceInstanceIdProperty(iap); 68 | } else { 69 | OutputDebugStringA("IDirectInputDevice_GetProperty failed"); 70 | } 71 | 72 | IDirectInputDevice_Release(device); 73 | 74 | if (deviceId.size() == 0) { 75 | return nullptr; 76 | } 77 | 78 | size_t outputSize = deviceId.size() + 1; 79 | output = new wchar_t[outputSize]; 80 | wcscpy_s(output, outputSize, deviceId.c_str()); 81 | 82 | return output; 83 | } 84 | 85 | } 86 | 87 | 88 | BOOL APIENTRY DllMain( HMODULE hModule, 89 | DWORD ul_reason_for_call, 90 | LPVOID lpReserved 91 | ) 92 | { 93 | switch (ul_reason_for_call) 94 | { 95 | case DLL_PROCESS_ATTACH: 96 | case DLL_THREAD_ATTACH: 97 | case DLL_THREAD_DETACH: 98 | case DLL_PROCESS_DETACH: 99 | break; 100 | } 101 | return TRUE; 102 | } 103 | 104 | -------------------------------------------------------------------------------- /DeviceLister/DIDeviceInputId/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 4 | // Windows Header Files 5 | #include 6 | -------------------------------------------------------------------------------- /DeviceLister/DIDeviceInputId/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to the pre-compiled header 2 | 3 | #include "pch.h" 4 | 5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. 6 | -------------------------------------------------------------------------------- /DeviceLister/DIDeviceInputId/pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: This is a precompiled header file. 2 | // Files listed below are compiled only once, improving build performance for future builds. 3 | // This also affects IntelliSense performance, including code completion and many code browsing features. 4 | // However, files listed here are ALL re-compiled if any one of them is updated between builds. 5 | // Do not add files here that you will be updating frequently as this negates the performance advantage. 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | #define DIRECTINPUT_VERSION 0x0800 11 | 12 | // add headers that you want to pre-compile here 13 | #include "framework.h" 14 | 15 | #endif //PCH_H 16 | -------------------------------------------------------------------------------- /DeviceLister/DeviceLister.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.33130.400 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeviceLister", "DeviceLister\DeviceLister.csproj", "{EBE0AF6C-E6DA-4497-843A-A3B2B16D2627}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {98D64F89-718F-4828-A906-C9499B4A8679} = {98D64F89-718F-4828-A906-C9499B4A8679} 9 | EndProjectSection 10 | EndProject 11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DIDeviceInputId", "DIDeviceInputId\DIDeviceInputId.vcxproj", "{98D64F89-718F-4828-A906-C9499B4A8679}" 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Debug|x64 = Debug|x64 17 | Debug|x86 = Debug|x86 18 | Release|Any CPU = Release|Any CPU 19 | Release|x64 = Release|x64 20 | Release|x86 = Release|x86 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {EBE0AF6C-E6DA-4497-843A-A3B2B16D2627}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {EBE0AF6C-E6DA-4497-843A-A3B2B16D2627}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {EBE0AF6C-E6DA-4497-843A-A3B2B16D2627}.Debug|x64.ActiveCfg = Debug|Any CPU 26 | {EBE0AF6C-E6DA-4497-843A-A3B2B16D2627}.Debug|x64.Build.0 = Debug|Any CPU 27 | {EBE0AF6C-E6DA-4497-843A-A3B2B16D2627}.Debug|x86.ActiveCfg = Debug|Any CPU 28 | {EBE0AF6C-E6DA-4497-843A-A3B2B16D2627}.Debug|x86.Build.0 = Debug|Any CPU 29 | {EBE0AF6C-E6DA-4497-843A-A3B2B16D2627}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {EBE0AF6C-E6DA-4497-843A-A3B2B16D2627}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {EBE0AF6C-E6DA-4497-843A-A3B2B16D2627}.Release|x64.ActiveCfg = Release|Any CPU 32 | {EBE0AF6C-E6DA-4497-843A-A3B2B16D2627}.Release|x64.Build.0 = Release|Any CPU 33 | {EBE0AF6C-E6DA-4497-843A-A3B2B16D2627}.Release|x86.ActiveCfg = Release|Any CPU 34 | {EBE0AF6C-E6DA-4497-843A-A3B2B16D2627}.Release|x86.Build.0 = Release|Any CPU 35 | {98D64F89-718F-4828-A906-C9499B4A8679}.Debug|Any CPU.ActiveCfg = Debug|Win32 36 | {98D64F89-718F-4828-A906-C9499B4A8679}.Debug|Any CPU.Build.0 = Debug|Win32 37 | {98D64F89-718F-4828-A906-C9499B4A8679}.Debug|x64.ActiveCfg = Debug|x64 38 | {98D64F89-718F-4828-A906-C9499B4A8679}.Debug|x64.Build.0 = Debug|x64 39 | {98D64F89-718F-4828-A906-C9499B4A8679}.Debug|x86.ActiveCfg = Debug|Win32 40 | {98D64F89-718F-4828-A906-C9499B4A8679}.Debug|x86.Build.0 = Debug|Win32 41 | {98D64F89-718F-4828-A906-C9499B4A8679}.Release|Any CPU.ActiveCfg = Release|Win32 42 | {98D64F89-718F-4828-A906-C9499B4A8679}.Release|Any CPU.Build.0 = Release|Win32 43 | {98D64F89-718F-4828-A906-C9499B4A8679}.Release|x64.ActiveCfg = Release|x64 44 | {98D64F89-718F-4828-A906-C9499B4A8679}.Release|x64.Build.0 = Release|x64 45 | {98D64F89-718F-4828-A906-C9499B4A8679}.Release|x86.ActiveCfg = Release|Win32 46 | {98D64F89-718F-4828-A906-C9499B4A8679}.Release|x86.Build.0 = Release|Win32 47 | EndGlobalSection 48 | GlobalSection(SolutionProperties) = preSolution 49 | HideSolutionNode = FALSE 50 | EndGlobalSection 51 | GlobalSection(ExtensibilityGlobals) = postSolution 52 | SolutionGuid = {D1358047-6840-475B-BFF7-0DF3D8E46C26} 53 | EndGlobalSection 54 | EndGlobal 55 | -------------------------------------------------------------------------------- /DeviceLister/DeviceLister/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /DeviceLister/DeviceLister/DeviceLister.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {EBE0AF6C-E6DA-4497-843A-A3B2B16D2627} 8 | WinExe 9 | Properties 10 | DeviceLister 11 | DeviceLister 12 | v3.5 13 | 512 14 | false 15 | 16 | publish\ 17 | true 18 | Disk 19 | false 20 | Foreground 21 | 7 22 | Days 23 | false 24 | false 25 | true 26 | 0 27 | 1.0.0.%2a 28 | false 29 | true 30 | 31 | 32 | x86 33 | true 34 | full 35 | false 36 | bin\Debug\ 37 | DEBUG;TRACE 38 | prompt 39 | 4 40 | 41 | 42 | x86 43 | pdbonly 44 | true 45 | bin\Release\ 46 | TRACE 47 | prompt 48 | 4 49 | 50 | 51 | 52 | False 53 | ..\..\..\..\Windows\Microsoft.NET\DirectX for Managed Code\1.0.2902.0\Microsoft.DirectX.DirectInput.dll 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | Form 69 | 70 | 71 | DeviceListerForm.cs 72 | 73 | 74 | 75 | 76 | DeviceListerForm.cs 77 | 78 | 79 | ResXFileCodeGenerator 80 | Resources.Designer.cs 81 | Designer 82 | 83 | 84 | True 85 | Resources.resx 86 | True 87 | 88 | 89 | SettingsSingleFileGenerator 90 | Settings.Designer.cs 91 | 92 | 93 | True 94 | Settings.settings 95 | True 96 | 97 | 98 | 99 | 100 | Designer 101 | 102 | 103 | 104 | 105 | False 106 | Microsoft .NET Framework 4.5 %28x86 and x64%29 107 | true 108 | 109 | 110 | False 111 | .NET Framework 3.5 SP1 Client Profile 112 | false 113 | 114 | 115 | False 116 | .NET Framework 3.5 SP1 117 | false 118 | 119 | 120 | 121 | 128 | -------------------------------------------------------------------------------- /DeviceLister/DeviceLister/DeviceListerForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace DeviceLister 2 | { 3 | partial class DeviceListerForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.label1 = new System.Windows.Forms.Label(); 32 | this.textBox = new System.Windows.Forms.TextBox(); 33 | this.SuspendLayout(); 34 | // 35 | // label1 36 | // 37 | this.label1.AutoSize = true; 38 | this.label1.Location = new System.Drawing.Point(39, 23); 39 | this.label1.Name = "label1"; 40 | this.label1.Size = new System.Drawing.Size(0, 13); 41 | this.label1.TabIndex = 0; 42 | // 43 | // textBox 44 | // 45 | this.textBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 46 | | System.Windows.Forms.AnchorStyles.Left) 47 | | System.Windows.Forms.AnchorStyles.Right))); 48 | this.textBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 49 | this.textBox.Location = new System.Drawing.Point(12, 12); 50 | this.textBox.Multiline = true; 51 | this.textBox.Name = "textBox"; 52 | this.textBox.ReadOnly = true; 53 | this.textBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; 54 | this.textBox.Size = new System.Drawing.Size(745, 267); 55 | this.textBox.TabIndex = 1; 56 | this.textBox.WordWrap = false; 57 | // 58 | // DeviceListerForm 59 | // 60 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 61 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 62 | this.ClientSize = new System.Drawing.Size(769, 291); 63 | this.Controls.Add(this.textBox); 64 | this.Controls.Add(this.label1); 65 | this.Name = "DeviceListerForm"; 66 | this.Text = "Device Lister"; 67 | this.Load += new System.EventHandler(this.DeviceListerForm_Load); 68 | this.ResumeLayout(false); 69 | this.PerformLayout(); 70 | 71 | } 72 | 73 | #endregion 74 | 75 | private System.Windows.Forms.Label label1; 76 | private System.Windows.Forms.TextBox textBox; 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /DeviceLister/DeviceLister/DeviceListerForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | using Microsoft.DirectX.DirectInput; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace DeviceLister 7 | { 8 | public partial class DeviceListerForm : Form 9 | { 10 | [DllImport("DIDeviceInputId.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] 11 | public static extern IntPtr GetDeviceInstanceID(string input); 12 | 13 | public DeviceListerForm() 14 | { 15 | InitializeComponent(); 16 | } 17 | 18 | private void DeviceListerForm_Load(object sender, EventArgs e) 19 | { 20 | string deviceData = ""; 21 | 22 | foreach (DeviceInstance di in Manager.GetDevices(DeviceClass.GameControl, EnumDevicesFlags.AttachedOnly)) 23 | { 24 | string deviceInstanceId; 25 | IntPtr resultPtr = GetDeviceInstanceID(di.InstanceGuid.ToString()); 26 | 27 | if (resultPtr == IntPtr.Zero) { 28 | deviceInstanceId = "[failed to get device instance ID]"; 29 | } else { 30 | deviceInstanceId = Marshal.PtrToStringUni(resultPtr); 31 | } 32 | 33 | deviceData += "\"" + di.ProductName + "\": {" + di.InstanceGuid + "} <" + deviceInstanceId + ">" + System.Environment.NewLine; 34 | } 35 | 36 | textBox.Text = deviceData; 37 | textBox.Select(0, 0); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /DeviceLister/DeviceLister/DeviceListerForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /DeviceLister/DeviceLister/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | 6 | namespace DeviceLister 7 | { 8 | static class Program 9 | { 10 | /// 11 | /// The main entry point for the application. 12 | /// 13 | [STAThread] 14 | static void Main() 15 | { 16 | Application.EnableVisualStyles(); 17 | Application.SetCompatibleTextRenderingDefault(false); 18 | Application.Run(new DeviceListerForm()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DeviceLister/DeviceLister/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("DeviceLister")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("DeviceLister")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("d3578b15-58ee-422d-8062-51fca033d557")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /DeviceLister/DeviceLister/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace DeviceLister.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DeviceLister.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /DeviceLister/DeviceLister/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /DeviceLister/DeviceLister/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace DeviceLister.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /DeviceLister/DeviceLister/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # devreorder 2 | A utility for reordering and hiding controllers for games using DirectInput 8. It's implemented using a wrapper DLL and can be used to change the behavior of a single game or your entire system. 3 | 4 | The main use of this tool is to bring some sanity to older DirectInput games that rely on the enumeration order of devices to determine the controller order in game (even though they are not supposed to do that). After Windows XP, and especially starting with Windows 8, the enumeration order of controllers is quite arbitrary and will change after rebooting Windows or unplugging and replugging in your devices. This can wreck havoc on games where you have carefully set up controller bindings for players 1-4, only to find that they are totally ruined the next time you boot up Windows. 5 | 6 | There is no supported way to change this order, and the only method I found before writing devreorder was to physically unplug all of your devices and plug them back in the order that you want. This technique, however, falls apart when dealing with wireless and virtual devices. This tool finally allows the order to be defined explicitly. 7 | 8 | Tested in Windows 8.1, but should in theory work in any version of Windows. Note that this currently only works for games that use DirectInput 8. Any older games that make use of an earlier version of DirectInput will not be affected, nor will games that use a different API for reading controller input, including Xinput, the old joystick API in winmm.dll, raw input, and the low level Windows HID API. (Although games that use older versions DirectInput, i.e. they use dinput.dll rather than dinput8.dll, may work with devreorder when combined with [dinputto8](https://github.com/elishacloud/dinputto8).) 9 | 10 | ## How to use: 11 | 12 | ### Apply to single game or program 13 | 14 | In the release zip file, or in the release directory if you cloned the repo, there is an x86 and x64 folder, each containing dinput8.dll. Depending on whether the game is 32-bit or 64-bit copy dinput8.dll in x86 or x64 respectively to the same folder containing the game's .exe file. 15 | 16 | Also copy devreorder.ini and edit it so that its `[order]` section contains a list of controllers in the order that you want them to appear. You can also optionally decide to hide certain controllers by either adding all the controllers you want to be hidden to the `[hidden]` section, or adding all the controllers you _don't_ want to be hidden to the `[visible]` section. 17 | 18 | Any controllers listed in the `[order]` section will always be sorted ahead of any controller not listed in this section. Controllers with the same name will be grouped together, though their relative other between each other will remain the same. You may also use the *instance GUID* of a particular device in case you need to change the order of devices that have the same name. 19 | 20 | When specifying names, you need to have it match exactly as it appears in the Game Controllers control panel or the included DeviceLister program, matching any punctuation, spaces, and capital letters. To open the Game Controllers control panel, type Win+R, type joy.cpl into the dialog box that appears, and then press enter. It will list any controllers that you currently have connected to your system in the order that they will appear to most games that use DirectInput. The DeviceLister application can also be used for this, and allows selecting the text so that it can be copied and pasted, probably making it a better, more convenient option. 21 | 22 | You can also use DeviceLister.exe to find the exact names, GUIDs, or device instance IDs of each of your connected devices. You can use GUIDs or device instance IDs instead of a device name in the `[order]`, `[hidden]`, or `[visible]` sections if you need to specify a specific controller when multiple controllers have the same name. These may also work better in cases where devreorder doesn't match a controller by name for some reason (such as a bug). The GUID must be enclosed in curly braces and match the format in DeviceLister.exe, e.g. `{01234567-89ab-cdef-0123-456789abcdef}`, and likewise a device instance ID must be enclosed in triangle brackets, e.g. ``. Unfortunately, when there's more than one device with the same name, DeviceLister currently doesn't have a convenient way of determining which listing corresponds to which physical device. However, the order it uses *should* be the same as in the Game Controllers control panel, and that can display which buttons on a particular device are pressed, so you can use that to help figure out which GUID or device instance ID corresponds with which device. (I hope to improve this situation in the future.) 23 | 24 | Please note that while GUIDs are supposed to remain consistent for any one device, they are specific to a particular Windows installation and are therefore not transferable to another system. Also, people have reported that due to bugs in Windows, the GUIDs might be different between different user accounts, or multiple devices may have the same GUID (which rather defeats the purpose of them being _global_ unique IDs). 25 | 26 | If GUIDs fail to get the job done, you can also try using device instance IDs, which _also_ should remain consistent for whichever device they refer to. However, they are based on where and how the device is connected, so (for example) connecting a USB device to a different port may change its device instance ID. They may not remain consistent for wireless devices. 27 | 28 | Finally, if you install devreorder system-wide, you can disable it for specific applications by adding their executable's filename to the `[ignored processes]` section. Be sure to include the file's extension too (which is usually `.exe`). 29 | 30 | **NOTE:** This method of using devreorder will not work for games that initialize DirectInput via the COM interface. If you follow these directions to apply devreorder to a single game and it is not having any effect, it is likely that the game is accessing the DirectInput COM interface. In that case, you will need to follow the directions in the [Registry changes](#registry-changes) section. 31 | 32 | **NOTE #2:** If you get an error when running DeviceLister.exe that says something like `System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.DirectX.DirectInput'` then you probably need to install the DirectX 9 runtime. [Here's a link to Microsoft's installer.](https://www.microsoft.com/en-us/download/details.aspx?id=8109) 33 | 34 | ### Use one settings file for all games 35 | 36 | If you want to have a single devreorder.ini file that applies to all games, copy it to `C:\ProgramData\devreorder\devreorder.ini` (or wherever. your program-data folder is) The wrapper DLL will always check the game's current directory for devreorder.ini and, failing that, then check `C:\ProgramData\devreorder\devreorder.ini` so you can always change the settings on a per-game basis if you prefer. 37 | 38 | ### Registry changes 39 | 40 | As noted above, some games initialize DirectInput via COM. Starting from Windows 8, by default this ignores `dinput8.dll` from the application directory in favour of a system one, however this behaviour can be changed with a registry entry localized to the current user (therefore, this change does not require Administrator rights). With these changes, DI COM interfaces will load from `dinput8.dll` from the game directory, if one exists, and loaded from a system directory otherwise. 41 | 42 | To make these changes, open the Command Prompt and run the following commands **exactly** as follows: 43 | ``` 44 | reg add HKCU\Software\Classes\CLSID\{25E609E4-B259-11CF-BFC7-444553540000}\InprocServer32 /ve /t REG_SZ /d dinput8.dll /reg:32 45 | reg add HKCU\Software\Classes\CLSID\{25E609E4-B259-11CF-BFC7-444553540000}\InprocServer32 /ve /t REG_SZ /d dinput8.dll /reg:64 46 | reg add HKCU\Software\Classes\CLSID\{25E609E5-B259-11CF-BFC7-444553540000}\InprocServer32 /ve /t REG_SZ /d dinput8.dll /reg:32 47 | reg add HKCU\Software\Classes\CLSID\{25E609E5-B259-11CF-BFC7-444553540000}\InprocServer32 /ve /t REG_SZ /d dinput8.dll /reg:64 48 | ``` 49 | 50 | ### Apply to your entire system 51 | 52 | (Warning! The following method involves changing DLL files in your Windows directory and is generally not recommended. Do not use this method unless you are comfortable making potentially breaking changes to your system and you understand the consequences, including the security implications. Proceed at your own peril!) 53 | 54 | If you want to affect the order of controllers for your entire system, that can be accomplished with the following: 55 | 56 | 1. Create a System Restore Point, just to be safe 57 | 2. Fully quit any applications that make use of DirectInput. (They cannot be running in the system tray.) 58 | 3. Open your `system32` directory, usually `C:\Windows\system32` 59 | 4. Take ownership of the file `dinput8.dll` and change its permissions so that the Administrators group has full control of the file. I'm not going to provide instructions here as you can use your favorite search engine to figure out how to do this. If you're not comfortable taking ownership of a file then you probably shouldn't be tampering with your Windows directory! 60 | 5. Rename `dinput8.dll` to `dinput8org.dll` 61 | 6. If your Windows installation is 64-bit, copy `x64/dinput8.dll` from the release directory or release zip file into `system32`. If your system is 32-bit (though these days that's uncommon), copy `x86/dinput8.dll` into `system32` and skip to step 12. 62 | 7. Open your `sysWOW64` directory, usually `C:\Windows\sysWOW64` 63 | 8. *If you're using TrackIR,* to prevent it from crashing, copy the original `dinput8.dll` from `sysWOW64` to the TrackIR directory. If you're not using TrackIR then you can ignore this step. 64 | 9. Take ownership of `dinput8.dll` and change its permissions so that the Administrators group has full control 65 | 10. Rename `dinput8.dll` to `dinput8org.dll` 66 | 11. Copy `x86/dinput8.dll` from the release directory or release zip file into `sysWOW64` 67 | 12. Copy `devreorder.ini` to a folder named `devreorder` located in your program-data directory, usually `C:\ProgramData\devreorder\devreorder.ini` 68 | 13. Edit that copy of `devreorder.ini` as per the instructions in the above sections 69 | 70 | *NB: It is extremely important that you rename the original copy of dinput8.dll to dinput8org.dll. devreorder will specifically look for a dll named that in the system32 / sysWOW64 directory, so if you use a different name than dinput8org.dll, then DirectInput will stop working and your game may crash!* 71 | 72 | ## Antivirus false positives 73 | 74 | It's been reported to me that some antivirus software may flag devreorder's versions of dinput8.dll as a virus or other kind of malware. These are false positives. If this happens to you then you will need to configure your antivirus software to whitelist these files so that it doesn't prevent devreorder from working. 75 | 76 | ## Possible future work 77 | 78 | - A GUI that streamlines installing the devreorder DLLs, and allows just dragging controllers into the order you want. (Wouldn't that be nice!) 79 | - Wrap other game input APIs 80 | 81 | I make no guarantees that I will ever get around to implementing any of these! 82 | 83 | ## Some credits 84 | 85 | This project makes use of both [MinHook](https://github.com/TsudaKageyu/minhook) by @TsudaKageyu and [SimpleIni](https://github.com/brofield/simpleini) by @brofield. 86 | 87 | The technique and a good chunk of source code for the DirectInput wrapper was adapted from the [x360ce](https://github.com/x360ce/x360ce) project. Special thanks to the contributors of that project! 88 | -------------------------------------------------------------------------------- /common/Common.h: -------------------------------------------------------------------------------- 1 | //#define LOGGER_DISABLE 2 | 3 | #pragma once 4 | 5 | #include "Types.h" 6 | #include "NonCopyable.h" 7 | #include "Logger.h" 8 | #include "Utils.h" 9 | #include "StringUtils.h" 10 | -------------------------------------------------------------------------------- /common/Logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "NonCopyable.h" 10 | #include "StringUtils.h" 11 | #include "Utils.h" 12 | 13 | #ifndef LOGGER_DISABLE 14 | class Logger : NonCopyable 15 | { 16 | public: 17 | Logger() : m_systime(), m_system(), m_file(INVALID_HANDLE_VALUE) {} 18 | 19 | Logger::~Logger() 20 | { 21 | if (m_file) 22 | CloseHandle(m_file); 23 | } 24 | 25 | static Logger& Logger::Get() 26 | { 27 | static Logger instance; 28 | return instance; 29 | }; 30 | 31 | bool File(const std::string& filename) 32 | { 33 | std::string logpath; 34 | FullPathFromPath(&logpath, filename); 35 | 36 | m_file = CreateFileA(logpath.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 37 | return m_file != INVALID_HANDLE_VALUE; 38 | } 39 | 40 | bool System() 41 | { 42 | return (m_system = true); 43 | } 44 | 45 | void Print(const char* format, ...) 46 | { 47 | va_list args; 48 | va_start(args, format); 49 | Print(format, args); 50 | va_end(args); 51 | } 52 | 53 | void Print(const wchar_t* format, ...) 54 | { 55 | va_list args; 56 | va_start(args, format); 57 | Print(format, args); 58 | va_end(args); 59 | } 60 | 61 | void Print(const char* format, va_list args) 62 | { 63 | bool to_file = m_file != INVALID_HANDLE_VALUE; 64 | bool to_system = m_system; 65 | 66 | if ((to_file || to_system) && format) 67 | { 68 | int outsize = _vscprintf(format, args) + 1; 69 | std::unique_ptr buffer(new char[outsize]); 70 | CharArrayFormatV(buffer.get(), outsize, format, args); 71 | 72 | #ifdef LOGGER_DISABLE_TIME 73 | std::string to_print(buffer.get(), outsize - 1); 74 | #else 75 | std::string to_print; 76 | GetTime(&to_print); 77 | to_print.append(buffer.get(), outsize - 1); 78 | #endif 79 | 80 | to_print.push_back('\r'); 81 | to_print.push_back('\n'); 82 | 83 | DWORD lenout = 0; 84 | if (to_system) OutputDebugStringA(to_print.c_str()); 85 | if (to_file) WriteFile(m_file, to_print.c_str(), (DWORD)to_print.size(), &lenout, NULL); 86 | printf("%s\n", to_print.c_str()); 87 | } 88 | } 89 | 90 | void Print(const wchar_t* format, va_list args) 91 | { 92 | bool to_file = m_file != INVALID_HANDLE_VALUE; 93 | bool to_system = m_system; 94 | 95 | if ((to_file || to_system) && format) 96 | { 97 | int outsize = _vscwprintf(format, args) + 1; 98 | std::unique_ptr buffer(new wchar_t[outsize]); 99 | CharArrayFormatV(buffer.get(), outsize, format, args); 100 | 101 | #ifdef LOGGER_DISABLE_TIME 102 | std::wstring to_print(buffer.get(), outsize - 1); 103 | #else 104 | std::wstring to_print; 105 | std::string time; 106 | GetTime(&time); 107 | to_print = UTF8ToUTF16(time); 108 | to_print.append(buffer.get(), outsize - 1); 109 | #endif 110 | 111 | to_print.push_back(L'\r'); 112 | to_print.push_back(L'\n'); 113 | 114 | DWORD lenout = 0; 115 | if (to_system) OutputDebugStringW(to_print.c_str()); 116 | if (to_file) WriteFile(m_file, UTF16ToUTF8(to_print).c_str(), (DWORD)to_print.size(), &lenout, NULL); 117 | wprintf(L"%s\n", to_print.c_str()); 118 | } 119 | } 120 | 121 | private: 122 | void GetTime(std::string* out) 123 | { 124 | static bool write_stamp = true; 125 | if (write_stamp) 126 | { 127 | static char stamp[] = "[TIME]\t\t[THREAD]\t[LOG]\n"; 128 | DWORD lenout = 0; 129 | 130 | bool to_file = m_file != INVALID_HANDLE_VALUE; 131 | 132 | if (to_file) WriteFile(m_file, stamp, _countof(stamp) - 1, &lenout, NULL); 133 | write_stamp = false; 134 | } 135 | 136 | GetLocalTime(&m_systime); 137 | *out = StringFormat("%02u:%02u:%02u.%03u\t%08u\t", m_systime.wHour, m_systime.wMinute, 138 | m_systime.wSecond, m_systime.wMilliseconds, GetCurrentThreadId()); 139 | 140 | } 141 | 142 | SYSTEMTIME m_systime; 143 | 144 | bool m_system; 145 | HANDLE m_file; 146 | }; 147 | 148 | inline void LogFile(const std::string& logname) 149 | { 150 | Logger::Get().File(logname); 151 | } 152 | 153 | inline void LogSystem() 154 | { 155 | Logger::Get().System(); 156 | } 157 | 158 | inline void PrintLog(const char* format, ...) 159 | { 160 | va_list args; 161 | va_start(args, format); 162 | Logger::Get().Print(format, args); 163 | va_end(args); 164 | } 165 | 166 | inline void PrintLog(const wchar_t * format, ...) 167 | { 168 | va_list args; 169 | va_start(args, format); 170 | Logger::Get().Print(format, args); 171 | va_end(args); 172 | } 173 | 174 | #define PrintFunc PrintLog(__FUNCTION__); 175 | 176 | #else 177 | #define LogFile(logname) (logname) 178 | #define LogSystem(); 179 | #define PrintLog(format, ...) (format) 180 | #endif 181 | -------------------------------------------------------------------------------- /common/NonCopyable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // An inheritable class to disallow the copy constructor and operator= functions 4 | class NonCopyable 5 | { 6 | protected: 7 | NonCopyable() {} 8 | NonCopyable(const NonCopyable&&) {} 9 | void operator=(const NonCopyable&&) {} 10 | private: 11 | NonCopyable(NonCopyable&); 12 | NonCopyable& operator=(NonCopyable& other); 13 | }; 14 | -------------------------------------------------------------------------------- /common/StringUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "Types.h" 10 | #include "StringUtils.h" 11 | 12 | bool CharArrayFormatV(char* out, int outsize, const char* format, va_list args) 13 | { 14 | int count; 15 | static _locale_t locale = nullptr; 16 | if (!locale) locale = _create_locale(LC_ALL, ".1252"); 17 | count = _vsnprintf_s_l(out, outsize, outsize, format, locale, args); 18 | 19 | if (count > 0 && count < outsize) 20 | { 21 | out[count] = '\0'; 22 | return true; 23 | } 24 | else 25 | { 26 | out[outsize - 1] = '\0'; 27 | return false; 28 | } 29 | } 30 | 31 | bool CharArrayFormatV(wchar_t* out, int outsize, const wchar_t* format, va_list args) 32 | { 33 | int count = _vsnwprintf_s(out, outsize, outsize, format, args); 34 | if (count > 0 && count < outsize) 35 | { 36 | out[count] = L'\0'; 37 | return true; 38 | } 39 | else 40 | { 41 | out[outsize - 1] = L'\0'; 42 | return false; 43 | } 44 | } 45 | 46 | std::string StringFormat(const char* format, ...) 47 | { 48 | va_list args; 49 | int required = 0; 50 | 51 | va_start(args, format); 52 | required = _vscprintf(format, args); 53 | 54 | std::unique_ptr buf(new char[required + 1]); 55 | CharArrayFormatV(buf.get(), required + 1, format, args); 56 | va_end(args); 57 | 58 | std::string temp = buf.get(); 59 | return temp; 60 | } 61 | 62 | std::wstring StringFormat(const wchar_t* format, ...) 63 | { 64 | va_list args; 65 | int required = 0; 66 | 67 | va_start(args, format); 68 | required = _vscwprintf(format, args); 69 | 70 | std::unique_ptr buf(new wchar_t[required + 1]); 71 | CharArrayFormatV(buf.get(), required + 1, format, args); 72 | va_end(args); 73 | 74 | std::wstring temp = buf.get(); 75 | return temp; 76 | } 77 | 78 | bool Convert(const std::string &str, s8 *const output) 79 | { 80 | char *endptr = nullptr; 81 | errno = 0; 82 | 83 | s32 value = strtol(str.c_str(), &endptr, 0); 84 | 85 | if (!endptr || *endptr) 86 | return false; 87 | 88 | if (errno == ERANGE) 89 | return false; 90 | 91 | *output = static_cast(value); 92 | return true; 93 | } 94 | 95 | bool Convert(const std::string &str, u8 *const output) 96 | { 97 | char *endptr = nullptr; 98 | errno = 0; 99 | 100 | u32 value = strtoul(str.c_str(), &endptr, 0); 101 | 102 | if (!endptr || *endptr) 103 | return false; 104 | 105 | if (errno == ERANGE) 106 | return false; 107 | 108 | *output = static_cast(value); 109 | return true; 110 | } 111 | 112 | bool Convert(const std::string &str, s16 *const output) 113 | { 114 | char *endptr = nullptr; 115 | errno = 0; 116 | 117 | s32 value = strtol(str.c_str(), &endptr, 0); 118 | 119 | if (!endptr || *endptr) 120 | return false; 121 | 122 | if (errno == ERANGE) 123 | return false; 124 | 125 | *output = static_cast(value); 126 | return true; 127 | } 128 | 129 | bool Convert(const std::string &str, u16 *const output) 130 | { 131 | char *endptr = nullptr; 132 | errno = 0; 133 | 134 | u32 value = strtoul(str.c_str(), &endptr, 0); 135 | 136 | if (!endptr || *endptr) 137 | return false; 138 | 139 | if (errno == ERANGE) 140 | return false; 141 | 142 | *output = static_cast(value); 143 | return true; 144 | } 145 | 146 | bool Convert(const std::string &str, s32 *const output) 147 | { 148 | char *endptr = nullptr; 149 | errno = 0; 150 | 151 | s32 value = strtol(str.c_str(), &endptr, 0); 152 | 153 | if (!endptr || *endptr) 154 | return false; 155 | 156 | if (errno == ERANGE) 157 | return false; 158 | 159 | *output = static_cast(value); 160 | return true; 161 | } 162 | 163 | bool Convert(const std::string &str, u32 *const output) 164 | { 165 | char *endptr = nullptr; 166 | errno = 0; 167 | 168 | u32 value = strtoul(str.c_str(), &endptr, 0); 169 | 170 | if (!endptr || *endptr) 171 | return false; 172 | 173 | if (errno == ERANGE) 174 | return false; 175 | 176 | *output = static_cast(value); 177 | return true; 178 | } 179 | 180 | bool Convert(const std::string &str, s64 *const output) 181 | { 182 | char *endptr = nullptr; 183 | errno = 0; 184 | 185 | s64 value = strtoll(str.c_str(), &endptr, 0); 186 | 187 | if (!endptr || *endptr) 188 | return false; 189 | 190 | if (errno == ERANGE) 191 | return false; 192 | 193 | *output = static_cast(value); 194 | return true; 195 | } 196 | 197 | bool Convert(const std::string &str, u64 *const output) 198 | { 199 | char *endptr = nullptr; 200 | errno = 0; 201 | 202 | u64 value = strtoull(str.c_str(), &endptr, 0); 203 | 204 | if (!endptr || *endptr) 205 | return false; 206 | 207 | if (errno == ERANGE) 208 | return false; 209 | 210 | *output = static_cast(value); 211 | return true; 212 | } 213 | 214 | bool Convert(const std::string &str, float *const output) 215 | { 216 | char *endptr = nullptr; 217 | errno = 0; 218 | 219 | float value = strtof(str.c_str(), &endptr); 220 | 221 | if (!endptr || *endptr) 222 | return false; 223 | 224 | if (errno == ERANGE) 225 | return false; 226 | 227 | *output = static_cast(value); 228 | return true; 229 | } 230 | 231 | bool Convert(const std::string &str, double *const output) 232 | { 233 | char *endptr = nullptr; 234 | errno = 0; 235 | 236 | double value = strtod(str.c_str(), &endptr); 237 | 238 | if (!endptr || *endptr) 239 | return false; 240 | 241 | if (errno == ERANGE) 242 | return false; 243 | 244 | *output = static_cast(value); 245 | return true; 246 | } 247 | 248 | bool Convert(const std::string &str, bool *const output) 249 | { 250 | if ("1" == str || !_stricmp("true", str.c_str())) 251 | *output = true; 252 | else if ("0" == str || !_stricmp("false", str.c_str())) 253 | *output = false; 254 | else 255 | return false; 256 | 257 | return true; 258 | } 259 | 260 | bool Convert(const std::string &str, long *const output) 261 | { 262 | char *endptr = nullptr; 263 | errno = 0; 264 | 265 | long value = strtol(str.c_str(), &endptr, 0); 266 | 267 | if (!endptr || *endptr) 268 | return false; 269 | 270 | if (errno == ERANGE) 271 | return false; 272 | 273 | *output = static_cast(value); 274 | return true; 275 | } 276 | 277 | bool Convert(const std::string &str, unsigned long *const output) 278 | { 279 | char *endptr = nullptr; 280 | errno = 0; 281 | 282 | unsigned long value = strtoul(str.c_str(), &endptr, 0); 283 | 284 | if (!endptr || *endptr) 285 | return false; 286 | 287 | if (errno == ERANGE) 288 | return false; 289 | 290 | *output = static_cast(value); 291 | return true; 292 | } 293 | 294 | std::string UTF16ToUTF8(const std::wstring& input) 295 | { 296 | auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), (int)input.size(), nullptr, 0, nullptr, nullptr); 297 | 298 | std::string output; 299 | output.resize(size); 300 | 301 | if (size == 0 || size != WideCharToMultiByte(CP_UTF8, 0, input.data(), (int)input.size(), &output[0], (int)output.size(), nullptr, nullptr)) 302 | { 303 | output.clear(); 304 | } 305 | 306 | return output; 307 | } 308 | 309 | std::wstring CPToUTF16(u32 code_page, const std::string& input) 310 | { 311 | auto const size = MultiByteToWideChar(code_page, 0, input.data(), (int)input.size(), nullptr, 0); 312 | 313 | std::wstring output; 314 | output.resize(size); 315 | 316 | if (size == 0 || size != MultiByteToWideChar(code_page, 0, input.data(), (int)input.size(), &output[0], (int)output.size())) 317 | { 318 | output.clear(); 319 | } 320 | 321 | return output; 322 | } 323 | 324 | std::wstring UTF8ToUTF16(const std::string& input) 325 | { 326 | return CPToUTF16(CP_UTF8, input); 327 | } 328 | 329 | std::string SHIFTJISToUTF8(const std::string& input) 330 | { 331 | return UTF16ToUTF8(CPToUTF16(932, input)); 332 | } 333 | 334 | std::string CP1252ToUTF8(const std::string& input) 335 | { 336 | return UTF16ToUTF8(CPToUTF16(1252, input)); 337 | } 338 | -------------------------------------------------------------------------------- /common/StringUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Types.h" 4 | 5 | #include 6 | #include 7 | 8 | std::string StringFormat(const char* format, ...); 9 | std::wstring StringFormat(const wchar_t* format, ...); 10 | 11 | bool CharArrayFormatV(char* out, int outsize, const char* format, va_list args); 12 | bool CharArrayFormatV(wchar_t* out, int outsize, const wchar_t* format, va_list args); 13 | 14 | bool Convert(const std::string &str, s8 *const output); 15 | bool Convert(const std::string &str, u8 *const output); 16 | bool Convert(const std::string &str, s16 *const output); 17 | bool Convert(const std::string &str, u16 *const output); 18 | bool Convert(const std::string &str, s32 *const output); 19 | bool Convert(const std::string &str, u32 *const output); 20 | bool Convert(const std::string &str, s64 *const output); 21 | bool Convert(const std::string &str, u64 *const output); 22 | bool Convert(const std::string &str, float *const output); 23 | bool Convert(const std::string &str, double *const output); 24 | bool Convert(const std::string &str, bool *output); 25 | 26 | bool Convert(const std::string &str, long *const output); 27 | bool Convert(const std::string &str, unsigned long *const output); 28 | 29 | std::string CP1252ToUTF8(const std::string& str); 30 | std::string UTF16ToUTF8(const std::wstring& str); 31 | std::wstring UTF8ToUTF16(const std::string& str); 32 | 33 | #ifdef _UNICODE 34 | inline std::string TStrToUTF8(const std::wstring& str) 35 | { 36 | return UTF16ToUTF8(str); 37 | } 38 | 39 | inline std::wstring UTF8ToTStr(const std::string& str) 40 | { 41 | return UTF8ToUTF16(str); 42 | } 43 | #else 44 | inline std::string TStrToUTF8(const std::string& str) 45 | { 46 | return str; 47 | } 48 | 49 | inline std::string UTF8ToTStr(const std::string& str) 50 | { 51 | return str; 52 | } 53 | #endif 54 | -------------------------------------------------------------------------------- /common/Types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef int8_t s8; 6 | typedef uint8_t u8; 7 | typedef int16_t s16; 8 | typedef uint16_t u16; 9 | typedef int32_t s32; 10 | typedef uint32_t u32; 11 | typedef int64_t s64; 12 | typedef uint64_t u64; 13 | 14 | typedef intptr_t sPointer; 15 | typedef uintptr_t uPointer; 16 | 17 | typedef float f32; 18 | typedef double f64; 19 | 20 | union Word 21 | { 22 | f32 float32; 23 | u32 bits32; 24 | u16 bits16[2]; 25 | u8 bits8[4]; 26 | }; 27 | 28 | union DWord 29 | { 30 | u64 bits64; 31 | f64 float64; 32 | f32 float32[2]; 33 | u32 bits32[2]; 34 | u16 bits16[4]; 35 | u8 bits8[8]; 36 | 37 | Word word[2]; 38 | }; 39 | 40 | union QWord 41 | { 42 | u64 bits64[2]; 43 | f64 float64[2]; 44 | f32 float32[4]; 45 | u32 bits32[4]; 46 | u16 bits16[8]; 47 | u8 bits8[16]; 48 | 49 | Word word[4]; 50 | DWord dword[2]; 51 | }; 52 | -------------------------------------------------------------------------------- /common/Utils.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "Common.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #pragma comment(lib, "shlwapi.lib") 10 | #pragma comment(lib, "shell32.lib") 11 | 12 | HMODULE LoadLibrarySystem(const std::string& library_name, std::string* out_path) 13 | { 14 | std::unique_ptr system_directory(new char[MAX_PATH]); 15 | GetSystemDirectoryA(system_directory.get(), MAX_PATH); 16 | 17 | std::string lib_path(system_directory.get()); 18 | StringPathAppend(&lib_path, library_name); 19 | 20 | if (out_path) 21 | *out_path = lib_path; 22 | 23 | return LoadLibraryA(lib_path.c_str()); 24 | } 25 | 26 | HMODULE LoadLibrarySystem(const std::wstring& library_name, std::wstring* out_path) 27 | { 28 | std::unique_ptr system_directory(new wchar_t[MAX_PATH]); 29 | GetSystemDirectoryW(system_directory.get(), MAX_PATH); 30 | 31 | std::wstring lib_path(system_directory.get()); 32 | StringPathAppend(&lib_path, library_name); 33 | 34 | if (out_path) 35 | *out_path = lib_path; 36 | 37 | return LoadLibraryW(lib_path.c_str()); 38 | } 39 | 40 | HMODULE LoadLibraryCurrent(const std::string& library_name, std::string* out_path) 41 | { 42 | std::string lib_path; 43 | ModuleDirectory(&lib_path); 44 | StringPathAppend(&lib_path, library_name); 45 | 46 | if (out_path) 47 | *out_path = lib_path; 48 | 49 | return LoadLibraryA(lib_path.c_str()); 50 | } 51 | 52 | HMODULE LoadLibraryCurrent(const std::wstring& library_name, std::wstring* out_path) 53 | { 54 | std::wstring lib_path; 55 | ModuleDirectory(&lib_path); 56 | StringPathAppend(&lib_path, library_name); 57 | 58 | if (out_path) 59 | *out_path = lib_path; 60 | 61 | return LoadLibraryW(lib_path.c_str()); 62 | } 63 | 64 | bool FileExist(const std::string& path) 65 | { 66 | HANDLE hFile = CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 67 | if (hFile != INVALID_HANDLE_VALUE) 68 | { 69 | CloseHandle(hFile); 70 | return true; 71 | } 72 | return false; 73 | } 74 | 75 | bool FileExist(const std::wstring& path) 76 | { 77 | HANDLE hFile = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 78 | if (hFile != INVALID_HANDLE_VALUE) 79 | { 80 | CloseHandle(hFile); 81 | return true; 82 | } 83 | return false; 84 | } 85 | 86 | bool CheckCommonDirectory(std::string* outpath, const std::string& dirname) 87 | { 88 | std::unique_ptr path(new char[MAX_PATH]); 89 | if (SHGetFolderPathA(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, path.get()) == S_OK) 90 | { 91 | PathAppendA(path.get(), dirname.c_str()); 92 | PathAppendA(path.get(), outpath->c_str()); 93 | if (FileExist(path.get())) 94 | { 95 | *outpath = path.get(); 96 | return true; 97 | } 98 | } 99 | return false; 100 | } 101 | 102 | bool CheckCommonDirectory(std::wstring* outpath, const std::wstring& dirname) 103 | { 104 | std::unique_ptr path(new WCHAR[MAX_PATH]); 105 | if (SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, path.get()) == S_OK) 106 | { 107 | PathAppendW(path.get(), dirname.c_str()); 108 | PathAppendW(path.get(), outpath->c_str()); 109 | if (FileExist(path.get())) 110 | { 111 | *outpath = path.get(); 112 | return true; 113 | } 114 | } 115 | return false; 116 | } 117 | 118 | bool FullPathFromPath(std::string* path, const std::string& in_path) 119 | { 120 | if (PathIsRelativeA(in_path.c_str())) 121 | { 122 | std::unique_ptr buffer(new char[MAX_PATH]); 123 | if (GetModuleFileNameA(CurrentModule(), buffer.get(), MAX_PATH) && 124 | PathRemoveFileSpecA(buffer.get())) 125 | { 126 | PathAppendA(buffer.get(), in_path.c_str()); 127 | *path = buffer.get(); 128 | } 129 | } 130 | else 131 | { 132 | *path = in_path; 133 | } 134 | 135 | if (FileExist(*path)) 136 | return true; 137 | 138 | return false; 139 | } 140 | 141 | bool FullPathFromPath(std::wstring* path, const std::wstring& in_path) 142 | { 143 | if (PathIsRelativeW(in_path.c_str())) 144 | { 145 | std::unique_ptr buffer(new WCHAR[MAX_PATH]); 146 | if (GetModuleFileNameW(CurrentModule(), buffer.get(), MAX_PATH) && 147 | PathRemoveFileSpecW(buffer.get())) 148 | { 149 | PathAppendW(buffer.get(), in_path.c_str()); 150 | *path = buffer.get(); 151 | } 152 | } 153 | else 154 | { 155 | *path = in_path; 156 | } 157 | 158 | if (FileExist(*path)) 159 | return true; 160 | 161 | return false; 162 | } 163 | 164 | bool StringPathAppend(std::string* path, const std::string& more) 165 | { 166 | std::unique_ptr buffer(new char[MAX_PATH]); 167 | *path = PathCombineA(buffer.get(), path->c_str(), more.c_str()); 168 | return !path->empty(); 169 | } 170 | 171 | bool StringPathAppend(std::wstring* path, const std::wstring& more) 172 | { 173 | std::unique_ptr buffer(new wchar_t[MAX_PATH]); 174 | *path = PathCombineW(buffer.get(), path->c_str(), more.c_str()); 175 | return !path->empty(); 176 | } 177 | 178 | bool ModulePath(std::string* out, HMODULE hModule) 179 | { 180 | std::unique_ptr buffer(new char[MAX_PATH]); 181 | GetModuleFileNameA(hModule, buffer.get(), MAX_PATH); 182 | *out = buffer.get(); 183 | return !out->empty(); 184 | } 185 | 186 | bool ModulePath(std::wstring* out, HMODULE hModule) 187 | { 188 | std::unique_ptr buffer(new wchar_t[MAX_PATH]); 189 | GetModuleFileNameW(hModule, buffer.get(), MAX_PATH); 190 | *out = buffer.get(); 191 | return !out->empty(); 192 | } 193 | 194 | bool ModuleDirectory(std::string* out, HMODULE hModule) 195 | { 196 | std::unique_ptr buffer(new char[MAX_PATH]); 197 | GetModuleFileNameA(hModule, buffer.get(), MAX_PATH); 198 | PathRemoveFileSpecA(buffer.get()); 199 | *out = buffer.get(); 200 | return !out->empty(); 201 | } 202 | 203 | bool ModuleDirectory(std::wstring* out, HMODULE hModule) 204 | { 205 | std::unique_ptr buffer(new wchar_t[MAX_PATH]); 206 | GetModuleFileNameW(hModule, buffer.get(), MAX_PATH); 207 | PathRemoveFileSpecW(buffer.get()); 208 | *out = buffer.get(); 209 | return !out->empty(); 210 | } 211 | 212 | bool ModuleFileName(std::string* out, HMODULE hModule) 213 | { 214 | std::unique_ptr buffer(new char[MAX_PATH]); 215 | GetModuleFileNameA(hModule, buffer.get(), MAX_PATH); 216 | *out = PathFindFileNameA(buffer.get()); 217 | return !out->empty(); 218 | } 219 | 220 | bool ModuleFileName(std::wstring* out, HMODULE hModule) 221 | { 222 | std::unique_ptr buffer(new wchar_t[MAX_PATH]); 223 | GetModuleFileNameW(hModule, buffer.get(), MAX_PATH); 224 | *out = PathFindFileNameW(buffer.get()); 225 | return !out->empty(); 226 | } 227 | 228 | void StringToGUID(GUID* id, const std::string& szBuf) 229 | { 230 | const char* p = szBuf.c_str(); 231 | if (strchr(p, '{')) p++; 232 | 233 | u32 d1; 234 | s32 d2, d3; 235 | s32 b[8]; 236 | 237 | if (sscanf_s(p, "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", 238 | &d1, &d2, &d3, &b[0], &b[1], &b[2], &b[3], &b[4], &b[5], &b[6], &b[7]) != 11) 239 | { 240 | *id = GUID_NULL; 241 | return; 242 | } 243 | 244 | id->Data1 = d1; 245 | id->Data2 = (u16)d2; 246 | id->Data3 = (u16)d3; 247 | 248 | for (int i = 0; i < 8; ++i) 249 | id->Data4[i] = (u8)b[i]; 250 | 251 | return; 252 | } 253 | 254 | void StringToGUID(GUID* id, const std::wstring& szBuf) 255 | { 256 | const wchar_t* p = szBuf.c_str(); 257 | if (wcschr(p, L'{')) p++; 258 | 259 | u32 d1; 260 | s32 d2, d3; 261 | s32 b[8]; 262 | 263 | if (swscanf_s(p, L"%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", 264 | &d1, &d2, &d3, &b[0], &b[1], &b[2], &b[3], &b[4], &b[5], &b[6], &b[7]) != 11) 265 | { 266 | *id = GUID_NULL; 267 | return; 268 | } 269 | 270 | id->Data1 = d1; 271 | id->Data2 = (u16)d2; 272 | id->Data3 = (u16)d3; 273 | 274 | for (int i = 0; i < 8; ++i) 275 | id->Data4[i] = (u8)b[i]; 276 | 277 | return; 278 | } 279 | 280 | bool GUIDtoString(std::string* out, const GUID &g) 281 | { 282 | std::unique_ptr buffer(new char[40]); 283 | sprintf_s(buffer.get(), 40, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", 284 | g.Data1, g.Data2, g.Data3, g.Data4[0], g.Data4[1], g.Data4[2], g.Data4[3], g.Data4[4], g.Data4[5], g.Data4[6], g.Data4[7]); 285 | 286 | *out = buffer.get(); 287 | return !out->empty(); 288 | } 289 | 290 | bool GUIDtoString(std::wstring* out, const GUID &g) 291 | { 292 | std::unique_ptr buffer(new wchar_t[40]); 293 | swprintf_s(buffer.get(), 40, L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", 294 | g.Data1, g.Data2, g.Data3, g.Data4[0], g.Data4[1], g.Data4[2], g.Data4[3], g.Data4[4], g.Data4[5], g.Data4[6], g.Data4[7]); 295 | 296 | *out = buffer.get(); 297 | return !out->empty(); 298 | } 299 | 300 | std::wstring thisModuleDirectory() 301 | { 302 | std::unique_ptr buffer(new WCHAR[MAX_PATH]); 303 | HMODULE hm; 304 | if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 305 | (LPWSTR)&thisModuleDirectory, &hm)) { 306 | return std::wstring(); 307 | } 308 | 309 | GetModuleFileNameW(hm, buffer.get(), MAX_PATH); 310 | PathRemoveFileSpecW(buffer.get()); 311 | return std::wstring(buffer.get()); 312 | } 313 | 314 | std::wstring getSystemDirectoryString() 315 | { 316 | std::unique_ptr systemDirectoryBuffer(new WCHAR[MAX_PATH]); 317 | GetSystemDirectoryW(systemDirectoryBuffer.get(), MAX_PATH); 318 | return std::wstring(systemDirectoryBuffer.get()); 319 | } -------------------------------------------------------------------------------- /common/Utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "Types.h" 6 | 7 | HMODULE LoadLibrarySystem(const std::string& library_name, std::string* out_path); 8 | HMODULE LoadLibrarySystem(const std::wstring& library_name, std::wstring* out_path); 9 | HMODULE LoadLibraryCurrent(const std::string& library_name, std::string* out_path); 10 | HMODULE LoadLibraryCurrent(const std::wstring& library_name, std::wstring* out_path); 11 | 12 | inline HMODULE& CurrentModule() 13 | { 14 | static HMODULE hModule = 0; 15 | if (!hModule) 16 | GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)&hModule, &hModule); 17 | return hModule; 18 | } 19 | 20 | bool FileExist(const std::string& path); 21 | bool FileExist(const std::wstring& path); 22 | bool CheckCommonDirectory(std::string* outpath, const std::string& dirname); 23 | bool CheckCommonDirectory(std::wstring* outpath, const std::wstring& dirname); 24 | bool FullPathFromPath(std::string* fullpath, const std::string& name); 25 | bool FullPathFromPath(std::wstring* fullpath, const std::wstring& name); 26 | bool StringPathAppend(std::string* path, const std::string& more); 27 | bool StringPathAppend(std::wstring* path, const std::wstring& more); 28 | bool ModulePath(std::string* out, HMODULE hModule = NULL); 29 | bool ModulePath(std::wstring* out, HMODULE hModule = NULL); 30 | bool ModuleDirectory(std::string* out, HMODULE hModule = NULL); 31 | bool ModuleDirectory(std::wstring* out, HMODULE hModule = NULL); 32 | bool ModuleFileName(std::string* out, HMODULE hModule = NULL); 33 | bool ModuleFileName(std::wstring* out, HMODULE hModule = NULL); 34 | std::wstring thisModuleDirectory(); 35 | std::wstring getSystemDirectoryString(); 36 | -------------------------------------------------------------------------------- /common/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | 10 | #define _WIN32_WINNT _WIN32_WINNT_VISTA 11 | 12 | #include 13 | 14 | #define DIRECTINPUT_VERSION 0x0800 15 | -------------------------------------------------------------------------------- /dinput8/DirectInputModuleManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include "SimpleIni.h" 8 | 9 | #include "Common.h" 10 | 11 | class DirectInputModuleManager : NonCopyable 12 | { 13 | public: 14 | HRESULT(WINAPI* DirectInput8Create)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter); 15 | HRESULT(WINAPI* DllCanUnloadNow)(void); 16 | HRESULT(WINAPI* DllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID FAR* ppv); 17 | HRESULT(WINAPI* DllRegisterServer)(void); 18 | HRESULT(WINAPI* DllUnregisterServer)(void); 19 | 20 | DirectInputModuleManager() 21 | { 22 | std::wstring loadedModulePath, chainLoadFileName; 23 | CSimpleIniW ini; 24 | ini.SetAllowEmptyValues(false); 25 | std::wstring inipath(L"devreorder.ini"); 26 | SI_Error err = ini.LoadFile(inipath.c_str()); 27 | LogSystem(); 28 | 29 | if (err < 0) { 30 | CheckCommonDirectory(&inipath, L"devreorder"); 31 | err = ini.LoadFile(inipath.c_str()); 32 | } 33 | 34 | if (err >= 0) { 35 | chainLoadFileName = ini.GetValue(L"\0", L"ChainLoadFileName", L""); 36 | } 37 | 38 | if (chainLoadFileName.size() > 0) { 39 | PrintLog(L"devreorder: chain loading %s", chainLoadFileName.c_str()); 40 | FullPathFromPath(&loadedModulePath, chainLoadFileName); 41 | m_module = LoadLibraryW(loadedModulePath.c_str()); 42 | } else { 43 | // Check to make sure we're not in the system32 directory and trying to load ourselves: 44 | std::wstring modulePath = thisModuleDirectory(); 45 | std::transform(modulePath.begin(), modulePath.end(), modulePath.begin(), towlower); 46 | 47 | std::wstring systemDirectory = getSystemDirectoryString(); 48 | std::transform(systemDirectory.begin(), systemDirectory.end(), systemDirectory.begin(), towlower); 49 | 50 | std::wstring dinput8orgPath = systemDirectory; 51 | StringPathAppend(&dinput8orgPath, L"dinput8org.dll"); 52 | 53 | // If we are in system32 then the user needs to have renamed the original dll to dinput8org.dll 54 | // Also if that file exists we should just use that since that way we know we're not loading another 55 | // copy of this wrapper DLL 56 | if (modulePath == systemDirectory || FileExist(dinput8orgPath)) { 57 | m_module = LoadLibrarySystem(L"dinput8org.dll", &loadedModulePath); 58 | } else { 59 | m_module = LoadLibrarySystem(L"dinput8.dll", &loadedModulePath); 60 | } 61 | } 62 | 63 | if (!m_module) 64 | { 65 | HRESULT hr = GetLastError(); 66 | std::unique_ptr error_msg(new WCHAR[MAX_PATH]); 67 | swprintf_s(error_msg.get(), MAX_PATH, L"Cannot load \"%s\" error: 0x%x", loadedModulePath.c_str(), hr); 68 | MessageBoxW(NULL, error_msg.get(), L"Error", MB_ICONERROR); 69 | exit(hr); 70 | } 71 | else 72 | { 73 | PrintLog("devreorder: Loaded \"%s\"", UTF16ToUTF8(loadedModulePath).c_str()); 74 | } 75 | 76 | GetProcAddress("DirectInput8Create", &DirectInput8Create); 77 | GetProcAddress("DllCanUnloadNow", &DllCanUnloadNow); 78 | GetProcAddress("DllGetClassObject", &DllGetClassObject); 79 | GetProcAddress("DllRegisterServer", &DllRegisterServer); 80 | GetProcAddress("DllUnregisterServer", &DllUnregisterServer); 81 | } 82 | 83 | ~DirectInputModuleManager() 84 | { 85 | if (m_module) 86 | { 87 | std::string xinput_path; 88 | ModulePath(&xinput_path, m_module); 89 | PrintLog("devreorder: Unloading %s", xinput_path.c_str()); 90 | FreeLibrary(m_module); 91 | } 92 | } 93 | 94 | static DirectInputModuleManager& Get() 95 | { 96 | static DirectInputModuleManager instance; 97 | return instance; 98 | } 99 | 100 | private: 101 | template 102 | inline void GetProcAddress(const char* funcname, T* ppfunc) 103 | { 104 | *ppfunc = reinterpret_cast(::GetProcAddress(m_module, funcname)); 105 | } 106 | 107 | HMODULE m_module; 108 | }; 109 | -------------------------------------------------------------------------------- /dinput8/dinput8.cpp: -------------------------------------------------------------------------------- 1 | 2 | #define CINTERFACE 3 | #include "stdafx.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "dinput8.h" 13 | #include "Common.h" 14 | #include "Logger.h" 15 | #include "Utils.h" 16 | #include "DirectInputModuleManager.h" 17 | #include "SimpleIni.h" 18 | 19 | using namespace std; 20 | 21 | enum MatchType { 22 | kNoMatch, 23 | kNameMatch, 24 | kGUIDMatch, 25 | kDeviceInstanceIDMatch, 26 | }; 27 | 28 | HRESULT(STDMETHODCALLTYPE *TrueEnumDevicesA) (LPDIRECTINPUT8A This, DWORD dwDevType, LPDIENUMDEVICESCALLBACKA lpCallback, LPVOID pvRef, DWORD dwFlags) = nullptr; 29 | HRESULT(STDMETHODCALLTYPE *TrueEnumDevicesW) (LPDIRECTINPUT8W This, DWORD dwDevType, LPDIENUMDEVICESCALLBACKW lpCallback, LPVOID pvRef, DWORD dwFlags) = nullptr; 30 | HRESULT(STDMETHODCALLTYPE *EnumDevicesA) (LPDIRECTINPUT8A This, DWORD dwDevType, LPDIENUMDEVICESCALLBACKA lpCallback, LPVOID pvRef, DWORD dwFlags) = nullptr; 31 | HRESULT(STDMETHODCALLTYPE *EnumDevicesW) (LPDIRECTINPUT8W This, DWORD dwDevType, LPDIENUMDEVICESCALLBACKW lpCallback, LPVOID pvRef, DWORD dwFlags) = nullptr; 32 | 33 | struct EnumCallbackUserData { 34 | void* di; 35 | void* enumData; 36 | }; 37 | 38 | vector loadAllKeysFromSectionOfIni(const wstring §ion) 39 | { 40 | vector result; 41 | CSimpleIniW ini; 42 | ini.SetAllowEmptyValues(true); 43 | wstring inipath(L"devreorder.ini"); 44 | SI_Error err = ini.LoadFile(inipath.c_str()); 45 | 46 | if (err < 0) { 47 | CheckCommonDirectory(&inipath, L"devreorder"); 48 | err = ini.LoadFile(inipath.c_str()); 49 | 50 | if (err < 0) { 51 | PrintLog("devreorder error: devreorder.ini file found"); 52 | return result; 53 | } else { 54 | PrintLog("devreorder: using system-wide devreorder.ini"); 55 | } 56 | } else { 57 | PrintLog("devreorder: using program-specific devreorder.ini"); 58 | } 59 | 60 | CSimpleIniW::TNamesDepend keys; 61 | ini.GetAllKeys(section.c_str(), keys); 62 | keys.sort(CSimpleIniW::Entry::LoadOrder()); 63 | 64 | for (CSimpleIniW::TNamesDepend::iterator it = keys.begin(); it != keys.end(); ++it) { 65 | result.push_back(it->pItem); 66 | } 67 | 68 | return result; 69 | } 70 | 71 | vector & sortedControllersW() 72 | { 73 | static vector result; 74 | static bool needToInitialize = true; 75 | 76 | if (needToInitialize) { 77 | needToInitialize = false; 78 | result = loadAllKeysFromSectionOfIni(L"order"); 79 | } 80 | 81 | return result; 82 | } 83 | 84 | vector & sortedControllersA() 85 | { 86 | static vector result; 87 | static bool needToInitialize = true; 88 | 89 | if (needToInitialize) { 90 | vector &wideList = sortedControllersW(); 91 | 92 | for (unsigned int i = 0; i < wideList.size(); ++i) { 93 | result.push_back(UTF16ToUTF8(wideList[i])); 94 | } 95 | 96 | needToInitialize = false; 97 | } 98 | 99 | return result; 100 | } 101 | 102 | vector & hiddenControllersW() 103 | { 104 | static vector result; 105 | static bool needToInitialize = true; 106 | 107 | if (needToInitialize) { 108 | result = loadAllKeysFromSectionOfIni(L"hidden"); 109 | needToInitialize = false; 110 | } 111 | 112 | return result; 113 | } 114 | 115 | vector & hiddenControllersA() 116 | { 117 | static vector result; 118 | static bool needToInitialize = true; 119 | 120 | if (needToInitialize) { 121 | vector &wideList = hiddenControllersW(); 122 | 123 | for(unsigned int i = 0; i < wideList.size(); ++i) { 124 | result.push_back(UTF16ToUTF8(wideList[i])); 125 | } 126 | 127 | needToInitialize = false; 128 | } 129 | 130 | return result; 131 | } 132 | 133 | vector & visibleControllersW() 134 | { 135 | static vector result; 136 | static bool needToInitialize = true; 137 | 138 | if (needToInitialize) { 139 | result = loadAllKeysFromSectionOfIni(L"visible"); 140 | needToInitialize = false; 141 | } 142 | 143 | return result; 144 | } 145 | 146 | vector & visibleControllersA() 147 | { 148 | static vector result; 149 | static bool needToInitialize = true; 150 | 151 | if (needToInitialize) { 152 | vector &wideList = visibleControllersW(); 153 | 154 | for (unsigned int i = 0; i < wideList.size(); ++i) { 155 | result.push_back(UTF16ToUTF8(wideList[i])); 156 | } 157 | 158 | needToInitialize = false; 159 | } 160 | 161 | return result; 162 | } 163 | 164 | vector & ignoredProcessesW() 165 | { 166 | static vector result; 167 | static bool needToInitialize = true; 168 | 169 | if (needToInitialize) { 170 | result = loadAllKeysFromSectionOfIni(L"ignored processes"); 171 | needToInitialize = false; 172 | } 173 | 174 | return result; 175 | } 176 | 177 | vector & ignoredProcessesA() 178 | { 179 | static vector result; 180 | static bool needToInitialize = true; 181 | 182 | if (needToInitialize) { 183 | vector &wideList = ignoredProcessesW(); 184 | 185 | for (unsigned int i = 0; i < wideList.size(); ++i) { 186 | result.push_back(UTF16ToUTF8(wideList[i])); 187 | } 188 | 189 | needToInitialize = false; 190 | } 191 | 192 | return result; 193 | } 194 | 195 | template 196 | struct DeviceEnumData { 197 | list nonsorted; 198 | vector > sorted; 199 | }; 200 | 201 | string trim(const string &str) 202 | { 203 | size_t first = str.find_first_not_of(' '); 204 | 205 | // string is empty or nothing but whitespace 206 | if (string::npos == first) { 207 | return string(); 208 | } 209 | 210 | size_t last = str.find_last_not_of(' '); 211 | return str.substr(first, (last - first + 1)); 212 | } 213 | 214 | wstring trim(const wstring &str) 215 | { 216 | size_t first = str.find_first_not_of(' '); 217 | 218 | // string is empty or nothing but whitespace 219 | if (wstring::npos == first) { 220 | return wstring(); 221 | } 222 | 223 | size_t last = str.find_last_not_of(' '); 224 | return str.substr(first, (last - first + 1)); 225 | } 226 | 227 | string toLower(const string &str) 228 | { 229 | string result = str; 230 | transform(result.begin(), result.end(), result.begin(), ::tolower); 231 | return result; 232 | } 233 | 234 | wstring toLower(const wstring &str) 235 | { 236 | wstring result = str; 237 | transform(result.begin(), result.end(), result.begin(), ::tolower); 238 | return result; 239 | } 240 | 241 | string GUIDToString(const GUID &guid) 242 | { 243 | CHAR result[40]; 244 | sprintf_s(result, 40, "{%08lx-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx}", 245 | guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], 246 | guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); 247 | return string(result); 248 | } 249 | 250 | wstring GUIDToWString(const GUID &guid) 251 | { 252 | WCHAR result[40]; 253 | swprintf_s(result, 40, L"{%08lx-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx}", 254 | guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], 255 | guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); 256 | return wstring(result); 257 | } 258 | 259 | 260 | std::wstring getDeviceInstanceIdProperty(const DIPROPGUIDANDPATH& iap) 261 | { 262 | DEVPROPTYPE propertyType; 263 | ULONG propertySize = 0; 264 | CONFIGRET cr = CM_Get_Device_Interface_PropertyW(iap.wszPath, &DEVPKEY_Device_InstanceId, &propertyType, nullptr, &propertySize, 0); 265 | 266 | if (cr != CR_BUFFER_SMALL) { 267 | return std::wstring(); 268 | } 269 | 270 | std::wstring deviceId; 271 | deviceId.resize(propertySize); 272 | cr = ::CM_Get_Device_Interface_PropertyW(iap.wszPath, &DEVPKEY_Device_InstanceId, &propertyType, (PBYTE)deviceId.data(), &propertySize, 0); 273 | 274 | if (cr != CR_SUCCESS) { 275 | return std::wstring(); 276 | } 277 | 278 | // There may be trailing null characters in the string that we need to remove so we can append to it: 279 | deviceId.erase(std::find(deviceId.begin(), deviceId.end(), L'\0'), deviceId.end()); 280 | 281 | return deviceId; 282 | } 283 | 284 | void getDeviceInstanceId(LPCDIDEVICEINSTANCEA deviceInstance, EnumCallbackUserData *userData, string &deviceInstanceId) 285 | { 286 | if (deviceInstanceId.size() > 0) { 287 | // Already got the device instance ID 288 | return; 289 | } 290 | 291 | LPDIRECTINPUT8A di = (LPDIRECTINPUT8A)userData->di; 292 | IDirectInputDevice8A *device; 293 | IDirectInput_CreateDevice(di, deviceInstance->guidInstance, &device, NULL); 294 | 295 | DIPROPGUIDANDPATH iap = {}; 296 | iap.diph.dwSize = sizeof(DIPROPGUIDANDPATH); 297 | iap.diph.dwHeaderSize = sizeof(DIPROPHEADER); 298 | iap.diph.dwHow = DIPH_DEVICE; 299 | 300 | std::wstring deviceIdWide; 301 | 302 | if (SUCCEEDED(IDirectInputDevice_GetProperty(device, DIPROP_GUIDANDPATH, &iap.diph))) { 303 | deviceIdWide = trim(toLower(getDeviceInstanceIdProperty(iap))); 304 | deviceIdWide.insert(0, L"<"); 305 | deviceIdWide.append(L">"); 306 | } else { 307 | PrintLog("Error: failed to get device instance ID for: %s", deviceInstance->tszProductName); 308 | // guaranteed not to match anything 309 | deviceIdWide = L"!"; 310 | } 311 | 312 | using convertType = std::codecvt_utf8; 313 | std::wstring_convert converter; 314 | deviceInstanceId = converter.to_bytes(deviceIdWide); 315 | 316 | IDirectInputDevice_Release(device); 317 | } 318 | 319 | void getDeviceInstanceId(LPCDIDEVICEINSTANCEW deviceInstance, EnumCallbackUserData* userData, wstring& deviceInstanceId) 320 | { 321 | if (deviceInstanceId.size() > 0) { 322 | // Already got the device instance ID 323 | return; 324 | } 325 | 326 | LPDIRECTINPUT8W di = (LPDIRECTINPUT8W)userData->di; 327 | IDirectInputDevice8W *device; 328 | IDirectInput_CreateDevice(di, deviceInstance->guidInstance, &device, NULL); 329 | 330 | DIPROPGUIDANDPATH iap = {}; 331 | iap.diph.dwSize = sizeof(DIPROPGUIDANDPATH); 332 | iap.diph.dwHeaderSize = sizeof(DIPROPHEADER); 333 | iap.diph.dwHow = DIPH_DEVICE; 334 | 335 | if (SUCCEEDED(IDirectInputDevice_GetProperty(device, DIPROP_GUIDANDPATH, &iap.diph))) { 336 | deviceInstanceId = trim(toLower(getDeviceInstanceIdProperty(iap))); 337 | deviceInstanceId.insert(0, L"<"); 338 | deviceInstanceId.append(L">"); 339 | } else { 340 | PrintLog(L"Error: failed to get device instance ID for: %s", deviceInstance->tszProductName); 341 | deviceInstanceId = L"!"; 342 | } 343 | 344 | IDirectInputDevice_Release(device); 345 | } 346 | 347 | bool currentProcessIsIgnored() 348 | { 349 | WCHAR currentProcessPathCStr[MAX_PATH]; 350 | DWORD size = GetModuleFileName(NULL, currentProcessPathCStr, MAX_PATH); 351 | wstring processFileName = currentProcessPathCStr; 352 | const size_t lastSlashIndex = processFileName.find_last_of(L"\\/"); 353 | 354 | if (lastSlashIndex != std::string::npos) { 355 | processFileName.erase(0, lastSlashIndex + 1); 356 | } 357 | 358 | processFileName = trim(toLower(processFileName)); 359 | PrintLog(L"Current process name: %s", processFileName.c_str()); 360 | 361 | std::vector ignored = ignoredProcessesW(); 362 | 363 | PrintLog(L"Ignored list:"); 364 | for (auto it = ignored.begin(); it != ignored.end(); ++it) { 365 | PrintLog(L" - %s", it->c_str()); 366 | 367 | if (trim(toLower(*it)) == processFileName) { 368 | PrintLog(L" found match!"); 369 | return true; 370 | } 371 | } 372 | 373 | return false; 374 | } 375 | 376 | MatchType deviceMatchesEntry(LPCDIDEVICEINSTANCEA deviceInstance, EnumCallbackUserData* userData, string &deviceInstanceId, const string &entry) 377 | { 378 | string trimmedEntry = trim(entry); 379 | string deviceIdentifier; 380 | 381 | if (entry.length() == 0) { 382 | return kNoMatch; 383 | } 384 | 385 | if (entry[0] == '{') { 386 | deviceIdentifier = GUIDToString(deviceInstance->guidInstance); 387 | trimmedEntry = toLower(trimmedEntry); 388 | return (deviceIdentifier == trimmedEntry) ? kGUIDMatch : kNoMatch; 389 | } else if (entry[0] == '<') { 390 | trimmedEntry = toLower(trimmedEntry); 391 | getDeviceInstanceId(deviceInstance, userData, deviceInstanceId); 392 | PrintLog(" a: %s", trimmedEntry.c_str()); 393 | PrintLog(" b: %s", deviceInstanceId.c_str()); 394 | return (deviceInstanceId == trimmedEntry) ? kDeviceInstanceIDMatch : kNoMatch; 395 | } else { 396 | deviceIdentifier = trim(deviceInstance->tszProductName); 397 | return (deviceIdentifier == trimmedEntry) ? kNameMatch : kNoMatch; 398 | } 399 | 400 | } 401 | 402 | MatchType deviceMatchesEntry(LPCDIDEVICEINSTANCEW deviceInstance, EnumCallbackUserData* userData, wstring& deviceInstanceId, const wstring &entry) 403 | { 404 | wstring trimmedEntry = trim(entry); 405 | wstring deviceIdentifier; 406 | 407 | if (entry.length() == 0) { 408 | return kNoMatch; 409 | } 410 | 411 | if (entry[0] == L'{') { 412 | deviceIdentifier = GUIDToWString(deviceInstance->guidInstance); 413 | trimmedEntry = toLower(trimmedEntry); 414 | return (deviceIdentifier == trimmedEntry) ? kGUIDMatch : kNoMatch; 415 | } else if (entry[0] == L'<') { 416 | trimmedEntry = toLower(trimmedEntry); 417 | getDeviceInstanceId(deviceInstance, userData, deviceInstanceId); 418 | PrintLog(L" a: %s", trimmedEntry.c_str()); 419 | PrintLog(L" b: %s", deviceInstanceId.c_str()); 420 | return (deviceInstanceId == trimmedEntry) ? kDeviceInstanceIDMatch : kNoMatch; 421 | } else { 422 | deviceIdentifier = trim(deviceInstance->tszProductName); 423 | return (deviceIdentifier == trimmedEntry) ? kNameMatch : kNoMatch; 424 | } 425 | } 426 | 427 | BOOL CALLBACK enumCallbackA(LPCDIDEVICEINSTANCEA deviceInstance, LPVOID userDataPtr) 428 | { 429 | EnumCallbackUserData *userData = (EnumCallbackUserData *)userDataPtr; 430 | DeviceEnumData *enumData = (DeviceEnumData *)userData->enumData; 431 | vector &order = sortedControllersA(); 432 | vector &hidden = hiddenControllersA(); 433 | vector &visible = visibleControllersA(); 434 | string deviceInstanceId; 435 | 436 | for (unsigned int i = 0; i < hidden.size(); ++i) { 437 | if (deviceMatchesEntry(deviceInstance, userData, deviceInstanceId, hidden[i])) { 438 | PrintLog("devreorder: product \"%s\" is hidden", deviceInstance->tszProductName); 439 | return DIENUM_CONTINUE; 440 | } 441 | } 442 | 443 | DWORD deviceType = GET_DIDEVICE_TYPE(deviceInstance->dwDevType); 444 | 445 | if (visible.size() > 0 && deviceType != DI8DEVTYPE_KEYBOARD && deviceType != DI8DEVTYPE_MOUSE 446 | && deviceType != DI8DEVTYPE_SCREENPOINTER) { 447 | bool isVisible = false; 448 | 449 | for (unsigned int i = 0; i < visible.size(); ++i) { 450 | if (deviceMatchesEntry(deviceInstance, userData, deviceInstanceId, visible[i])) { 451 | isVisible = true; 452 | } 453 | } 454 | 455 | if (!isVisible) { 456 | PrintLog("devreorder: product \"%s\" is not in visible section", deviceInstance->tszProductName); 457 | return DIENUM_CONTINUE; 458 | } 459 | } 460 | 461 | int nameMatchIndex = -1; 462 | 463 | for (unsigned int i = 0; i < order.size(); ++i) { 464 | MatchType match = deviceMatchesEntry(deviceInstance, userData, deviceInstanceId, order[i]); 465 | 466 | // Important that we prioritize a match via device instance ID or GUID over a match via name 467 | if (match == kDeviceInstanceIDMatch) { 468 | PrintLog("devreorder: product \"%s\" is sorted up by device instance ID", deviceInstance->tszProductName); 469 | enumData->sorted[i].push_back(*deviceInstance); 470 | return DIENUM_CONTINUE; 471 | 472 | } else if (match == kGUIDMatch) { 473 | PrintLog("devreorder: product \"%s\" is sorted up by GUID", deviceInstance->tszProductName); 474 | enumData->sorted[i].push_back(*deviceInstance); 475 | return DIENUM_CONTINUE; 476 | 477 | } else if (match == kNameMatch) { 478 | nameMatchIndex = i; 479 | } 480 | } 481 | 482 | if (nameMatchIndex != -1) { 483 | PrintLog("devreorder: product \"%s\" is sorted up by name", deviceInstance->tszProductName); 484 | enumData->sorted[nameMatchIndex].push_back(*deviceInstance); 485 | return DIENUM_CONTINUE; 486 | } 487 | 488 | PrintLog("devreorder: product \"%s\" is not sorted differently", deviceInstance->tszProductName); 489 | enumData->nonsorted.push_back(*deviceInstance); 490 | return DIENUM_CONTINUE; 491 | } 492 | 493 | BOOL CALLBACK enumCallbackW(LPCDIDEVICEINSTANCEW deviceInstance, LPVOID userDataPtr) 494 | { 495 | EnumCallbackUserData *userData = (EnumCallbackUserData *)userDataPtr; 496 | DeviceEnumData *enumData = (DeviceEnumData *)userData->enumData; 497 | vector &order = sortedControllersW(); 498 | vector &hidden = hiddenControllersW(); 499 | vector &visible = visibleControllersW(); 500 | wstring deviceInstanceId; 501 | 502 | for (unsigned int i = 0; i < hidden.size(); ++i) { 503 | if (deviceMatchesEntry(deviceInstance, userData, deviceInstanceId, hidden[i])) { 504 | PrintLog(L"devreorder: product \"%s\" is hidden", deviceInstance->tszProductName); 505 | return DIENUM_CONTINUE; 506 | } 507 | } 508 | 509 | DWORD deviceType = GET_DIDEVICE_TYPE(deviceInstance->dwDevType); 510 | 511 | if (visible.size() > 0 && deviceType != DI8DEVTYPE_KEYBOARD && deviceType != DI8DEVTYPE_MOUSE 512 | && deviceType != DI8DEVTYPE_SCREENPOINTER) { 513 | bool isVisible = false; 514 | 515 | for (unsigned int i = 0; i < visible.size(); ++i) { 516 | if (deviceMatchesEntry(deviceInstance, userData, deviceInstanceId, visible[i])) { 517 | isVisible = true; 518 | } 519 | } 520 | 521 | if (!isVisible) { 522 | PrintLog(L"devreorder: product \"%s\" is not in visible section", deviceInstance->tszProductName); 523 | return DIENUM_CONTINUE; 524 | } 525 | } 526 | 527 | int nameMatchIndex = -1; 528 | 529 | for (unsigned int i = 0; i < order.size(); ++i) { 530 | MatchType match = deviceMatchesEntry(deviceInstance, userData, deviceInstanceId, order[i]); 531 | 532 | // Important that we prioritize a match via device instance ID or GUID over a match via name 533 | if (match == kDeviceInstanceIDMatch) { 534 | PrintLog(L"devreorder: product \"%s\" is sorted up by device instance ID", deviceInstance->tszProductName); 535 | enumData->sorted[i].push_back(*deviceInstance); 536 | return DIENUM_CONTINUE; 537 | 538 | } else if (match == kGUIDMatch) { 539 | PrintLog(L"devreorder: product \"%s\" is sorted up by GUID", deviceInstance->tszProductName); 540 | enumData->sorted[i].push_back(*deviceInstance); 541 | return DIENUM_CONTINUE; 542 | 543 | } else if (match == kNameMatch) { 544 | nameMatchIndex = i; 545 | } 546 | } 547 | 548 | if (nameMatchIndex != -1) { 549 | PrintLog(L"devreorder: product \"%s\" is sorted up by name", deviceInstance->tszProductName); 550 | enumData->sorted[nameMatchIndex].push_back(*deviceInstance); 551 | return DIENUM_CONTINUE; 552 | } 553 | 554 | PrintLog(L"devreorder: product \"%s\" is not sorted differently", deviceInstance->tszProductName); 555 | enumData->nonsorted.push_back(*deviceInstance); 556 | return DIENUM_CONTINUE; 557 | } 558 | 559 | HRESULT STDMETHODCALLTYPE HookEnumDevicesA(LPDIRECTINPUT8A This, DWORD dwDevType, LPDIENUMDEVICESCALLBACKA lpCallback, LPVOID pvRef, DWORD dwFlags) 560 | { 561 | vector &order = sortedControllersA(); 562 | DeviceEnumData enumData; 563 | enumData.sorted.resize(order.size()); 564 | 565 | EnumCallbackUserData userData; 566 | userData.enumData = (void *)&enumData; 567 | userData.di = (void *)This; 568 | 569 | PrintLog("devreorder: determining new sorting order for devices"); 570 | HRESULT result = TrueEnumDevicesA(This, dwDevType, enumCallbackA, (LPVOID)&userData, dwFlags); 571 | 572 | if (result != DI_OK) { 573 | return result; 574 | } 575 | 576 | for(unsigned int i = 0; i < enumData.sorted.size(); ++i) { 577 | for (list::iterator it = enumData.sorted[i].begin(); it != enumData.sorted[i].end(); ++it) { 578 | if (lpCallback(&(*it), pvRef) != DIENUM_CONTINUE) { 579 | return result; 580 | } 581 | } 582 | } 583 | 584 | for (list::iterator it = enumData.nonsorted.begin(); it != enumData.nonsorted.end(); ++it) { 585 | if (lpCallback(&(*it), pvRef) != DIENUM_CONTINUE) { 586 | return result; 587 | } 588 | } 589 | 590 | return result; 591 | } 592 | 593 | HRESULT STDMETHODCALLTYPE HookEnumDevicesW(LPDIRECTINPUT8W This, DWORD dwDevType, LPDIENUMDEVICESCALLBACKW lpCallback, LPVOID pvRef, DWORD dwFlags) 594 | { 595 | vector &order = sortedControllersW(); 596 | DeviceEnumData enumData; 597 | enumData.sorted.resize(order.size()); 598 | 599 | EnumCallbackUserData userData; 600 | userData.enumData = (void *)&enumData; 601 | userData.di = (void *)This; 602 | 603 | PrintLog("devreorder: determining new sorting order for devices"); 604 | HRESULT result = TrueEnumDevicesW(This, dwDevType, enumCallbackW, (LPVOID)&userData, dwFlags); 605 | 606 | if (result != DI_OK) { 607 | return result; 608 | } 609 | 610 | for (unsigned int i = 0; i < enumData.sorted.size(); ++i) { 611 | for (list::iterator it = enumData.sorted[i].begin(); it != enumData.sorted[i].end(); ++it) { 612 | if (lpCallback(&(*it), pvRef) != DIENUM_CONTINUE) { 613 | return result; 614 | } 615 | } 616 | } 617 | 618 | for (list::iterator it = enumData.nonsorted.begin(); it != enumData.nonsorted.end(); ++it) { 619 | if (lpCallback(&(*it), pvRef) != DIENUM_CONTINUE) { 620 | return result; 621 | } 622 | } 623 | 624 | return result; 625 | } 626 | 627 | void CreateHooks(REFIID riidltf, LPVOID *realDI) 628 | { 629 | PrintLog("devreorder: in CreateHooks"); 630 | 631 | if (currentProcessIsIgnored()) { 632 | PrintLog("... current process is ignored, not hooking into DirectInput"); 633 | return; 634 | } 635 | 636 | if (IsEqualIID(riidltf, IID_IDirectInput8A)) 637 | { 638 | LPDIRECTINPUT8A pDIA = static_cast(*realDI); 639 | 640 | if (pDIA) 641 | { 642 | PrintLog("devreorder: using ANSI interface"); 643 | if (pDIA->lpVtbl->EnumDevices) 644 | { 645 | EnumDevicesA = pDIA->lpVtbl->EnumDevices; 646 | IH_CreateHook(EnumDevicesA, HookEnumDevicesA, &TrueEnumDevicesA); 647 | IH_EnableHook(EnumDevicesA); 648 | } 649 | } 650 | } 651 | else if (IsEqualIID(riidltf, IID_IDirectInput8W)) 652 | { 653 | LPDIRECTINPUT8W pDIW = static_cast(*realDI); 654 | 655 | if (pDIW) 656 | { 657 | PrintLog("devreorder: using UNICODE interface"); 658 | 659 | if (pDIW->lpVtbl->EnumDevices) 660 | { 661 | EnumDevicesW = pDIW->lpVtbl->EnumDevices; 662 | IH_CreateHook(EnumDevicesW, HookEnumDevicesW, &TrueEnumDevicesW); 663 | IH_EnableHook(EnumDevicesW); 664 | } 665 | } 666 | } 667 | } 668 | 669 | extern "C" HRESULT WINAPI DirectInput8Create(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter) 670 | { 671 | PrintLog("devreorder: Calling hooked DirectInput8Create"); 672 | HRESULT hr = DirectInputModuleManager::Get().DirectInput8Create(hinst, dwVersion, riidltf, ppvOut, punkOuter); 673 | 674 | if (hr != DI_OK) return hr; 675 | 676 | PrintLog("devreorder: in CreateHooks"); 677 | 678 | if (currentProcessIsIgnored()) { 679 | PrintLog("... current process is ignored, not hooking into DirectInput"); 680 | return hr; 681 | } 682 | 683 | CreateHooks(riidltf, ppvOut); 684 | 685 | return hr; 686 | } 687 | 688 | extern "C" HRESULT WINAPI DllCanUnloadNow(void) 689 | { 690 | return DirectInputModuleManager::Get().DllCanUnloadNow(); 691 | } 692 | 693 | extern "C" HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID FAR* ppv) 694 | { 695 | PrintLog("devreorder: Calling hooked DllGetClassObject"); 696 | 697 | if (currentProcessIsIgnored()) { 698 | PrintLog("... current process is ignored, not hooking into DirectInput"); 699 | return DirectInputModuleManager::Get().DllGetClassObject(rclsid, riid, ppv); 700 | } 701 | 702 | IClassFactory *cf; 703 | HRESULT hr = DirectInputModuleManager::Get().DllGetClassObject(rclsid, riid, (void**)&cf); 704 | 705 | if (hr != DI_OK) { 706 | return hr; 707 | } 708 | 709 | *ppv = cf; 710 | 711 | IDirectInput8 *realDI; 712 | hr = cf->lpVtbl->CreateInstance(cf, NULL, IID_IDirectInput8, (void**)&realDI); 713 | 714 | if (hr != DI_OK) { 715 | return hr; 716 | } 717 | 718 | CreateHooks(IID_IDirectInput8, (LPVOID *)&realDI); 719 | 720 | return hr; 721 | } 722 | 723 | extern "C" HRESULT WINAPI DllRegisterServer(void) 724 | { 725 | return DirectInputModuleManager::Get().DllRegisterServer(); 726 | } 727 | 728 | extern "C" HRESULT WINAPI DllUnregisterServer(void) 729 | { 730 | return DirectInputModuleManager::Get().DllUnregisterServer(); 731 | } 732 | -------------------------------------------------------------------------------- /dinput8/dinput8.def: -------------------------------------------------------------------------------- 1 | LIBRARY "dinput8.dll" 2 | 3 | EXPORTS 4 | DirectInput8Create @1 5 | 6 | DllCanUnloadNow PRIVATE 7 | DllGetClassObject PRIVATE 8 | DllRegisterServer PRIVATE 9 | DllUnregisterServer PRIVATE 10 | -------------------------------------------------------------------------------- /dinput8/dinput8.h: -------------------------------------------------------------------------------- 1 | /* x360ce - XBOX360 Controller Emulator 2 | * Copyright (C) 2002-2010 Racer_S 3 | * Copyright (C) 2010-2013 Robert Krawczyk 4 | * 5 | * x360ce is free software: you can redistribute it and/or modify it under the terms 6 | * of the GNU Lesser General Public License as published by the Free Software Found- 7 | * ation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * x360ce is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 10 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 11 | * PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with x360ce. 14 | * If not, see . 15 | */ 16 | 17 | #ifndef _DINPUT8_H_ 18 | #define _DINPUT8_H_ 19 | 20 | #include 21 | #include 22 | #include 23 | #include "Logger.h" 24 | 25 | template 26 | void IH_CreateHookF(LPVOID pTarget, LPVOID pDetour, N* ppOriginal, const char* pTargetName) 27 | { 28 | if (*ppOriginal) return; 29 | MH_STATUS status = MH_CreateHook(pTarget, pDetour, reinterpret_cast(ppOriginal)); 30 | PrintLog("devreorder: CreateHook %s status %s", pTargetName, MH_StatusToString(status)); 31 | } 32 | 33 | inline void IH_EnableHookF(LPVOID pTarget, const char* pTargetName) 34 | { 35 | MH_STATUS status = MH_EnableHook(pTarget); 36 | PrintLog("devreorder: EnableHook %s status %s", pTargetName, MH_StatusToString(status)); 37 | } 38 | 39 | #define IH_CreateHook(pTarget, pDetour, ppOrgiginal) IH_CreateHookF(pTarget, pDetour, ppOrgiginal, #pTarget) 40 | #define IH_EnableHook(pTarget) IH_EnableHookF(pTarget, #pTarget) 41 | 42 | 43 | #endif -------------------------------------------------------------------------------- /dinput8/dinput8.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dinput8", "dinput8.vcxproj", "{FE3BC27D-9B4F-4063-A6A5-04B38F79AD64}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {FE3BC27D-9B4F-4063-A6A5-04B38F79AD64}.Debug|x64.ActiveCfg = Debug|x64 17 | {FE3BC27D-9B4F-4063-A6A5-04B38F79AD64}.Debug|x64.Build.0 = Debug|x64 18 | {FE3BC27D-9B4F-4063-A6A5-04B38F79AD64}.Debug|x86.ActiveCfg = Debug|Win32 19 | {FE3BC27D-9B4F-4063-A6A5-04B38F79AD64}.Debug|x86.Build.0 = Debug|Win32 20 | {FE3BC27D-9B4F-4063-A6A5-04B38F79AD64}.Release|x64.ActiveCfg = Release|x64 21 | {FE3BC27D-9B4F-4063-A6A5-04B38F79AD64}.Release|x64.Build.0 = Release|x64 22 | {FE3BC27D-9B4F-4063-A6A5-04B38F79AD64}.Release|x86.ActiveCfg = Release|Win32 23 | {FE3BC27D-9B4F-4063-A6A5-04B38F79AD64}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /dinput8/dinput8.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {FE3BC27D-9B4F-4063-A6A5-04B38F79AD64} 23 | Win32Proj 24 | dinput8 25 | dinput8 26 | 10.0 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v142 33 | Unicode 34 | false 35 | 36 | 37 | DynamicLibrary 38 | true 39 | v142 40 | Unicode 41 | 42 | 43 | DynamicLibrary 44 | false 45 | v142 46 | true 47 | Unicode 48 | 49 | 50 | DynamicLibrary 51 | false 52 | v142 53 | true 54 | Unicode 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | $(ProjectDir)obj\$(Configuration)\ 75 | $(ProjectDir)bin\$(Configuration)\ 76 | 77 | 78 | true 79 | $(ProjectDir)bin64\$(Configuration)\ 80 | $(ProjectDir)obj64\$(Configuration)\ 81 | 82 | 83 | false 84 | $(ProjectDir)bin\$(Configuration)\ 85 | $(ProjectDir)obj\$(Configuration)\ 86 | 87 | 88 | false 89 | $(ProjectDir)bin64\$(Configuration)\ 90 | $(ProjectDir)obj64\$(Configuration)\ 91 | 92 | 93 | 94 | NotUsing 95 | Level3 96 | Disabled 97 | WIN32;_DEBUG;_WINDOWS;_USRDLL;DINPUT8_EXPORTS;LOGGER_DISABLE_TIME;%(PreprocessorDefinitions) 98 | true 99 | $(ProjectDir);$(ProjectDir)..\common;$(ProjectDir)..\SimpleIni;$(ProjectDir)..\MinHook\Include;$(DXSDK_DIR)\Include;%(AdditionalIncludeDirectories) 100 | MultiThreadedDebug 101 | 102 | 103 | Windows 104 | true 105 | 106 | 107 | dinput8.def 108 | $(DXSDK_DIR)\Lib\x86 109 | dinput8.lib;dxguid.lib;Cfgmgr32.lib;%(AdditionalDependencies) 110 | 111 | 112 | 113 | 114 | NotUsing 115 | Level3 116 | Disabled 117 | WIN32;_DEBUG;_WINDOWS;_USRDLL;DINPUT8_EXPORTS;LOGGER_DISABLE_TIME;%(PreprocessorDefinitions) 118 | true 119 | $(ProjectDir);$(ProjectDir)..\common;$(ProjectDir)..\SimpleIni;$(ProjectDir)..\MinHook\Include;$(DXSDK_DIR)\Include;%(AdditionalIncludeDirectories) 120 | MultiThreadedDebug 121 | 122 | 123 | Windows 124 | true 125 | 126 | 127 | dinput8.def 128 | $(DXSDK_DIR)\Lib\x64 129 | dinput8.lib;dxguid.lib;psapi.lib;wintrust.lib;Shlwapi.lib;Cfgmgr32.lib;%(AdditionalDependencies) 130 | 131 | 132 | 133 | 134 | Level3 135 | NotUsing 136 | MaxSpeed 137 | true 138 | WIN32;NDEBUG;_WINDOWS;_USRDLL;DINPUT8_EXPORTS;%(PreprocessorDefinitions) 139 | true 140 | true 141 | $(ProjectDir);$(ProjectDir)..\common;$(ProjectDir)..\SimpleIni;$(ProjectDir)..\MinHook\Include;$(DXSDK_DIR)\Include;%(AdditionalIncludeDirectories) 142 | MultiThreaded 143 | 144 | 145 | Windows 146 | true 147 | true 148 | true 149 | 150 | 151 | dinput8.def 152 | dinput8.lib;dxguid.lib;psapi.lib;wintrust.lib;Shlwapi.lib;Cfgmgr32.lib;%(AdditionalDependencies) 153 | 154 | 155 | 156 | 157 | Level3 158 | NotUsing 159 | MaxSpeed 160 | true 161 | true 162 | WIN32;NDEBUG;_WINDOWS;_USRDLL;DINPUT8_EXPORTS;%(PreprocessorDefinitions) 163 | true 164 | $(ProjectDir);$(ProjectDir)..\common;$(ProjectDir)..\SimpleIni;$(ProjectDir)..\MinHook\Include;$(DXSDK_DIR)\Include;%(AdditionalIncludeDirectories) 165 | MultiThreaded 166 | 167 | 168 | Windows 169 | true 170 | true 171 | true 172 | 173 | 174 | dinput8.def 175 | dinput8.lib;dxguid.lib;psapi.lib;wintrust.lib;Shlwapi.lib;Cfgmgr32.lib;%(AdditionalDependencies) 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | false 210 | false 211 | 212 | 213 | 214 | 215 | false 216 | false 217 | 218 | 219 | 220 | 221 | 222 | 223 | Create 224 | Create 225 | Create 226 | Create 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | -------------------------------------------------------------------------------- /dinput8/dinput8.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {b9809de3-7f60-4981-8a0f-3f76b12796dc} 18 | 19 | 20 | {99f7f07a-6fec-4e96-ba34-edc8190952c3} 21 | 22 | 23 | {0b86fc53-c792-4e7a-80e9-424fd2944b6b} 24 | 25 | 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | Header Files 38 | 39 | 40 | InputHook 41 | 42 | 43 | InputHook 44 | 45 | 46 | InputHook 47 | 48 | 49 | InputHook 50 | 51 | 52 | InputHook 53 | 54 | 55 | InputHook 56 | 57 | 58 | InputHook 59 | 60 | 61 | SimpleIni 62 | 63 | 64 | common 65 | 66 | 67 | common 68 | 69 | 70 | common 71 | 72 | 73 | common 74 | 75 | 76 | common 77 | 78 | 79 | common 80 | 81 | 82 | common 83 | 84 | 85 | 86 | 87 | Source Files 88 | 89 | 90 | Source Files 91 | 92 | 93 | Source Files 94 | 95 | 96 | InputHook 97 | 98 | 99 | InputHook 100 | 101 | 102 | InputHook 103 | 104 | 105 | InputHook 106 | 107 | 108 | InputHook 109 | 110 | 111 | common 112 | 113 | 114 | common 115 | 116 | 117 | 118 | 119 | Source Files 120 | 121 | 122 | -------------------------------------------------------------------------------- /dinput8/dllmain.cpp: -------------------------------------------------------------------------------- 1 | /* x360ce - XBOX360 Controller Emulator 2 | * Copyright (C) 2002-2010 Racer_S 3 | * Copyright (C) 2010-2011 Robert Krawczyk 4 | * 5 | * x360ce is free software: you can redistribute it and/or modify it under the terms 6 | * of the GNU Lesser General Public License as published by the Free Software Found- 7 | * ation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * x360ce is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 10 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 11 | * PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with x360ce. 14 | * If not, see . 15 | */ 16 | 17 | #include "stdafx.h" 18 | #include 19 | #include 20 | #include 21 | 22 | #include "dinput8.h" 23 | #include "Logger.h" 24 | 25 | 26 | #pragma comment(lib,"Shlwapi.lib") 27 | 28 | void _cdecl ExitInstance() 29 | { 30 | } 31 | 32 | BOOL APIENTRY DllMain(HMODULE hModule, 33 | DWORD ul_reason_for_call, 34 | LPVOID lpReserved 35 | ) 36 | { 37 | LogSystem(); 38 | MH_Initialize(); 39 | 40 | switch (ul_reason_for_call) 41 | { 42 | case DLL_PROCESS_ATTACH: 43 | DisableThreadLibraryCalls(hModule); 44 | atexit(ExitInstance); 45 | break; 46 | case DLL_PROCESS_DETACH: 47 | break; 48 | } 49 | return TRUE; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /dinput8/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // dinput8.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /dinput8/stdafx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "targetver.h" 4 | 5 | #ifdef DEBUG 6 | #include 7 | #endif 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #define WIN32_LEAN_AND_MEAN 14 | #define STRICT 15 | #define NOMINMAX 16 | 17 | #include 18 | #include 19 | 20 | #include "Common.h" 21 | -------------------------------------------------------------------------------- /dinput8/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | 10 | #define _WIN32_WINNT _WIN32_WINNT_VISTA 11 | 12 | #include 13 | 14 | #define DIRECTINPUT_VERSION 0x0800 15 | -------------------------------------------------------------------------------- /release/DeviceLister.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briankendall/devreorder/bc7f96d394029123185c5b4429b9704c7fe3f401/release/DeviceLister.exe -------------------------------------------------------------------------------- /release/devreorder.ini: -------------------------------------------------------------------------------- 1 | ; devreorder settings 2 | ; Any line starting with ; is a comment and will be ignored 3 | 4 | [order] 5 | ; In this section write the names, GUIDs, or device instance 6 | ; IDs of your controllers in the order you want them to be 7 | ; detected, one per line. 8 | ; Make sure any names exactly match the name printed in the 9 | ; Game Controllers control panel or in DeviceLister.exe, 10 | ; including any capital letters and punctuation. If you use 11 | ; a device's GUID instead of its name, make sure the GUID is 12 | ; enclosed in curly braces, just like in DeviceLister.exe. 13 | ; Similarly device instance IDs must match DeviceLister.exe 14 | ; and be enclosed in triangle brackets. 15 | ; Example: 16 | ; vJoy Device 17 | ; Controller (XBOX 360 For Windows) 18 | ; {01234567-89ab-cdef-0123-456789abcdef} 19 | ; 20 | 21 | [hidden] 22 | ; In this section, list the controllers 23 | ; that you want to be hidden. Again their name must exactly 24 | ; match the name printed in the Game Controllers control 25 | ; panel or in DeviceLister.exe, and GUIDs and device instance 26 | ; IDs must be in the same format as above. 27 | ; Example: 28 | ; Wireless Controller 29 | ; {01234567-89ab-cdef-0123-456789abcdef} 30 | ; 31 | 32 | [visible] 33 | ; In this section, list the controllers that you want to be 34 | ; the only controllers which are visible. If this section is 35 | ; not empty, then all controllers not listed in it will be 36 | ; hidden. If this section is left blank, then it will have no 37 | ; effect and only the controllers in the [hidden] section 38 | ; will be invisible. (This does not affect keyboards, mice, 39 | ; and other pointing devices.) 40 | ; Example: 41 | ; Controller (XBOX 360 For Windows) 42 | ; {01234567-89ab-cdef-0123-456789abcdef} 43 | ; 44 | 45 | [ignored processes] 46 | ; In this section, write the names of processes you want 47 | ; devreorder to ignore so that the order and visibility of 48 | ; their controllers are not changed, as though devreorder is 49 | ; not installed. Matches are not case sensitive. Be sure to 50 | ; include the file extension as well. 51 | ; Example: 52 | ; steam.exe 53 | -------------------------------------------------------------------------------- /release/x64/dinput8.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briankendall/devreorder/bc7f96d394029123185c5b4429b9704c7fe3f401/release/x64/dinput8.dll -------------------------------------------------------------------------------- /release/x86/dinput8.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briankendall/devreorder/bc7f96d394029123185c5b4429b9704c7fe3f401/release/x86/dinput8.dll --------------------------------------------------------------------------------