Skip to content

Instantly share code, notes, and snippets.

@deepak1556
Created June 24, 2025 14:31
Show Gist options
  • Save deepak1556/f11695e81ae7972c7f0bdadb3565deb3 to your computer and use it in GitHub Desktop.
Save deepak1556/f11695e81ae7972c7f0bdadb3565deb3 to your computer and use it in GitHub Desktop.
Patch to make Electron load resources from a versioned folder that is extracted from exe file info
```
diff --git a/base/base_paths.cc b/base/base_paths.cc
index 59ffbd2b67501..2eb39666e8778 100644
--- a/base/base_paths.cc
+++ b/base/base_paths.cc
@@ -71,8 +71,10 @@ bool PathProvider(int key, FilePath* result) {
}
*result = result->DirName();
return true;
+#if !BUILDFLAG(IS_WIN)
case DIR_ASSETS:
return PathService::Get(DIR_MODULE, result);
+#endif // !BUILDFLAG(IS_WIN)
#endif // !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_IOS)
case DIR_TEMP:
return GetTempDir(result);
diff --git a/base/base_paths_win.cc b/base/base_paths_win.cc
index aa9d066a2a994..f4ea3c1ef3801 100644
--- a/base/base_paths_win.cc
+++ b/base/base_paths_win.cc
@@ -12,8 +12,10 @@
#include "base/environment.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
+#include "base/file_version_info.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
+#include "base/strings/string_util_win.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/current_module.h"
#include "base/win/scoped_co_mem.h"
@@ -49,6 +51,18 @@ bool PathProviderWin(int key, FilePath* result) {
cur = FilePath(system_buffer);
break;
}
+ case base::DIR_ASSETS: {
+ auto file_version_info = FileVersionInfo::CreateFileVersionInfoForModule(
+ CURRENT_MODULE());
+ if (!PathService::Get(DIR_MODULE, &cur)) {
+ return false;
+ }
+ if (!file_version_info) {
+ break;
+ }
+ cur = cur.Append(base::AsWString(file_version_info->product_version()));
+ break;
+ }
case base::DIR_WINDOWS:
GetWindowsDirectory(system_buffer, MAX_PATH);
cur = FilePath(system_buffer);
diff --git a/content/browser/gpu/gpu_data_manager_impl_private.cc b/content/browser/gpu/gpu_data_manager_impl_private.cc
index 003659857de3c..f64a4acdea81c 100644
--- a/content/browser/gpu/gpu_data_manager_impl_private.cc
+++ b/content/browser/gpu/gpu_data_manager_impl_private.cc
@@ -152,7 +152,7 @@ bool GetIntelCacheFileNames(std::vector<base::FilePath::StringType>* names) {
DCHECK(names);
DCHECK(names->empty());
base::FilePath module_path;
- if (!base::PathService::Get(base::FILE_EXE, &module_path))
+ if (!base::PathService::Get(base::DIR_ASSETS, &module_path))
return false;
module_path = module_path.BaseName().RemoveExtension();
base::FilePath::StringType module_name = module_path.value();
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc
index 53b72124ffaa7..bcc2f90c6920d 100644
--- a/sandbox/policy/win/sandbox_win.cc
+++ b/sandbox/policy/win/sandbox_win.cc
@@ -151,10 +151,10 @@ ResultCode AddGenericConfig(sandbox::TargetConfig* config) {
// Add the policy for read-only PDB file access for stack traces.
#if !defined(OFFICIAL_BUILD)
- base::FilePath exe;
- if (!base::PathService::Get(base::FILE_EXE, &exe))
+ base::FilePath assets_path;
+ if (!base::PathService::Get(base::DIR_ASSETS, &assets_path))
return SBOX_ERROR_GENERIC;
- base::FilePath pdb_path = exe.DirName().Append(L"*.pdb");
+ base::FilePath pdb_path = assets_path.Append(L"*.pdb");
{
ResultCode result = config->AllowFileAccess(FileSemantics::kAllowReadonly,
pdb_path.value().c_str());
diff --git a/sandbox/win/src/process_mitigations.cc b/sandbox/win/src/process_mitigations.cc
index 39282ab6be12d..2f15ef4d448c5 100644
--- a/sandbox/win/src/process_mitigations.cc
+++ b/sandbox/win/src/process_mitigations.cc
@@ -21,6 +21,7 @@
#include "base/check_op.h"
#include "base/files/file_path.h"
#include "base/notreached.h"
+#include "base/path_service.h"
#include "base/rand_util.h"
#include "base/scoped_native_library.h"
#include "base/win/access_token.h"
@@ -119,6 +120,13 @@ bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags starting_flags,
return false;
}
+++ b/sandbox/policy/win/sandbox_win.cc
@@ -151,10 +151,10 @@ ResultCode AddGenericConfig(sandbox::TargetConfig* config) {
// Add the policy for read-only PDB file access for stack traces.
#if !defined(OFFICIAL_BUILD)
- base::FilePath exe;
- if (!base::PathService::Get(base::FILE_EXE, &exe))
+ base::FilePath assets_path;
+ if (!base::PathService::Get(base::DIR_ASSETS, &assets_path))
return SBOX_ERROR_GENERIC;
- base::FilePath pdb_path = exe.DirName().Append(L"*.pdb");
+ base::FilePath pdb_path = assets_path.Append(L"*.pdb");
{
ResultCode result = config->AllowFileAccess(FileSemantics::kAllowReadonly,
pdb_path.value().c_str());
diff --git a/sandbox/win/src/process_mitigations.cc b/sandbox/win/src/process_mitigations.cc
index 39282ab6be12d..2f15ef4d448c5 100644
--- a/sandbox/win/src/process_mitigations.cc
+++ b/sandbox/win/src/process_mitigations.cc
@@ -21,6 +21,7 @@
#include "base/check_op.h"
#include "base/files/file_path.h"
#include "base/notreached.h"
+#include "base/path_service.h"
#include "base/rand_util.h"
#include "base/scoped_native_library.h"
#include "base/win/access_token.h"
@@ -119,6 +120,13 @@ bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags starting_flags,
return false;
}
+ base::FilePath assets_path;
+ if (base::PathService::Get(base::DIR_ASSETS, &assets_path)) {
+ LOG(WARNING) << "Adding assets path to DLL search order: "
+ << assets_path.value();
+ AddDllDirectory(assets_path.value().c_str());
+ }
+
applied_flags |= MITIGATION_DLL_SEARCH_ORDER;
}
```
```
diff --git a/shell/app/electron_main_delegate.cc b/shell/app/electron_main_delegate.cc
index a66e140a1e..36ddcb15e0 100644
--- a/shell/app/electron_main_delegate.cc
+++ b/shell/app/electron_main_delegate.cc
@@ -218,7 +218,7 @@ std::string LoadResourceBundle(const std::string& locale) {
pak_dir =
base::apple::FrameworkBundlePath().Append(FILE_PATH_LITERAL("Resources"));
#else
- base::PathService::Get(base::DIR_MODULE, &pak_dir);
+ base::PathService::Get(base::DIR_ASSETS, &pak_dir);
#endif
std::string loaded_locale = ui::ResourceBundle::InitSharedInstanceWithLocale(
diff --git a/shell/app/electron_main_win.cc b/shell/app/electron_main_win.cc
index da0143a9e6..30e9b00cf7 100644
--- a/shell/app/electron_main_win.cc
+++ b/shell/app/electron_main_win.cc
@@ -13,17 +13,26 @@
#include <cstdlib>
#include <memory>
#include <string>
+#include <string_view>
#include <utility>
+#include "base/base_paths.h"
#include "base/at_exit.h"
#include "base/debug/alias.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
#include "base/i18n/icu_util.h"
+#include "base/logging.h"
#include "base/memory/raw_ptr_exclusion.h"
+#include "base/path_service.h"
#include "base/process/launch.h"
#include "base/strings/cstring_view.h"
+#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/dark_mode_support.h"
#include "chrome/app/exit_code_watcher_win.h"
+#include "chrome/common/win/delay_load_notify_hook.h"
#include "components/crash/core/app/crash_switches.h"
#include "components/crash/core/app/run_as_crashpad_handler_win.h"
#include "content/public/app/content_main.h"
@@ -44,6 +53,7 @@ namespace {
// from //electron:electron_app
const char kUserDataDir[] = "user-data-dir";
const char kProcessType[] = "type";
+const wchar_t kFFmpegDll[] = L"ffmpeg.dll";
[[nodiscard]] bool IsEnvSet(const base::cstring_view name) {
size_t required_size = 0;
@@ -51,6 +61,58 @@ const char kProcessType[] = "type";
return required_size != 0;
}
+// Indicates whether a file can be opened using the same flags that
+// ::LoadLibrary() uses to open modules.
+bool ModuleCanBeRead(const base::FilePath& file_path) {
+ return base::File(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ)
+ .IsValid();
+}
+
+// Returns the full path to |module_name|.
+base::FilePath GetModulePath(std::wstring_view module_name) {
+ base::FilePath assets_dir;
+ const bool has_path = base::PathService::Get(base::DIR_ASSETS, &assets_dir);
+ DCHECK(has_path);
+
+ // Look for the module in a versioned sub-directory of the current
+ // executable's directory and return the path if it can be read. This is the
+ // expected location of modules for proper installs.
+ const base::FilePath module_path =
+ assets_dir.Append(module_name);
+ if (ModuleCanBeRead(module_path))
+ return module_path;
+
+ // Othwerwise, return the path to the module in the current executable's
+ // directory. This is the expected location of modules for dev builds.
+ base::FilePath exe_dir;
+ DCHECK(base::PathService::Get(base::DIR_EXE, &exe_dir));
+ return exe_dir.Append(module_name);
+}
+
+FARPROC DelayLoadCallback(unsigned delay_load_event,
+ DelayLoadInfo* delay_load_info) {
+ if (delay_load_event == dliNotePreLoadLibrary &&
+ base::EqualsCaseInsensitiveASCII(
+ delay_load_info->szDll, "ffmpeg.dll")) {
+ PLOG(WARNING) << "Delay load event: " << delay_load_event
+ << ", Dll: " << delay_load_info->szDll;
+ base::FilePath file = GetModulePath(kFFmpegDll);
+ if (file.empty()) {
+ PLOG(ERROR) << "Cannot find module " << kFFmpegDll;
+ return nullptr;
+ }
+ ::SetCurrentDirectoryW(file.DirName().value().c_str());
+ HMODULE handle = ::LoadLibraryExW(file.value().c_str(), nullptr,
+ LOAD_WITH_ALTERED_SEARCH_PATH);
+ if (!handle) {
+ PLOG(ERROR) << "Failed to load FFmpeg DLL from " << file.value();
+ return nullptr;
+ }
+ return reinterpret_cast<FARPROC>(handle);
+ }
+ return nullptr;
+}
+
} // namespace
namespace crash_reporter {
@@ -220,6 +282,8 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
if (!electron::CheckCommandLineArguments(command_line->argv()))
return -1;
+ chrome::SetDelayLoadHookCallback(&DelayLoadCallback);
+
sandbox::SandboxInterfaceInfo sandbox_info = {nullptr};
content::InitializeSandboxInfo(&sandbox_info);
electron::ElectronMainDelegate delegate;
diff --git a/shell/browser/api/electron_api_app.cc b/shell/browser/api/electron_api_app.cc
index 3c19d19172..cc7a2f4e37 100644
--- a/shell/browser/api/electron_api_app.cc
+++ b/shell/browser/api/electron_api_app.cc
@@ -399,6 +399,7 @@ int GetPathConstant(std::string_view name) {
{"pictures", chrome::DIR_USER_PICTURES},
#if BUILDFLAG(IS_WIN)
{"recent", electron::DIR_RECENT},
+ {"assets", base::DIR_ASSETS},
#endif
{"sessionData", DIR_SESSION_DATA},
{"temp", base::DIR_TEMP},
diff --git a/shell/common/asar/archive_win.cc b/shell/common/asar/archive_win.cc
index d1deb6e346..effcde3ba3 100644
--- a/shell/common/asar/archive_win.cc
+++ b/shell/common/asar/archive_win.cc
@@ -24,13 +24,13 @@ const wchar_t kIntegrityCheckResourceType[] = L"Integrity";
const wchar_t kIntegrityCheckResourceItem[] = L"ElectronAsar";
std::optional<base::FilePath> Archive::RelativePath() const {
- base::FilePath exe_path;
- if (!base::PathService::Get(base::FILE_EXE, &exe_path)) {
- LOG(FATAL) << "Couldn't get exe file path";
+ base::FilePath assets_path;
+ if (!base::PathService::Get(base::DIR_ASSETS, &assets_path)) {
+ LOG(FATAL) << "Couldn't get assets file path";
}
base::FilePath relative_path;
- if (!exe_path.DirName().AppendRelativePath(path_, &relative_path)) {
+ if (!assets_path.AppendRelativePath(path_, &relative_path)) {
return std::nullopt;
}
diff --git a/shell/common/node_bindings.cc b/shell/common/node_bindings.cc
index 9ad2274e6a..1a92ad0ae5 100644
--- a/shell/common/node_bindings.cc
+++ b/shell/common/node_bindings.cc
@@ -442,11 +442,10 @@ base::FilePath GetResourcesPath() {
#if BUILDFLAG(IS_MAC)
return MainApplicationBundlePath().Append("Contents").Append("Resources");
#else
- auto* command_line = base::CommandLine::ForCurrentProcess();
- base::FilePath exec_path(command_line->GetProgram());
- base::PathService::Get(base::FILE_EXE, &exec_path);
+ base::FilePath assets_path;
+ base::PathService::Get(base::DIR_ASSETS, &assets_path);
- return exec_path.DirName().Append(FILE_PATH_LITERAL("resources"));
+ return assets_path.Append(FILE_PATH_LITERAL("resources"));
#endif
}
} // namespace
```
@deepak1556
Copy link
Author

StringFileInfo block in the resources only has limited size for the version string and it cannot capture the values we want. Moving it to the StringTable helps overcome the size limit. Following changes need to be adopted to read from the table instead,

diff --git a/base/base_paths_win.cc b/base/base_paths_win.cc
index aa9d066a2a994..1771daeafb0ad 100644
--- a/base/base_paths_win.cc
+++ b/base/base_paths_win.cc
@@ -12,17 +12,46 @@
 #include "base/environment.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/path_service.h"
 #include "base/strings/string_util.h"
+#include "base/strings/string_util_win.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/current_module.h"
 #include "base/win/scoped_co_mem.h"
 #include "base/win/windows_version.h"
+#include "electron/shell/browser/resources/win/resource.h"

 using base::FilePath;

 namespace base {

+namespace {
+
+bool LoadStringResource(HMODULE resource_module,
+                        int resource_id,
+                        std::wstring& string) {
+  DCHECK(resource_module);
+
+  string.clear();
+
+  const wchar_t* string_resource = nullptr;
+  int string_length = LoadStringW(resource_module,
+                                  base::checked_cast<uint32_t>(resource_id),
+                                  reinterpret_cast<wchar_t*>(&string_resource),
+                                  /*cchBufferMax=*/0);
+  if (string_length <= 0) {
+    PLOG(ERROR) << "LoadStringW() failed for resource ID: " << resource_id;
+    return false;
+  }
+
+  string.append(string_resource, base::checked_cast<uint32_t>(string_length));
+  return true;
+}
+
+}  // namespace
+
 bool PathProviderWin(int key, FilePath* result) {
   // We need to go compute the value. It would be nice to support paths with
   // names longer than MAX_PATH, but the system functions don't seem to be
@@ -49,6 +78,18 @@ bool PathProviderWin(int key, FilePath* result) {
       cur = FilePath(system_buffer);
       break;
     }
+    case base::DIR_ASSETS: {
+      if (!PathService::Get(DIR_MODULE, &cur)) {
+        return false;
+      }
+      std::wstring product_version;
+      if (!LoadStringResource(CURRENT_MODULE(), IDS_ASSET_FOLDER_NAME,
+                              product_version)) {
+        break;
+      }
+      cur = cur.Append(product_version);
+      break;
+    }
     case base::DIR_WINDOWS:
       GetWindowsDirectory(system_buffer, MAX_PATH);
       cur = FilePath(system_buffer);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment