| // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "base/native_library.h" | 
 |  | 
 | #include <windows.h> | 
 |  | 
 | #include "base/files/file_util.h" | 
 | #include "base/metrics/histogram_macros.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "base/strings/stringprintf.h" | 
 | #include "base/strings/utf_string_conversions.h" | 
 | #include "base/threading/thread_restrictions.h" | 
 |  | 
 | namespace base { | 
 |  | 
 | using AddDllDirectory = HMODULE (*)(PCWSTR new_directory); | 
 |  | 
 | namespace { | 
 | // This enum is used to back an UMA histogram, and should therefore be treated | 
 | // as append-only. | 
 | enum LoadLibraryResult { | 
 |   // LoadLibraryExW API/flags are available and the call succeeds. | 
 |   SUCCEED = 0, | 
 |   // LoadLibraryExW API/flags are availabe to use but the call fails, then | 
 |   // LoadLibraryW is used and succeeds. | 
 |   FAIL_AND_SUCCEED, | 
 |   // LoadLibraryExW API/flags are availabe to use but the call fails, then | 
 |   // LoadLibraryW is used but fails as well. | 
 |   FAIL_AND_FAIL, | 
 |   // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used | 
 |   // and succeeds. | 
 |   UNAVAILABLE_AND_SUCCEED, | 
 |   // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used | 
 |   // but fails. | 
 |   UNAVAILABLE_AND_FAIL, | 
 |   // Add new items before this one, always keep this one at the end. | 
 |   END | 
 | }; | 
 |  | 
 | // A helper method to log library loading result to UMA. | 
 | void LogLibrarayLoadResultToUMA(LoadLibraryResult result) { | 
 |   UMA_HISTOGRAM_ENUMERATION("LibraryLoader.LoadNativeLibraryWindows", result, | 
 |                             LoadLibraryResult::END); | 
 | } | 
 |  | 
 | // A helper method to check if AddDllDirectory method is available, thus | 
 | // LOAD_LIBRARY_SEARCH_* flags are available on systems. | 
 | bool AreSearchFlagsAvailable() { | 
 |   // The LOAD_LIBRARY_SEARCH_* flags are available on systems that have | 
 |   // KB2533623 installed. To determine whether the flags are available, use | 
 |   // GetProcAddress to get the address of the AddDllDirectory, | 
 |   // RemoveDllDirectory, or SetDefaultDllDirectories function. If GetProcAddress | 
 |   // succeeds, the LOAD_LIBRARY_SEARCH_* flags can be used with LoadLibraryEx. | 
 |   // https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx | 
 |   // The LOAD_LIBRARY_SEARCH_* flags are used in the LoadNativeLibraryHelper | 
 |   // method. | 
 |   auto add_dll_dir_func = reinterpret_cast<AddDllDirectory>( | 
 |       GetProcAddress(GetModuleHandle(L"kernel32.dll"), "AddDllDirectory")); | 
 |   return !!add_dll_dir_func; | 
 | } | 
 |  | 
 | // A helper method to encode the library loading result to enum | 
 | // LoadLibraryResult. | 
 | LoadLibraryResult GetLoadLibraryResult(bool are_search_flags_available, | 
 |                                        bool has_load_library_succeeded) { | 
 |   LoadLibraryResult result; | 
 |   if (are_search_flags_available) { | 
 |     if (has_load_library_succeeded) | 
 |       result = LoadLibraryResult::FAIL_AND_SUCCEED; | 
 |     else | 
 |       result = LoadLibraryResult::FAIL_AND_FAIL; | 
 |   } else if (has_load_library_succeeded) { | 
 |     result = LoadLibraryResult::UNAVAILABLE_AND_SUCCEED; | 
 |   } else { | 
 |     result = LoadLibraryResult::UNAVAILABLE_AND_FAIL; | 
 |   } | 
 |   return result; | 
 | } | 
 |  | 
 | NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path, | 
 |                                       NativeLibraryLoadError* error) { | 
 |   // LoadLibrary() opens the file off disk. | 
 |   AssertBlockingAllowed(); | 
 |  | 
 |   HMODULE module = nullptr; | 
 |  | 
 |   // This variable records the library loading result. | 
 |   LoadLibraryResult load_library_result = LoadLibraryResult::SUCCEED; | 
 |  | 
 |   bool are_search_flags_available = AreSearchFlagsAvailable(); | 
 |   if (are_search_flags_available) { | 
 |     // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR flag is needed to search the library | 
 |     // directory as the library may have dependencies on DLLs in this | 
 |     // directory. | 
 |     module = ::LoadLibraryExW( | 
 |         library_path.value().c_str(), nullptr, | 
 |         LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); | 
 |     // If LoadLibraryExW succeeds, log this metric and return. | 
 |     if (module) { | 
 |       LogLibrarayLoadResultToUMA(load_library_result); | 
 |       return module; | 
 |     } | 
 |     // GetLastError() needs to be called immediately after | 
 |     // LoadLibraryExW call. | 
 |     if (error) | 
 |       error->code = GetLastError(); | 
 |   } | 
 |  | 
 |   // If LoadLibraryExW API/flags are unavailable or API call fails, try | 
 |   // LoadLibraryW API. | 
 |   // TODO(chengx): Currently, if LoadLibraryExW API call fails, LoadLibraryW is | 
 |   // still tried. We should strictly prefer the LoadLibraryExW over the | 
 |   // LoadLibraryW if LoadLibraryW is statistically showing no extra benefits. If | 
 |   // UMA metric shows that FAIL_AND_FAIL is the primary failure mode and/or | 
 |   // FAIL_AND_SUCCESS is close to zero, we should remove this fallback. | 
 |   // (http://crbug.com/701944) | 
 |  | 
 |   // Switch the current directory to the library directory as the library | 
 |   // may have dependencies on DLLs in this directory. | 
 |   bool restore_directory = false; | 
 |   FilePath current_directory; | 
 |   if (GetCurrentDirectory(¤t_directory)) { | 
 |     FilePath plugin_path = library_path.DirName(); | 
 |     if (!plugin_path.empty()) { | 
 |       SetCurrentDirectory(plugin_path); | 
 |       restore_directory = true; | 
 |     } | 
 |   } | 
 |  | 
 |   module = ::LoadLibraryW(library_path.value().c_str()); | 
 |  | 
 |   // GetLastError() needs to be called immediately after LoadLibraryW call. | 
 |   if (!module && error) | 
 |     error->code = GetLastError(); | 
 |  | 
 |   if (restore_directory) | 
 |     SetCurrentDirectory(current_directory); | 
 |  | 
 |   // Get the library loading result and log it to UMA. | 
 |   LogLibrarayLoadResultToUMA( | 
 |       GetLoadLibraryResult(are_search_flags_available, !!module)); | 
 |  | 
 |   return module; | 
 | } | 
 | }  // namespace | 
 |  | 
 | std::string NativeLibraryLoadError::ToString() const { | 
 |   return StringPrintf("%lu", code); | 
 | } | 
 |  | 
 | NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path, | 
 |                                            const NativeLibraryOptions& options, | 
 |                                            NativeLibraryLoadError* error) { | 
 |   return LoadNativeLibraryHelper(library_path, error); | 
 | } | 
 |  | 
 | void UnloadNativeLibrary(NativeLibrary library) { | 
 |   FreeLibrary(library); | 
 | } | 
 |  | 
 | void* GetFunctionPointerFromNativeLibrary(NativeLibrary library, | 
 |                                           StringPiece name) { | 
 |   return reinterpret_cast<void*>(GetProcAddress(library, name.data())); | 
 | } | 
 |  | 
 | std::string GetNativeLibraryName(StringPiece name) { | 
 |   DCHECK(IsStringASCII(name)); | 
 |   return name.as_string() + ".dll"; | 
 | } | 
 |  | 
 | std::string GetLoadableModuleName(StringPiece name) { | 
 |   return GetNativeLibraryName(name); | 
 | } | 
 |  | 
 | }  // namespace base |