Skip to content

Commit

Permalink
impr: Refactored forwarder executable and add lots more information t…
Browse files Browse the repository at this point in the history
…o it
  • Loading branch information
WerWolv committed Sep 27, 2023
1 parent b3ef615 commit e80c7bf
Show file tree
Hide file tree
Showing 29 changed files with 210 additions and 136 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ addBundledLibraries()
# Add ImHex sources
add_subdirectory(lib/libimhex)
add_subdirectory(main)
add_custom_target(imhex_all ALL DEPENDS main libimhex)
add_custom_target(imhex_all ALL DEPENDS main main-forwarder libimhex)

# Add unit tests
enable_testing()
Expand Down
6 changes: 3 additions & 3 deletions cmake/build_helpers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ macro(createPackage)
else()
install(TARGETS main RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
if(WIN32) # Forwarder is only needed on Windows
install(TARGETS imhex-forwarder BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR})
install(TARGETS main-forwarder BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
endif()

Expand Down Expand Up @@ -611,12 +611,12 @@ function(generatePDBs)
)
FetchContent_Populate(cv2pdb)

set(PDBS_TO_GENERATE main imhex-forwarder libimhex ${PLUGINS})
set(PDBS_TO_GENERATE main main-forwarder libimhex ${PLUGINS})
add_custom_target(pdbs)
foreach (PDB ${PDBS_TO_GENERATE})
if (PDB STREQUAL "main")
set(GENERATED_PDB imhex)
elseif (PDB STREQUAL "imhex-forwarder")
elseif (PDB STREQUAL "main-forwarder")
set(GENERATED_PDB imhex-gui)
elseif (PDB STREQUAL "libimhex")
set(GENERATED_PDB libimhex)
Expand Down
63 changes: 4 additions & 59 deletions main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,65 +1,10 @@
project(main)

add_executable(main ${APPLICATION_TYPE}
source/main.cpp
source/crash_handlers.cpp

source/window/window.cpp
source/window/win_window.cpp
source/window/macos_window.cpp
source/window/linux_window.cpp

source/messaging/common.cpp
source/messaging/linux.cpp
source/messaging/macos.cpp
source/messaging/win.cpp

source/init/splash_window.cpp
source/init/tasks.cpp

${IMHEX_ICON}
)

target_include_directories(main PUBLIC include)
setupCompilerFlags(main)

set(LIBROMFS_RESOURCE_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/romfs)
set(LIBROMFS_PROJECT_NAME imhex)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../lib/external/libromfs ${CMAKE_CURRENT_BINARY_DIR}/main/libromfs EXCLUDE_FROM_ALL)
set_target_properties(${LIBROMFS_LIBRARY} PROPERTIES POSITION_INDEPENDENT_CODE ON)

if (WIN32)
# HACK: `imhex` -> `imhex-gui` and we add a forwarder as `imhex`, so that the user can just run `imhex` and it will start the GUI
# Workaround for .NET plugin crashing caused by the console window being freed.
set(IMHEX_APPLICATION_NAME "imhex-gui")
add_executable(imhex-forwarder
source/forwarder/main.cpp
${IMHEX_ICON})
target_link_libraries(imhex-forwarder PRIVATE libwolv-io ${FMT_LIBRARIES})
set_target_properties(imhex-forwarder PROPERTIES
OUTPUT_NAME "imhex"
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/..
CXX_VISIBILITY_PRESET hidden
POSITION_INDEPENDENT_CODE ON)
set(IMHEX_APPLICATION_NAME "imhex-gui")
else ()
set(IMHEX_APPLICATION_NAME "imhex")
endif ()

set_target_properties(main PROPERTIES
OUTPUT_NAME ${IMHEX_APPLICATION_NAME}
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/..
CXX_VISIBILITY_PRESET hidden
POSITION_INDEPENDENT_CODE ON)

add_compile_definitions(IMHEX_PROJECT_NAME="${PROJECT_NAME}")

target_link_libraries(main PRIVATE libromfs-imhex libimhex ${FMT_LIBRARIES})
add_subdirectory(gui)
if (WIN32)
target_link_libraries(main PRIVATE usp10 wsock32 ws2_32 Dwmapi.lib)
else ()
target_link_libraries(main PRIVATE pthread)
endif ()

if (APPLE)
add_compile_definitions(GL_SILENCE_DEPRECATION)
endif ()
add_subdirectory(forwarder)
endif ()
12 changes: 12 additions & 0 deletions main/forwarder/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
project(main-forwarder)

add_executable(main-forwarder
source/main.cpp
${IMHEX_ICON}
)
target_link_libraries(main-forwarder PRIVATE libwolv-io ${FMT_LIBRARIES})
set_target_properties(main-forwarder PROPERTIES
OUTPUT_NAME "imhex"
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/..
CXX_VISIBILITY_PRESET hidden
POSITION_INDEPENDENT_CODE ON)
88 changes: 88 additions & 0 deletions main/forwarder/source/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* This is a simple forwarder that launches the main ImHex executable with the
* same command line as the current process. The reason for this is that even
* though ImHex is a GUI application in general, it also has a command line
* interface that can be used to perform various tasks. The issue with this is
* that kernel32 will automatically allocate a console for us which will float
* around in the background if we launch ImHex from the explorer. This forwarder
* will get rid of the console window if ImHex was launched from the explorer and
* enables ANSI escape sequences if ImHex was launched from the command line.
*
* The main reason this is done in a separate executable is because we use FreeConsole()
* to get rid of the console window. Due to bugs in older versions of Windows (Windows 10 and older)
* this will also close the process's standard handles (stdin, stdout, stderr) in
* a way that cannot be recovered from. This means that if we were to do this in the
* main application, if any code would try to interact with these handles (duplicate them
* or modify them in any way), the application would crash.
*
* None of this would be necessary if Windows had a third type of application (besides
* console and GUI) that would act like a console application but would not allocate
* a console window. This would allow us to have a single executable that would work
* the same as on all other platforms. There are plans to add this to Windows in the
* future, but it is not yet available. Also even if it was available, it would not
* be available on older versions of Windows, so we would still need this forwarder
*/

#include <windows.h>
#include <wolv/io/fs.hpp>

#include <fmt/format.h>

void handleConsoleWindow() {
HWND consoleWindow = ::GetConsoleWindow();
DWORD processId = 0;
::GetWindowThreadProcessId(consoleWindow, &processId);

// Check if ImHex was launched from the explorer or from the command line
if (::GetCurrentProcessId() == processId) {
// If it was launched from the explorer, kernel32 has allocated a console for us
// Get rid of it to avoid having a useless console window floating around
::FreeConsole();
} else {
// If it was launched from the command line, enable ANSI escape sequences to have colored output
auto hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE);
if (hConsole != INVALID_HANDLE_VALUE) {
DWORD mode = 0;
if (::GetConsoleMode(hConsole, &mode) == TRUE) {
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT;
::SetConsoleMode(hConsole, mode);
}
}
}
}

int launchExecutable() {
// Get the path of the main ImHex executable
auto executablePath = wolv::io::fs::getExecutablePath();
auto executableFullPath = executablePath->parent_path() / "imhex-gui.exe";

::PROCESS_INFORMATION process = { };
::STARTUPINFOW startupInfo = { };
startupInfo.cb = sizeof(STARTUPINFOW);

// Create a new process for imhex-gui.exe with the same command line as the current process
if (::CreateProcessW(executableFullPath.wstring().c_str(), ::GetCommandLineW(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startupInfo, &process) == FALSE) {
// Handle error if the process could not be created

auto errorCode = ::GetLastError();
auto errorMessageString = std::system_category().message(errorCode);

auto errorMessage = fmt::format("Failed to start ImHex:\n\nError code: 0x{:08X}\n\n{}", errorCode, errorMessageString);

::MessageBoxA(nullptr, errorMessage.c_str(), "ImHex Forwarder", MB_OK | MB_ICONERROR);

return EXIT_FAILURE;
}

::WaitForSingleObject(process.hProcess, INFINITE);
::CloseHandle(process.hProcess);

return EXIT_SUCCESS;
}

int main() {
handleConsoleWindow();
auto result = launchExecutable();

return result;
}
48 changes: 48 additions & 0 deletions main/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
project(main)

add_executable(main ${APPLICATION_TYPE}
source/main.cpp
source/crash_handlers.cpp

source/window/window.cpp
source/window/win_window.cpp
source/window/macos_window.cpp
source/window/linux_window.cpp

source/messaging/common.cpp
source/messaging/linux.cpp
source/messaging/macos.cpp
source/messaging/win.cpp

source/init/splash_window.cpp
source/init/tasks.cpp

${IMHEX_ICON}
)

target_include_directories(main PUBLIC include)
setupCompilerFlags(main)

set(LIBROMFS_RESOURCE_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/romfs)
set(LIBROMFS_PROJECT_NAME imhex)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../lib/external/libromfs ${CMAKE_CURRENT_BINARY_DIR}/main/gui/libromfs EXCLUDE_FROM_ALL)
set_target_properties(${LIBROMFS_LIBRARY} PROPERTIES POSITION_INDEPENDENT_CODE ON)

set_target_properties(main PROPERTIES
OUTPUT_NAME ${IMHEX_APPLICATION_NAME}
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/..
CXX_VISIBILITY_PRESET hidden
POSITION_INDEPENDENT_CODE ON)

add_compile_definitions(IMHEX_PROJECT_NAME="${PROJECT_NAME}")

target_link_libraries(main PRIVATE libromfs-imhex libimhex ${FMT_LIBRARIES})
if (WIN32)
target_link_libraries(main PRIVATE usp10 wsock32 ws2_32 Dwmapi.lib)
else ()
target_link_libraries(main PRIVATE pthread)
endif ()

if (APPLE)
add_compile_definitions(GL_SILENCE_DEPRECATION)
endif ()
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes.
Loading

0 comments on commit e80c7bf

Please sign in to comment.