1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/native_library.h"
9 #include "base/files/file_util.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/threading/scoped_blocking_call.h"
18 using AddDllDirectory = HMODULE (*)(PCWSTR new_directory);
21 // This enum is used to back an UMA histogram, and should therefore be treated
23 enum LoadLibraryResult {
24 // LoadLibraryExW API/flags are available and the call succeeds.
26 // LoadLibraryExW API/flags are availabe to use but the call fails, then
27 // LoadLibraryW is used and succeeds.
29 // LoadLibraryExW API/flags are availabe to use but the call fails, then
30 // LoadLibraryW is used but fails as well.
32 // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used
34 UNAVAILABLE_AND_SUCCEED,
35 // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used
38 // Add new items before this one, always keep this one at the end.
42 // A helper method to log library loading result to UMA.
43 void LogLibrarayLoadResultToUMA(LoadLibraryResult result) {
44 UMA_HISTOGRAM_ENUMERATION("LibraryLoader.LoadNativeLibraryWindows", result,
45 LoadLibraryResult::END);
48 // A helper method to check if AddDllDirectory method is available, thus
49 // LOAD_LIBRARY_SEARCH_* flags are available on systems.
50 bool AreSearchFlagsAvailable() {
51 // The LOAD_LIBRARY_SEARCH_* flags are available on systems that have
52 // KB2533623 installed. To determine whether the flags are available, use
53 // GetProcAddress to get the address of the AddDllDirectory,
54 // RemoveDllDirectory, or SetDefaultDllDirectories function. If GetProcAddress
55 // succeeds, the LOAD_LIBRARY_SEARCH_* flags can be used with LoadLibraryEx.
56 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx
57 // The LOAD_LIBRARY_SEARCH_* flags are used in the LoadNativeLibraryHelper
59 auto add_dll_dir_func = reinterpret_cast<AddDllDirectory>(
60 GetProcAddress(GetModuleHandle(L"kernel32.dll"), "AddDllDirectory"));
61 return !!add_dll_dir_func;
64 // A helper method to encode the library loading result to enum
66 LoadLibraryResult GetLoadLibraryResult(bool are_search_flags_available,
67 bool has_load_library_succeeded) {
68 LoadLibraryResult result;
69 if (are_search_flags_available) {
70 if (has_load_library_succeeded)
71 result = LoadLibraryResult::FAIL_AND_SUCCEED;
73 result = LoadLibraryResult::FAIL_AND_FAIL;
74 } else if (has_load_library_succeeded) {
75 result = LoadLibraryResult::UNAVAILABLE_AND_SUCCEED;
77 result = LoadLibraryResult::UNAVAILABLE_AND_FAIL;
82 NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path,
83 NativeLibraryLoadError* error) {
84 // LoadLibrary() opens the file off disk.
85 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
87 HMODULE module = nullptr;
89 // This variable records the library loading result.
90 LoadLibraryResult load_library_result = LoadLibraryResult::SUCCEED;
92 bool are_search_flags_available = AreSearchFlagsAvailable();
93 if (are_search_flags_available) {
94 // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR flag is needed to search the library
95 // directory as the library may have dependencies on DLLs in this
97 module = ::LoadLibraryExW(
98 library_path.value().c_str(), nullptr,
99 LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
100 // If LoadLibraryExW succeeds, log this metric and return.
102 LogLibrarayLoadResultToUMA(load_library_result);
105 // GetLastError() needs to be called immediately after
106 // LoadLibraryExW call.
108 error->code = GetLastError();
111 // If LoadLibraryExW API/flags are unavailable or API call fails, try
112 // LoadLibraryW API. From UMA, this fallback is necessary for many users.
114 // Switch the current directory to the library directory as the library
115 // may have dependencies on DLLs in this directory.
116 bool restore_directory = false;
117 FilePath current_directory;
118 if (GetCurrentDirectory(¤t_directory)) {
119 FilePath plugin_path = library_path.DirName();
120 if (!plugin_path.empty()) {
121 SetCurrentDirectory(plugin_path);
122 restore_directory = true;
125 module = ::LoadLibraryW(library_path.value().c_str());
127 // GetLastError() needs to be called immediately after LoadLibraryW call.
128 if (!module && error)
129 error->code = GetLastError();
131 if (restore_directory)
132 SetCurrentDirectory(current_directory);
134 // Get the library loading result and log it to UMA.
135 LogLibrarayLoadResultToUMA(
136 GetLoadLibraryResult(are_search_flags_available, !!module));
142 std::string NativeLibraryLoadError::ToString() const {
143 return StringPrintf("%lu", code);
146 NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path,
147 const NativeLibraryOptions& options,
148 NativeLibraryLoadError* error) {
149 return LoadNativeLibraryHelper(library_path, error);
152 void UnloadNativeLibrary(NativeLibrary library) {
153 FreeLibrary(library);
156 void* GetFunctionPointerFromNativeLibrary(NativeLibrary library,
158 return reinterpret_cast<void*>(GetProcAddress(library, name.data()));
161 std::string GetNativeLibraryName(StringPiece name) {
162 DCHECK(IsStringASCII(name));
163 return name.as_string() + ".dll";
166 std::string GetLoadableModuleName(StringPiece name) {
167 return GetNativeLibraryName(name);