diff --git a/ep_extra/README.md b/ep_extra/README.md
new file mode 100644
index 000000000..580ef750e
--- /dev/null
+++ b/ep_extra/README.md
@@ -0,0 +1,5 @@
+# ExplorerPatcher Custom Libraries Chainloader
+
+ExplorerPatcher has a simple, built-in mechanism that allows users to load their own DLL into `explorer.exe` right after ExplorerPatcher finishes initializing its hooks. Interested users hould place a DLL called `ep_extra.dll` in `C:\Windows`. When ExplorerPatcher finishes its setup, it loads the `ep_extra.dll` library and calls the `ep_extra_EntryPoint` function. Although this is very useful so that users can load their custom code, it is quite limited at the moment, as it loads just one DLL.
+
+This project is a solution to this issue. A chainloader is implemented here, that looks for other modules matching the `ep_extra_*.dll` pattern in `C:\Windows` as well, and loads them one after the other.
diff --git a/ep_extra/ep_extra.rc b/ep_extra/ep_extra.rc
new file mode 100644
index 000000000..0dca6b07c
--- /dev/null
+++ b/ep_extra/ep_extra.rc
@@ -0,0 +1,100 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "VALINET Solutions SRL"
+ VALUE "FileDescription", "ExplorerPatcher Custom Libraries Chainloader"
+ VALUE "FileVersion", "1.0.0.0"
+ VALUE "InternalName", "ep_extra.dll"
+ VALUE "LegalCopyright", "Copyright (C) 2006-2022 VALINET Solutions SRL. All rights reserved."
+ VALUE "OriginalFilename", "ep_extra.dll"
+ VALUE "ProductName", "ExplorerPatcher"
+ VALUE "ProductVersion", "1.0.0.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/ep_extra/ep_extra.vcxproj b/ep_extra/ep_extra.vcxproj
new file mode 100644
index 000000000..0e333464e
--- /dev/null
+++ b/ep_extra/ep_extra.vcxproj
@@ -0,0 +1,179 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 16.0
+ Win32Proj
+ {93fa47cc-7753-4f86-b583-69048f51c5ab}
+ epextra
+ 10.0
+
+
+
+ DynamicLibrary
+ true
+ v142
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v142
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v142
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v142
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)\build\$(Configuration)
+
+
+ false
+ $(SolutionDir)\build\$(Configuration)
+
+
+ true
+ $(SolutionDir)\build\$(Configuration)
+
+
+ false
+ $(SolutionDir)\build\$(Configuration)
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ MultiThreadedDebug
+
+
+ Console
+ true
+
+
+ /EXPORT:ep_extra_EntryPoint %(AdditionalOptions)
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ MultiThreaded
+
+
+ Console
+ true
+ true
+ true
+
+
+ /EXPORT:ep_extra_EntryPoint %(AdditionalOptions)
+
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ MultiThreadedDebug
+
+
+ Console
+ true
+
+
+ /EXPORT:ep_extra_EntryPoint %(AdditionalOptions)
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ MultiThreaded
+
+
+ Console
+ true
+ true
+ true
+
+
+ %(AdditionalOptions)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ep_extra/ep_extra.vcxproj.filters b/ep_extra/ep_extra.vcxproj.filters
new file mode 100644
index 000000000..e30d842f1
--- /dev/null
+++ b/ep_extra/ep_extra.vcxproj.filters
@@ -0,0 +1,37 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+
+
+ Resource Files
+
+
+
\ No newline at end of file
diff --git a/ep_extra/main.asm b/ep_extra/main.asm
new file mode 100644
index 000000000..e57969ab7
--- /dev/null
+++ b/ep_extra/main.asm
@@ -0,0 +1,18 @@
+EXTERN worker : PROC
+
+.CODE
+
+ep_extra_EntryPoint PROC EXPORT
+ PUSH RBP
+ MOV RBP, RSP
+ SUB RSP, 30H
+ CALL worker
+ CMP RAX, 0
+ JE finish
+ JMP RAX
+finish:
+ LEAVE
+ RET
+ep_extra_EntryPoint ENDP
+
+END
\ No newline at end of file
diff --git a/ep_extra/resource.h b/ep_extra/resource.h
new file mode 100644
index 000000000..bc8b44eac
--- /dev/null
+++ b/ep_extra/resource.h
@@ -0,0 +1,14 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by ep_extra.rc
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/ep_extra/worker.c b/ep_extra/worker.c
new file mode 100644
index 000000000..8f0b97fe1
--- /dev/null
+++ b/ep_extra/worker.c
@@ -0,0 +1,82 @@
+#include
+#include
+#pragma comment(lib, "Shlwapi.lib")
+#include
+
+HMODULE hModule = NULL;
+HANDLE sigFinish = NULL;
+void* pFinishProc = NULL;
+
+void done() {
+ WaitForSingleObject(sigFinish, INFINITE);
+ FreeLibraryAndExitThread(hModule, 0);
+}
+
+void* worker() {
+ wchar_t pattern[MAX_PATH];
+ GetWindowsDirectoryW(pattern, MAX_PATH);
+ wcscat_s(pattern, MAX_PATH, L"\\ep_extra_*.dll");
+
+ WIN32_FIND_DATA data;
+ HANDLE hFind = FindFirstFileW(pattern, &data);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ wprintf(L">> Found ep_extra library: \"%s\"\n", data.cFileName);
+ GetWindowsDirectoryW(pattern, MAX_PATH);
+ wcscat_s(pattern, MAX_PATH, L"\\");
+ wcscat_s(pattern, MAX_PATH, data.cFileName);
+ HMODULE hLib = LoadLibraryW(pattern);
+ if (hLib) {
+ FARPROC proc = (FARPROC)(GetProcAddress(hLib, "setup"));
+ if (proc) {
+ if (!proc()) FreeLibrary(hLib);
+ }
+ else FreeLibrary(hLib);
+ }
+ } while (FindNextFileW(hFind, &data));
+ FindClose(hFind);
+ }
+
+ sigFinish = CreateEventW(NULL, FALSE, FALSE, NULL);
+ if (sigFinish) {
+ BYTE payload[] = {
+ 0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rcx, sigFinish
+ 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rax, SetEvent
+ 0xFF, 0xD0, // call SetEvent
+ 0xC9, // leave
+ 0xC3 // ret
+ };
+ *(INT64*)(payload + 2) = sigFinish;
+ *(INT64*)(payload + 12) = SetEvent;
+
+ pFinishProc = VirtualAlloc(NULL, sizeof(payload), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+ if (pFinishProc) {
+ memcpy(pFinishProc, payload, sizeof(payload));
+ SHCreateThread(done, 0, CTF_NOADDREFLIB, NULL);
+ return pFinishProc;
+ }
+ }
+ return NULL;
+}
+
+BOOL WINAPI DllMain(
+ _In_ HINSTANCE hinstDLL,
+ _In_ DWORD fdwReason,
+ _In_ LPVOID lpvReserved
+)
+{
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ DisableThreadLibraryCalls(hinstDLL);
+ hModule = hinstDLL;
+ break;
+ case DLL_THREAD_ATTACH:
+ break;
+ case DLL_THREAD_DETACH:
+ break;
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}