From 8b4109014909c41f065c0a739f48280e664f27cd Mon Sep 17 00:00:00 2001 From: nayk Date: Sat, 5 Apr 2025 12:02:06 +0300 Subject: [PATCH] Initial commit --- .gitignore | 107 ++++++++++++++++++++++ LICENSE | 9 ++ LICENSE.ru | 25 +++++ README.md | 3 +- sources/CMakeLists.txt | 35 +++++++ sources/main.cpp | 201 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 379 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 LICENSE.ru create mode 100644 sources/CMakeLists.txt create mode 100644 sources/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..23a669a --- /dev/null +++ b/.gitignore @@ -0,0 +1,107 @@ +.build/ +_other/ +_distrib*/ + +# Tmp files +*~ +Thumbs.db* + +# C++ objects and libs +*.slo +*.lo +*.o +*.a +*.la +*.lai +*.so +*.so.* +*.dll +*.dylib +*.ko +*.obj +*.elf +*.lib + +# Qt-es +object_script.*.Release +object_script.*.Debug +*_plugin_import.cpp +/.qmake.cache +/.qmake.stash +*.pro.user +*.pro.user.* +*.qbs.user +*.qbs.user.* +*.moc +moc_*.cpp +moc_*.h +qrc_*.cpp +ui_*.h +*.qmlc +*.jsc +Makefile* +*build-* +*.qm +*.prl + +# Qt unit tests +target_wrapper.* + +# QtCreator +*.autosave + +# QtCreator Qml +*.qmlproject.user +*.qmlproject.user.* + +# QtCreator CMake +CMakeLists.txt.user* + +# QtCreator 4.8< compilation database +compile_commands.json + +# QtCreator local machine specific files for imported projects +*creator.user* + +*_qmlcache.qrc + +# ---> C +# Prerequisites +*.d + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# Fortran module files +*.mod +*.smod + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..26bc826 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2024 Evgeny Teterin (nayk) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/LICENSE.ru b/LICENSE.ru new file mode 100644 index 0000000..7202602 --- /dev/null +++ b/LICENSE.ru @@ -0,0 +1,25 @@ +MIT лицензия + +Copyright (c) 2024 Evgeny Teterin (nayk) + +Данная лицензия разрешает лицам, получившим копию +данного программного обеспечения и сопутствующей документации +(в дальнейшем именуемыми «Программное Обеспечение»), безвозмездно +использовать Программное Обеспечение без ограничений, +включая неограниченное право на использование, копирование, изменение, +слияние, публикацию, распространение, сублицензирование и/или продажу +копий Программного Обеспечения, а также лицам, которым предоставляется +данное Программное Обеспечение, при соблюдении следующих условий: + +Указанное выше уведомление об авторском праве и данные условия +должны быть включены во все копии или значимые части данного Программного Обеспечения. + +ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», +БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, +ВКЛЮЧАЯ ГАРАНТИИ ТОВАРНОЙ ПРИГОДНОСТИ, СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ +НАЗНАЧЕНИЮ И ОТСУТСТВИЯ НАРУШЕНИЙ, НО НЕ ОГРАНИЧИВАЯСЬ ИМИ. +НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ +ПО КАКИМ-ЛИБО ИСКАМ, ЗА УЩЕРБ ИЛИ ПО ИНЫМ ТРЕБОВАНИЯМ, В ТОМ ЧИСЛЕ, +ПРИ ДЕЙСТВИИ КОНТРАКТА, ДЕЛИКТЕ ИЛИ ИНОЙ СИТУАЦИИ, +ВОЗНИКШИМ ИЗ-ЗА ИСПОЛЬЗОВАНИЯ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ +ИЛИ ИНЫХ ДЕЙСТВИЙ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ. diff --git a/README.md b/README.md index fa4da4c..4802362 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ -# Launcher +# Application Launcher +Запускатор для приложений Qt и др. diff --git a/sources/CMakeLists.txt b/sources/CMakeLists.txt new file mode 100644 index 0000000..4eaabd4 --- /dev/null +++ b/sources/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.24) + +project(launcher LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Настройки для MSVC +if(MSVC) + # Статическая линковка CRT + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + + # Оптимизация размера и скорости + add_compile_options( + "$<$:/Os>" # Оптимизация по размеру + "$<$:/Oy>" # Отключение кадров стека + "$<$:/GL>" # Включение LTO + ) + + # Настройки линковщика + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/LTCG /OPT:REF /OPT:ICF") +endif() + +add_executable(${PROJECT_NAME} WIN32 main.cpp) + +target_link_libraries(${PROJECT_NAME} + PRIVATE Kernel32.lib + PRIVATE shlwapi.lib +) + +include(GNUInstallDirs) +install(TARGETS ${PROJECT_NAME} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) diff --git a/sources/main.cpp b/sources/main.cpp new file mode 100644 index 0000000..c7b4700 --- /dev/null +++ b/sources/main.cpp @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include +#include // Для IsWindows7OrGreater() + +#pragma comment(lib, "shlwapi.lib") +//============================================================================== +// Проверка версии Windows (требуется Windows 7+) +bool IsSupportedWindowsVersion() +{ + return IsWindows7OrGreater(); +} +//============================================================================== +// Проверка существования файла +bool FileExists(const std::wstring& path) +{ + DWORD attrs = GetFileAttributesW(path.c_str()); + return (attrs != INVALID_FILE_ATTRIBUTES) && !(attrs & FILE_ATTRIBUTE_DIRECTORY); +} +//============================================================================== +// Поиск всех подкаталогов, содержащих "lib" в имени (регистронезависимо) +std::vector FindLibSubdirectories(const std::wstring& baseDir) +{ + std::vector result; + + WIN32_FIND_DATAW findData; + std::wstring searchPath = baseDir + L"\\*"; + HANDLE hFind = FindFirstFileW(searchPath.c_str(), &findData); + + if (hFind != INVALID_HANDLE_VALUE) { + do { + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + std::wstring dirName = findData.cFileName; + if (dirName != L"." && dirName != L"..") { + // Преобразуем в нижний регистр для проверки + std::wstring lowerName = dirName; + std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower); + + if (lowerName.find(L"lib") != std::wstring::npos) { + result.push_back(baseDir + L"\\" + dirName); + } + } + } + } while (FindNextFileW(hFind, &findData)); + FindClose(hFind); + } + + return result; +} +//============================================================================== +// Получение директории текущего EXE +std::wstring GetExeDirectory() +{ + wchar_t buf[MAX_PATH]; + GetModuleFileNameW(nullptr, buf, MAX_PATH); + return std::wstring(buf).substr(0, std::wstring(buf).find_last_of(L"\\/")); +} +//============================================================================== +// Построение пути к целевой программе +std::wstring GetTargetPath() +{ + // Получаем путь к текущему исполняемому файлу + wchar_t currentPath[MAX_PATH]; + GetModuleFileNameW(nullptr, currentPath, MAX_PATH); + + // Извлекаем имя файла (без пути) + std::wstring exePath(currentPath); + size_t lastSlash = exePath.find_last_of(L"\\/"); + std::wstring exeName = (lastSlash == std::wstring::npos) ? exePath : exePath.substr(lastSlash + 1); + + // Формируем путь к программе в подкаталоге bin + std::wstring targetPath = exePath.substr(0, lastSlash + 1) + L"bin\\" + exeName; + + if (!FileExists(targetPath)) { + MessageBoxW(nullptr, + (L"File not found:\n" + targetPath).c_str(), + L"Error", MB_ICONERROR); + return L""; + } + return targetPath; +} +//============================================================================== +// Создание нового блока окружения с обновлённым PATH +std::wstring BuildEnvironment() +{ + std::wstring baseDir = GetExeDirectory(); + std::wstring newPath; + + // 1. Обновляем PATH + const std::vector paths = { + baseDir + L"\\bin", + baseDir + L"\\modules", + baseDir + L"\\plugins" + }; + + for (const auto& path : paths) { + if (PathIsDirectoryW(path.c_str())) { + if (!newPath.empty()) newPath += L';'; + newPath += path; + } + } + + // Добавляем все подкаталоги с "lib" в имени + auto libDirs = FindLibSubdirectories(baseDir); + for (const auto& path : libDirs) { + if (!newPath.empty()) newPath += L";"; + newPath += path; + } + + // Добавляем системный PATH + wchar_t sysPath[32767]; + GetEnvironmentVariableW(L"PATH", sysPath, 32767); + newPath = newPath + L';' + sysPath; + + // 2. Обрабатываем QT_PLUGIN_PATH + std::wstring qtPluginPath = baseDir + L"\\plugins"; + wchar_t oldQtPluginPath[32767]; + DWORD qtPathSize = GetEnvironmentVariableW(L"QT_PLUGIN_PATH", oldQtPluginPath, 32767); + + if (qtPathSize > 0) { + // Если переменная уже существует, заменяем её значение + qtPluginPath = baseDir + L"\\plugins;" + oldQtPluginPath; + } + + // 3. Формируем полный блок среды + std::wstring envBlock = + L"PATH=" + newPath + L'\0' + + L"QT_PLUGIN_PATH=" + qtPluginPath + L'\0'; + + // Копируем остальные переменные (кроме PATH и QT_PLUGIN_PATH) + LPWCH envStrings = GetEnvironmentStringsW(); + for (LPWSTR var = envStrings; *var; var += wcslen(var) + 1) { + if (wcsncmp(var, L"PATH=", 5) != 0 && + wcsncmp(var, L"QT_PLUGIN_PATH=", 15) != 0) { + envBlock += std::wstring(var) + L'\0'; + } + } + envBlock += L'\0'; + + FreeEnvironmentStringsW(envStrings); + return envBlock; +} +//============================================================================== +int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) +{ + // Проверка версии Windows + if (!IsSupportedWindowsVersion()) { + MessageBoxW(nullptr, + L"Minimum version Windows 7", + L"Error version", MB_ICONERROR); + return 1; + } + + // Получаем путь к целевой программе + std::wstring targetPath = GetTargetPath(); + if (targetPath.empty()) return 1; + + // Подготавливаем окружение + std::wstring envBlock = BuildEnvironment(); + + // Запускаем процесс + STARTUPINFOW si = { sizeof(si) }; + PROCESS_INFORMATION pi; + + std::wstring cmdLine = L"\"" + targetPath + L"\""; + LPWSTR args = GetCommandLineW(); + if (args) { + std::wstring fullArgs = args; + size_t pos = fullArgs.find(L' '); + if (pos != std::wstring::npos) { + cmdLine += fullArgs.substr(pos); + } + } + + if (!CreateProcessW( + targetPath.c_str(), + &cmdLine[0], + nullptr, + nullptr, + FALSE, + CREATE_UNICODE_ENVIRONMENT, + (LPVOID)envBlock.c_str(), + nullptr, + &si, + &pi + )) { + DWORD err = GetLastError(); + MessageBoxW(nullptr, + (L"Error create process (code " + std::to_wstring(err) + L")\n" + targetPath).c_str(), + L"Error", MB_ICONERROR); + return 1; + } + + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + return 0; +} +//============================================================================== +