@@ -16,20 +16,14 @@ And modified by @SAM1430B from splitscreen.me .
1616
1717#include " InjectionInfo.h"
1818
19- // Helper function to convert a string to lowercase
20- std::wstring to_lower (const std::wstring& str) {
21- std::wstring lower_str = str;
22- std::transform (lower_str.begin (), lower_str.end (), lower_str.begin (), ::towlower);
23- return lower_str;
24- }
25-
2619// Command-line usage information
2720void print_usage () {
2821 std::wcout << L" Usage: WinSplitPlus.exe [options] C:\\ path\\ to\\ game.exe [game arguments]\n "
2922 << L" Options:\n "
3023 << L" -Player <Number> Identifier.\n "
3124 << L" -WinClass Enable Window class hook.\n "
3225 << L" -WinName Enable Window Name hook.\n "
26+ << L" -FindWindow Enable FindWindow hook.\n "
3327 << L" -Mutex <Name> Hook Mutex.\n "
3428 << L" -Width <W> -Height <H> Set Window Size.\n "
3529 << L" -Posx <X> -Posy <Y> Set Window Position.\n\n "
@@ -38,9 +32,52 @@ void print_usage() {
3832 << L" -W Enable Unicode hooks (e.g. RegisterClassW).\n "
3933 << L" -Std Enable Standard hooks (e.g. RegisterClass).\n "
4034 << L" -Ex Enable Extended hooks (e.g. RegisterClassEx).\n\n "
35+ << L" -IgnoreWinClass <Name> Ignore specific Class Name (partial match).\n "
36+ << L" -IgnoreWinName <Name> Ignore specific Window Name (partial match).\n "
4137 << L" (If no filters are provided, ALL are enabled by default.)\n\n " ;
4238}
4339
40+ bool InjectStandardDLL (HANDLE hProcess, const std::wstring& dllPath)
41+ {
42+ HMODULE hKernel32 = GetModuleHandleW (L" Kernel32" );
43+ if (!hKernel32) return false ;
44+
45+ LPVOID pLoadLibrary = (LPVOID )GetProcAddress (hKernel32, " LoadLibraryW" );
46+ if (!pLoadLibrary) return false ;
47+
48+ size_t bytesNeeded = (dllPath.length () + 1 ) * sizeof (wchar_t );
49+ LPVOID pRemoteString = VirtualAllocEx (hProcess, NULL , bytesNeeded, MEM_COMMIT | MEM_RESERVE , PAGE_READWRITE );
50+ if (!pRemoteString) return false ;
51+
52+ if (!WriteProcessMemory (hProcess, pRemoteString, dllPath.c_str (), bytesNeeded, NULL ))
53+ {
54+ VirtualFreeEx (hProcess, pRemoteString, 0 , MEM_RELEASE );
55+ return false ;
56+ }
57+
58+ HANDLE hThread = CreateRemoteThread (hProcess, NULL , 0 , (LPTHREAD_START_ROUTINE )pLoadLibrary, pRemoteString, 0 , NULL );
59+ if (!hThread)
60+ {
61+ VirtualFreeEx (hProcess, pRemoteString, 0 , MEM_RELEASE );
62+ return false ;
63+ }
64+
65+ WaitForSingleObject (hThread, INFINITE );
66+
67+ CloseHandle (hThread);
68+ VirtualFreeEx (hProcess, pRemoteString, 0 , MEM_RELEASE );
69+
70+ std::wcout << L" Standard Injection: " << dllPath.substr (dllPath.find_last_of (L" \\ /" ) + 1 ) << L" - OK" << std::endl;
71+ return true ;
72+ }
73+
74+ // Helper function to convert a string to lowercase
75+ std::wstring to_lower (const std::wstring& str) {
76+ std::wstring lower_str = str;
77+ std::transform (lower_str.begin (), lower_str.end (), lower_str.begin (), ::towlower);
78+ return lower_str;
79+ }
80+
4481int wmain (int argc, wchar_t * argv[])
4582{
4683 InjectionInfo injectionInfo = {};
@@ -83,6 +120,9 @@ int wmain(int argc, wchar_t* argv[])
83120 injectionInfo.injectionFlags = injectionInfo.injectionFlags | InjectionFlags::HOOK_CREATE_WINDOW ;
84121 changeWindowName = true ;
85122 }
123+ else if (lower_arg == L" -findwindow" ) {
124+ injectionInfo.injectionFlags = injectionInfo.injectionFlags | InjectionFlags::HOOK_FIND_WINDOW ;
125+ }
86126 else if (lower_arg == L" -mutex" && i + 1 < argc) {
87127 injectionInfo.injectionFlags = injectionInfo.injectionFlags | InjectionFlags::HOOK_CREATE_MUTEX ;
88128 baseMutexName = argv[++i];
@@ -118,6 +158,14 @@ int wmain(int argc, wchar_t* argv[])
118158 injectionInfo.injectionFlags = injectionInfo.injectionFlags | InjectionFlags::HOOK_EXTENDED ;
119159 hasVariantFilter = true ;
120160 }
161+ else if (lower_arg == L" -ignorewinclass" && i + 1 < argc) {
162+ std::wstring val = argv[++i];
163+ wcscpy_s (injectionInfo.customIgnoreClassName , IGNORE_MAX_LENGTH , val.c_str ());
164+ }
165+ else if (lower_arg == L" -ignorewinname" && i + 1 < argc) {
166+ std::wstring val = argv[++i];
167+ wcscpy_s (injectionInfo.customIgnoreWindowName , IGNORE_MAX_LENGTH , val.c_str ());
168+ }
121169 else {
122170 if (gamePath.empty ()) gamePath = original_arg;
123171 else {
@@ -133,7 +181,7 @@ int wmain(int argc, wchar_t* argv[])
133181 system (" pause" );
134182 return 1 ;
135183 }
136-
184+
137185 // Construct final names based on Player number
138186 if (!hasCharsetFilter) {
139187 injectionInfo.injectionFlags = injectionInfo.injectionFlags | InjectionFlags::HOOK_ANSI | InjectionFlags::HOOK_UNICODE ;
@@ -166,7 +214,7 @@ int wmain(int argc, wchar_t* argv[])
166214 wcscpy_s (injectionInfo.mutexNewName , MUTEX_NAME_MAX_LENGTH , finalMutexName.c_str ());
167215 }
168216
169- // Inject the DLL
217+ // Prepare DLLs
170218 WCHAR exePath[MAX_PATH ];
171219 GetModuleFileName (NULL , exePath, MAX_PATH );
172220 std::wstring exeDir = exePath;
@@ -175,8 +223,6 @@ int wmain(int argc, wchar_t* argv[])
175223 exeDir = exeDir.substr (0 , lastSlash);
176224 }
177225
178- std::vector<std::wstring> dllsToInject;
179-
180226 std::wstring coreDllName;
181227
182228#if defined(_WIN64)
@@ -189,45 +235,49 @@ int wmain(int argc, wchar_t* argv[])
189235 coreDllName = L" WinSplitPlusIJ32.dll" ;
190236#endif
191237
192- // Add the Core DLL to the top of the list
193238 std::wstring coreDllPath = exeDir + L" \\ " + coreDllName;
194-
195- if (GetFileAttributesW (coreDllPath.c_str ()) != INVALID_FILE_ATTRIBUTES ) {
196- dllsToInject.push_back (coreDllPath);
197- std::wcout << L" Added Core DLL: " << coreDllName << std::endl;
198- }
199- else {
239+ if (GetFileAttributesW (coreDllPath.c_str ()) == INVALID_FILE_ATTRIBUTES ) {
200240 std::wcerr << L" CRITICAL ERROR: Core DLL not found at: " << coreDllPath << std::endl;
201241 Sleep (10000 );
202242 return 1 ;
203243 }
204244
245+ // Plugin DLLs
246+ std::vector<std::wstring> pluginDlls;
205247 std::wstring pluginsDir = exeDir + L" \\ plugins\\ " ;
206248 std::wstring searchPath = pluginsDir + L" *.dll" ;
207249 WIN32_FIND_DATA wfd;
208250 HANDLE hFind = FindFirstFile (searchPath.c_str (), &wfd);
209251
210252 if (hFind != INVALID_HANDLE_VALUE ) {
211253 do {
212- std::wstring dllName = wfd.cFileName ;
213- dllsToInject.push_back (pluginsDir + dllName);
254+ std::wstring filename = wfd.cFileName ;
255+ std::wstring lowerName = to_lower (filename);
256+ if (lowerName.length () >= 4 &&
257+ lowerName.substr (lowerName.length () - 4 ) == L" .dll" )
258+ {
259+ pluginDlls.push_back (pluginsDir + filename);
260+ }
261+ else
262+ {
263+ // std::wcout << L"Skipping invalid match: " << filename << std::endl;
264+ }
265+
214266 } while (FindNextFile (hFind, &wfd));
215267 FindClose (hFind);
216268 }
217269 else {
218- std::wcout << L" No plugins found in " << pluginsDir << L" (Only Core DLL will be injected). " << std::endl;
270+ std::wcout << L" No plugins found in " << pluginsDir << std::endl;
219271 }
220272
221-
222- // Suspended state is needed for Window hooks
273+ // Launch Process
223274 PROCESS_INFORMATION pi = {};
224275 STARTUPINFOW si = {};
225276 si.cb = sizeof (si);
226277
227278 std::wstring fullCommandLine = L" \" " + gamePath + L" \" " + gameArgs;
228279
229280 std::wcout << L" Launching game suspended: " << gamePath << std::endl;
230- std::wcout << L" Injecting " << dllsToInject.size () << L" DLLs..." << std::endl;
231281
232282 if (!CreateProcess (
233283 NULL ,
@@ -242,39 +292,39 @@ int wmain(int argc, wchar_t* argv[])
242292 &pi // ProcessInfo
243293 )) {
244294 std::wcerr << L" Failed to create process: " << GetLastError () << std::endl;
245- std::wcerr << L" Command: " << fullCommandLine << std::endl;
246295 Sleep (7000 );
247296 return 1 ;
248297 }
249298
250299 DWORD processId = pi.dwProcessId ;
251- bool allInjectionsSucceeded = true ;
252-
253- for (const auto & dllPath : dllsToInject) {
254- std::wcout << L" Injecting " << dllPath.substr (dllPath.find_last_of (L" \\ /" ) + 1 ) << L" ..." << std::endl;
255-
256- NTSTATUS nt = RhInjectLibrary (
257- processId,
258- 0 ,
259- EASYHOOK_INJECT_DEFAULT ,
260- const_cast <wchar_t *>(dllPath.c_str ()),
261- const_cast <wchar_t *>(dllPath.c_str ()),
262- &injectionInfo,
263- sizeof (InjectionInfo)
264- );
265-
266- if (nt != 0 ) {
267- std::wcerr << L" Failed to inject DLL " << dllPath << L" : " << RtlGetLastErrorString () << std::endl;
268- allInjectionsSucceeded = false ;
269- }
270- }
271300
272- if (!allInjectionsSucceeded) {
273- std::wcerr << L" Warning: One or more DLLs failed to inject." << std::endl;
274- Sleep (5000 );
301+
302+ std::wcout << L" Injecting Core DLL via EasyHook..." << std::endl;
303+ NTSTATUS nt = RhInjectLibrary (
304+ processId,
305+ 0 ,
306+ EASYHOOK_INJECT_DEFAULT ,
307+ #if defined(_WIN64)
308+ NULL , const_cast <wchar_t *>(coreDllPath.c_str ()),
309+ #else
310+ const_cast <wchar_t *>(coreDllPath.c_str ()), NULL ,
311+ #endif
312+ & injectionInfo, sizeof (InjectionInfo)
313+ );
314+
315+ if (nt != 0 ) {
316+ std::wcerr << L" Failed to inject Core DLL: " << RtlGetLastErrorString () << std::endl;
317+ std::wcerr << L" Terminating..." << std::endl;
318+ TerminateProcess (pi.hProcess , 1 );
319+ return 1 ;
275320 }
276- else {
277- std::wcout << L" All injections successful." << std::endl;
321+
322+ if (!pluginDlls.empty ())
323+ {
324+ std::wcout << L" Injecting " << pluginDlls.size () << L" plugins via LoadLibrary..." << std::endl;
325+ for (const auto & dllPath : pluginDlls) {
326+ InjectStandardDLL (pi.hProcess , dllPath);
327+ }
278328 }
279329
280330 std::wcout << L" Resuming game process..." << std::endl;
@@ -283,7 +333,7 @@ int wmain(int argc, wchar_t* argv[])
283333 CloseHandle (pi.hProcess );
284334 CloseHandle (pi.hThread );
285335
286- std::wcout << L" Successfully launched and injected into process " << processId << L" . The launcher will now exit ." << std::endl;
336+ std::wcout << L" Success. Launcher exiting ." << std::endl;
287337
288338 return 0 ;
289339}
0 commit comments