├── .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
--------------------------------------------------------------------------------