commit 4e0430cda275754bb62e95fe11452e0d81b74ed5 Author: taynpg Date: Fri Mar 8 11:35:52 2024 +0800 first commit diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..4ed25d6 --- /dev/null +++ b/.clang-format @@ -0,0 +1,17 @@ +# .clang-format + +# 风格格式化 +BasedOnStyle: Google +# 4 空格缩进 +IndentWidth: 4 +# 连续对齐变量的声明 +AlignConsecutiveDeclarations: true +# 指针左侧对齐 +PointerAlignment: Left +# 访问说明符(public、private等)的偏移 +AccessModifierOffset: -4 +# 大括号 +BreakBeforeBraces: Custom +BraceWrapping: + # 函数定义后面大括号在新行 + AfterFunction: true diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..7d86823 --- /dev/null +++ b/.clangd @@ -0,0 +1,12 @@ +Hover: + ShowAKA: Yes +Diagnostics: + UnusedIncludes: None # 禁用未使用头文件提示 + Suppress: [ + anon_type_definition, # 禁用匿名的typedef提示 + unused-variable, # 禁用未使用变量提示 + unused-function, # 禁用未使用函数提示 + unused-includes + ] + ClangTidy: + Remove: misc-unused-alias-decls \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..26cf7e4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +bui* +.vs \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9b15169 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,34 @@ +{ + "files.autoSave": "onFocusChange", + "editor.fontSize": 14, + "editor.fontFamily": "'FiraCode Nerd Font Mono', 'FiraCode Nerd Font Mono', 'FiraCode Nerd Font Mono'", + "cmake.configureOnOpen": true, + "cmake.debugConfig": { + "console": "externalTerminal", + "args": [ + "G:\\ARTM\\bin\\x64\\Debug\\CaptureExe.exe" + ] + }, + "cmake.options.statusBarVisibility": "visible", + "cmake.generator": "Ninja", + "C_Cpp.intelliSenseEngine": "disabled", + "clangd.arguments": [ + "--header-insertion=never", + "--all-scopes-completion", + "--completion-style=detailed", + "--clang-tidy", + "-j=4", + "--pch-storage=memory", + "--compile-commands-dir=build", + "--background-index", + "--ranking-model=heuristics", + "--function-arg-placeholders=false", + "--query-driver=/usr/bin/clang++" + ], + "editor.inlayHints.enabled": "off", + "editor.unicodeHighlight.allowedLocales": { + "ja": true, + "zh-hant": true, + "zh-hans": true + } +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..826b0d6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required (VERSION 3.8) +project (dll-dependent) +set(CMAKE_CXX_STANDARD 11) + +set(CMAKE_PREFIX_PATH "C:/Bin/Boost") + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") +endif() +if (MSVC) + add_compile_options(/source-charset:utf-8) + add_compile_options(/EHsc) + add_compile_options(-D_CRT_SECURE_NO_WARNINGS) +endif() + +set(Boost_USE_STATIC_LIBS OFF) +find_package(Boost REQUIRED filesystem) +include_directories(${Boost_INCLUDES}) + +add_executable(dll-dependent main.cpp dll_handle.cpp cmd_opr.cpp) +target_link_libraries(dll-dependent PRIVATE ${Boost_LIBRARIES}) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a1f05ab --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# 简介 + +一个自动导入依赖dll库的工具。 \ No newline at end of file diff --git a/cmd_opr.cpp b/cmd_opr.cpp new file mode 100644 index 0000000..b3f7312 --- /dev/null +++ b/cmd_opr.cpp @@ -0,0 +1,50 @@ +#include "cmd_opr.h" + +#include + +#include +#include + + +std::string CmdOpr::exec_cmd(const std::string& cmd) +{ + std::string result{}; + + FILE* pipe = _popen(cmd.c_str(), "r"); + if (!pipe) { + return result; + } + + char buffer[128]{}; + while (!feof(pipe)) { + if (fgets(buffer, 128, pipe) != nullptr) { + result += buffer; + } + } + + _pclose(pipe); + return result; +} + +bool add_environment_path(const std::string& path) +{ + // 获取当前的 PATH 变量需要动态调整缓冲区大小 + DWORD bufferSize = GetEnvironmentVariable("PATH", nullptr, 0); + if (bufferSize == 0) { + std::cerr << "Failed to get PATH variable size!" << std::endl; + return false; + } + + char* currentPath = new char[bufferSize]; + GetEnvironmentVariable("PATH", currentPath, bufferSize); + + // 构造新的 PATH 变量值(在原有路径后面添加新路径) + std::string newPath = std::string(currentPath) + ";" + path; + + // 设置修改后的 PATH 变量到环境变量中 + if (!SetEnvironmentVariable("PATH", newPath.c_str())) { + std::cerr << "Failed to set PATH variable!" << std::endl; + } + delete[] currentPath; + return true; +} \ No newline at end of file diff --git a/cmd_opr.h b/cmd_opr.h new file mode 100644 index 0000000..cddab33 --- /dev/null +++ b/cmd_opr.h @@ -0,0 +1,12 @@ +#pragma once +#include + +class CmdOpr +{ +public: + CmdOpr() = default; + ~CmdOpr() = default; +public: + static std::string exec_cmd(const std::string& cmd); + static bool add_environment_path(const std::string& path); +}; \ No newline at end of file diff --git a/dll_handle.cpp b/dll_handle.cpp new file mode 100644 index 0000000..f15c5ba --- /dev/null +++ b/dll_handle.cpp @@ -0,0 +1,206 @@ +#include "dll_handle.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace fs = boost::filesystem; + +bool DllHandle::read_ini(const std::string& purpose_dir) +{ + no_dlls_.clear(); + exe_dlls_.clear(); + + fs::path cur_exe = boost::dll::program_location(); + fs::path config_path = cur_exe.parent_path().append("config.txt"); + if (!fs::exists(config_path)) { + std::cout << "配置文件不存在,不进行设置。" << std::endl; + return false; + } + std::ifstream in(config_path.string(), std::ios::in); + if (!in.is_open()) { + std::cout << "打开配置文件失败:" << cur_exe.string() + << ", 不进行设置。\n"; + return false; + } + std::list dirs{}; + + // dirs.push_back(purpose_dir); + + std::string tmp{}; + + int add_status = 0; + while (std::getline(in, tmp)) { + dirs.push_back(tmp); + } + in.close(); + + for (const auto& data : dirs) { + if (!fs::exists(data) || !fs::is_directory(data)) { + continue; + } + for (auto const& entry : fs::recursive_directory_iterator(data)) { + if (!entry.is_regular_file() || + entry.path().extension().string() != ".dll") { + continue; + } + DllInfo info; + info.name = entry.path().filename().string(); + info.full_path = entry.path().string(); + no_dlls_.push_back(info); + } + } + handle_multi_dll(); + + for (auto const& entry : fs::recursive_directory_iterator(purpose_dir)) { + if (!entry.is_regular_file() || + entry.path().extension().string() != ".dll") { + continue; + } + DllInfo info; + info.name = entry.path().filename().string(); + info.full_path = entry.path().string(); + exe_dlls_.push_back(info); + } + return true; +} + +std::list DllHandle::get_dependents(const std::string& binary) +{ + std::string cmd = "dumpbin /DEPENDENTS " + binary; + std::string result = CmdOpr::exec_cmd(cmd); + std::list dlls{}; + + std::list tmp{}; + boost::split(tmp, result, boost::is_any_of("\n")); + for (const auto& data : tmp) { + if (!boost::contains(data, ".dll") || boost::contains(data, "Dump")) { + continue; + } + std::string dllname(data); + boost::trim(dllname); + dlls.push_back(dllname); + } + return dlls; +} + +// 将字符串转换为小写形式 +std::string toLower(const std::string& str) +{ + std::string result = str; + std::transform(result.begin(), result.end(), result.begin(), ::tolower); + return result; +} + +bool DllHandle::find_dll(const std::string& name, DllInfo& info) +{ + bool find = false; + // 先在 exe 所在的目录找。 + for (const auto& data : exe_dlls_) { + if (toLower(name) != toLower(data.name)) { + continue; + } + info.name = name; + info.full_path = data.full_path; + find = true; + break; + } + + // 如果找到了就不往下找了 + if (find) { + return true; + } + + for (const auto& data : no_dlls_) { + if (toLower(name) != toLower(data.name)) { + continue; + } + info.name = name; + info.full_path = data.full_path; + find = true; + break; + } + return find; +} + +// 手动筛选多个dll的情况 +void DllHandle::handle_multi_dll() +{ + simple_dlls.clear(); + for (const auto& data : no_dlls_) { + simple_dlls[toLower(data.name)].push_back(data); + } + + for (auto& data : simple_dlls) { + if (data.second.size() > 1) { + std::cout << "========================\n"; + std::cout << "多个重名的dll,选择哪一个?\n"; + int index = 0; + for (auto& dll : data.second) { + dll.index = ++index; + std::cout << dll.index << ":" << dll.full_path << ".\n"; + } + std::string input{}; + std::cin >> input; + int select = std::stoi(input); + data.second.remove_if( + [&](const DllInfo& info) { return info.index != select; }); + } + } + no_dlls_.clear(); + for (const auto& data : simple_dlls) { + no_dlls_.push_back(data.second.front()); + } +} + +bool DllHandle::is_have(const std::list& vec, const std::string& name) +{ + bool find = false; + for (const auto& data : vec) { + if (toLower(data.name) != toLower(name)) { + continue; + } + find = true; + break; + } + return find; +} + +void DllHandle::copy_dll(const std::string& purpose_dir, + const std::list& tasks) +{ + fs::path report_dir = fs::path(purpose_dir).parent_path(); + auto now = std::chrono::system_clock::now(); + std::time_t currentTime = std::chrono::system_clock::to_time_t(now); + + std::tm timeInfo; + localtime_s(&timeInfo, ¤tTime); + + char buffer[256]{}; + std::strftime(buffer, sizeof(buffer), "report_%Y%m%d%H%M%S.txt", &timeInfo); + std::string report_name = report_dir.append(buffer).string(); + + std::ofstream out(report_name, std::ios::out); + if (!out.is_open()) { + std::cout << "打开报告文件:" << report_name << "失败,结束执行。\n"; + return; + } + for (const auto& data : tasks) { + fs::path des = fs::path(purpose_dir).append(data.name); + if (fs::exists(des)) { + std::string s = "跳过已存在的文件:" + des.string(); + out << s << "\n"; + std::cout << s << "\n"; + } else { + fs::copy(data.full_path, des); + std::string s = "复制文件:" + data.full_path; + out << s << "\n"; + std::cout << s << "\n"; + } + } + out.close(); +} diff --git a/dll_handle.h b/dll_handle.h new file mode 100644 index 0000000..d764f77 --- /dev/null +++ b/dll_handle.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include +#include "cmd_opr.h" + +struct DllInfo { + int index{}; + std::string name{}; + std::string full_path{}; +}; + +class DllHandle { +public: + DllHandle() = default; + ~DllHandle() = default; + +public: + // 读取配置文件 + bool read_ini(const std::string& purpose_dir); + // 获取一个二进制文件的依赖(参数为全路径) + std::list get_dependents(const std::string& binary); + // 查找配置文件中配置的目录中指定名称的dll的信息 + bool find_dll(const std::string& name, DllInfo& info); + // 是否在容器中 + static bool is_have(const std::list& vec, const std::string& name); + // 处理dll复制 + static void copy_dll(const std::string& purpose_dir, + const std::list& tasks); + // 手动筛选多个dll的情况 + void handle_multi_dll(); + +private: + std::list no_dlls_{}; + std::list exe_dlls_{}; + std::map> simple_dlls{}; +}; diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..1856f18 --- /dev/null +++ b/main.cpp @@ -0,0 +1,63 @@ +#include +#include + +#include "dll_handle.h" + + +namespace fs = boost::filesystem; + +/* + * 注意: + 1.配置文件若有中文需要GBK编码 + 2.程序会递归查找指定目录下所有的dll,如果有重复内容,请指定排除目录。 + 3.配置文件名为 config.txt,与当前程序同目录,内容格式为: + 每一行一个文件夹。 + */ + +int main(int argc, char** argv) +{ + if (argc < 2) { + + std::string info("参数不够,第二个参数传入要处理的二进制文件路径(注意需要全路径)\n" + "配置文件名为 config.txt,与当前程序同目录,内容格式为:\n" + "每一行一个文件夹。"); + std::cout << info << std::endl; + return 0; + } + + fs::path exe_path(argv[1]); + if (!fs::exists(exe_path)) { + std::cout << "传入的路径不存在,请检查。" << std::endl; + return 0; + } + fs::path exe_dir = exe_path.parent_path(); + + DllHandle handle{}; + handle.read_ini(exe_dir.string()); + + // handle.get_dependents() + + // // Get All Dlls Dependents + std::list tmp_list{}; + std::list task{}; + tmp_list = handle.get_dependents(argv[1]); + + DllInfo tmp_info{}; + while (!tmp_list.empty()) { + std::string& name = tmp_list.front(); + if (DllHandle::is_have(task, name) || + !handle.find_dll(name, tmp_info)) { + tmp_list.pop_front(); + continue; + } + // std::cout << tmp_info.name << "\n"; + auto dep = handle.get_dependents(tmp_info.full_path); + for (const auto& data : dep) { + tmp_list.push_back(data); + } + task.push_back(tmp_info); + tmp_list.pop_front(); + } + handle.copy_dll(exe_dir.string(), task); + return 0; +}