diff --git a/samples/CPlusPlusWin32/VeloCppWinSample.cpp b/samples/CPlusPlusWin32/VeloCppWinSample.cpp index f8e5ac7a..652f130c 100644 --- a/samples/CPlusPlusWin32/VeloCppWinSample.cpp +++ b/samples/CPlusPlusWin32/VeloCppWinSample.cpp @@ -25,7 +25,7 @@ HINSTANCE hInst; const WCHAR szTitle[] = L"Velopack C++ Sample App"; const WCHAR szWindowClass[] = L"VeloCppWinSample"; -std::optional managerOpt = std::nullopt; +std::unique_ptr manager; std::optional updInfo = std::nullopt; bool downloaded = false; @@ -38,22 +38,36 @@ INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); std::wstring Utf8ToWString(std::string const& str); std::string WStringToUtf8(std::wstring const& wstr); +void handle_vpkc_log(const char* pszLevel, const char* pszMessage) +{ + std::cout << pszLevel << ": " << pszMessage << std::endl; +} + int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { + // Redirect StdOut to console window + AllocConsole(); + FILE* fp; + _wfreopen_s(&fp, L"CONOUT$", L"w", stdout); + _wfreopen_s(&fp, L"CONOUT$", L"w", stderr); + _wfreopen_s(&fp, L"CONIN$", L"r", stdin); + + // Initialize Velopack log capture std::cout << "Velopack C++ Sample App" << std::endl; - UNREFERENCED_PARAMETER(hPrevInstance); + vpkc_set_log(handle_vpkc_log); // This should run as early as possible in the main method. // Velopack may exit / restart the app at this point. // See VelopackApp class for more options/configuration. - VelopackApp::Build().Run(); + VelopackApp::Build() + .Run(); try { // If the app is not installed, creating an UpdateManager will throw an exception. - managerOpt = UpdateManager(UPDATE_URL); + manager = std::make_unique(UPDATE_URL); } catch (std::exception& e) { std::wstring what = Utf8ToWString(e.what()); @@ -136,14 +150,9 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_COMMAND: { if (LOWORD(wParam) == BN_CLICKED) { - if (!managerOpt.has_value()) { - break; // this should not happen because we construct UpdateManager in wWinMain - } - auto manager = managerOpt.value(); - if ((HWND)lParam == hCheckButton) { try { - updInfo = manager.CheckForUpdates(); + updInfo = manager->CheckForUpdates(); if (updInfo.has_value()) { std::string version = updInfo.value().TargetFullRelease.Version; std::wstring message = L"Update available: " + Utf8ToWString(version); @@ -161,7 +170,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) else if ((HWND)lParam == hDownloadButton) { if (updInfo.has_value()) { try { - manager.DownloadUpdates(updInfo.value()); + manager->DownloadUpdates(updInfo.value()); downloaded = true; MessageBoxCentered(hWnd, L"Download completed successfully.", szTitle, MB_OK); } @@ -179,7 +188,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) MessageBoxCentered(hWnd, L"Download an update first.", szTitle, MB_OK); } else { - manager.WaitExitThenApplyUpdate(updInfo.value()); + manager->WaitExitThenApplyUpdate(updInfo.value()); exit(0); } } @@ -192,10 +201,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) HDC hdc = BeginPaint(hWnd, &ps); RECT r{ 0, 5, ps.rcPaint.right, ps.rcPaint.bottom }; - std::string currentVersion = "Not Installed"; - if (managerOpt.has_value()) { - currentVersion = managerOpt.value().GetCurrentVersion(); - } + std::string currentVersion = manager->GetCurrentVersion(); auto ver = Utf8ToWString(currentVersion); std::wstring text = L"Welcome to v" + ver + L" of the\nVelopack C++ Sample App."; DrawText(hdc, text.c_str(), -1, &r, DT_BOTTOM | DT_CENTER); diff --git a/samples/CPlusPlusWin32/VeloCppWinSample.sln b/samples/CPlusPlusWin32/VeloCppWinSample.sln index ab3e94e2..f3fef84c 100644 --- a/samples/CPlusPlusWin32/VeloCppWinSample.sln +++ b/samples/CPlusPlusWin32/VeloCppWinSample.sln @@ -5,6 +5,12 @@ VisualStudioVersion = 17.8.34525.116 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VeloCppWinSample", "VeloCppWinSample.vcxproj", "{F9BB1F11-3827-4745-B11B-77FA5DBE1195}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{25F8ED9F-9255-4CEC-AE44-7F2EAA392C18}" + ProjectSection(SolutionItems) = preProject + build.bat = build.bat + readme.md = readme.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 diff --git a/samples/CPlusPlusWin32/dev-build.bat b/samples/CPlusPlusWin32/build.bat similarity index 84% rename from samples/CPlusPlusWin32/dev-build.bat rename to samples/CPlusPlusWin32/build.bat index c0809b4a..89d46981 100644 --- a/samples/CPlusPlusWin32/dev-build.bat +++ b/samples/CPlusPlusWin32/build.bat @@ -13,9 +13,8 @@ if "%~1"=="" ( ) echo. -echo Building Velopack Rust -cd %~dp0..\..\..\for-rust -cargo build --features cli -r +echo Building Velopack C Lib with Cargo +cargo build -p velopack_libc cd %~dp0 @@ -43,8 +42,6 @@ cd %~dp0 echo #define UPDATE_URL "REPLACE_ME" > constants.h -copy %~dp0..\..\..\for-rust\target\release\vfusion.exe x64\Debug - echo. echo Building Velopack Release v%~1 -vpk pack -u VeloCppWinSample -o releases -p x64\Debug -v %* \ No newline at end of file +vpk pack -u VelopackCppWin32Sample -o releases -p x64\Debug -v %* -e VeloCppWinSample.exe \ No newline at end of file diff --git a/samples/CPlusPlusWin32/readme.md b/samples/CPlusPlusWin32/readme.md index 3b0a5694..1e0b52c5 100644 --- a/samples/CPlusPlusWin32/readme.md +++ b/samples/CPlusPlusWin32/readme.md @@ -1,5 +1,5 @@ -# VeloCppWinSample -_Prerequisites: Rust/Cargo, Dotnet, Msbuild_ +# VelopackCppWin32Sample +_Prerequisites: Rust/Cargo, Msbuild_ This app is purely a proof of concept at this time, `velopack.hpp` currently only works on windows and needs testing / fixes for other operating systems, and probably also needs fixing for unicode/strings. This sample is made up of a simple Win32 desktop app, generated via a Visual Studio template, and it includes `velopack.hpp` and [`subprocess.h`](https://github.com/sheredom/subprocess.h). diff --git a/src/lib-cpp/include/Velopack.h b/src/lib-cpp/include/Velopack.h index 4698fc6e..0f7c16da 100644 --- a/src/lib-cpp/include/Velopack.h +++ b/src/lib-cpp/include/Velopack.h @@ -48,7 +48,7 @@ extern "C" { #endif -typedef void* vpkc_update_manager_t; +typedef void vpkc_update_manager_t; typedef void (*vpkc_progress_callback_t)(size_t progress); typedef void (*vpkc_log_callback_t)(const char* pszLevel, const char* pszMessage); typedef void (*vpkc_hook_callback_t)(const char* pszAppVersion); @@ -93,7 +93,7 @@ typedef struct { // !! AUTO-GENERATED-END C_TYPES // Update Manager -VPKC_EXPORT bool VPKC_CALL vpkc_new_update_manager(const char* pszUrlOrString, vpkc_update_options_t* pOptions, vpkc_locator_config_t* pLocator, vpkc_update_manager_t* pManager); +VPKC_EXPORT bool VPKC_CALL vpkc_new_update_manager(const char* pszUrlOrString, vpkc_update_options_t* pOptions, vpkc_locator_config_t* pLocator, vpkc_update_manager_t** pManager); VPKC_EXPORT size_t VPKC_CALL vpkc_get_current_version(vpkc_update_manager_t* pManager, char* pszVersion, size_t cVersion); VPKC_EXPORT size_t VPKC_CALL vpkc_get_app_id(vpkc_update_manager_t* pManager, char* pszId, size_t cId); VPKC_EXPORT bool VPKC_CALL vpkc_is_portable(vpkc_update_manager_t* pManager); @@ -330,7 +330,7 @@ public: class UpdateManager { private: - vpkc_update_manager_t m_pManager; + vpkc_update_manager_t* m_pManager = 0; public: UpdateManager(const std::string& urlOrPath, const UpdateOptions* options = nullptr, const VelopackLocatorConfig* locator = nullptr) { vpkc_update_options_t* pOptions = nullptr; @@ -345,31 +345,31 @@ public: pLocator = &vpkc_locator; } - if (0 != vpkc_new_update_manager(urlOrPath.c_str(), pOptions, pLocator, &m_pManager)) { + if (!vpkc_new_update_manager(urlOrPath.c_str(), pOptions, pLocator, &m_pManager)) { throw_last_error(); } }; ~UpdateManager() { - vpkc_free_update_manager(&m_pManager); + vpkc_free_update_manager(m_pManager); }; bool IsPortable() noexcept { - return vpkc_is_portable(&m_pManager); + return vpkc_is_portable(m_pManager); }; std::string GetCurrentVersion() noexcept { - size_t neededSize = vpkc_get_current_version(&m_pManager, nullptr, 0); + size_t neededSize = vpkc_get_current_version(m_pManager, nullptr, 0); std::string strVersion(neededSize, '\0'); - vpkc_get_current_version(&m_pManager, &strVersion[0], neededSize); + vpkc_get_current_version(m_pManager, &strVersion[0], neededSize); return strVersion; }; std::string GetAppId() noexcept { - size_t neededSize = vpkc_get_app_id(&m_pManager, nullptr, 0); + size_t neededSize = vpkc_get_app_id(m_pManager, nullptr, 0); std::string strId(neededSize, '\0'); - vpkc_get_app_id(&m_pManager, &strId[0], neededSize); + vpkc_get_app_id(m_pManager, &strId[0], neededSize); return strId; }; std::optional UpdatePendingRestart() noexcept { vpkc_asset_t asset; - if (vpkc_update_pending_restart(&m_pManager, &asset)) { + if (vpkc_update_pending_restart(m_pManager, &asset)) { VelopackAsset cpp_asset = to_cpp(asset); vpkc_free_asset(&asset); return cpp_asset; @@ -378,7 +378,7 @@ public: }; std::optional CheckForUpdates() { vpkc_update_info_t update; - vpkc_update_check_t result = vpkc_check_for_updates(&m_pManager, &update); + vpkc_update_check_t result = vpkc_check_for_updates(m_pManager, &update); switch (result) { case vpkc_update_check_t::UPDATE_ERROR: throw_last_error(); @@ -394,7 +394,7 @@ public: }; void DownloadUpdates(const UpdateInfo& update, vpkc_progress_callback_t progress = nullptr) { vpkc_update_info_t vpkc_update = to_c(update); - if (!vpkc_download_updates(&m_pManager, &vpkc_update, progress)) { + if (!vpkc_download_updates(m_pManager, &vpkc_update, progress)) { throw_last_error(); } }; @@ -406,7 +406,7 @@ public: } vpkc_asset_t vpkc_asset = to_c(asset); - bool result = vpkc_wait_exit_then_apply_update(&m_pManager, &vpkc_asset, silent, restart, pRestartArgs, restartArgs.size()); + bool result = vpkc_wait_exit_then_apply_update(m_pManager, &vpkc_asset, silent, restart, pRestartArgs, restartArgs.size()); // Free all the memory for (size_t i = 0; i < restartArgs.size(); i++) { diff --git a/src/lib-cpp/src/bridge.cc b/src/lib-cpp/src/bridge.cc index d7e6e675..5ffa207c 100644 --- a/src/lib-cpp/src/bridge.cc +++ b/src/lib-cpp/src/bridge.cc @@ -235,15 +235,20 @@ static inline void clear_last_error() { } // Update Manager -VPKC_EXPORT bool VPKC_CALL vpkc_new_update_manager(const char* pszUrlOrString, vpkc_update_options_t* pOptions, vpkc_locator_config_t* pLocator, vpkc_update_manager_t* pManager) { +VPKC_EXPORT bool VPKC_CALL vpkc_new_update_manager(const char* pszUrlOrString, vpkc_update_options_t* pOptions, vpkc_locator_config_t* pLocator, vpkc_update_manager_t** pManager) { clear_last_error(); try { + if (pManager == nullptr) { + set_last_error("pManager cannot be null"); + return false; + } + VelopackLocatorConfigDtoOption locator = to_bridge_opt(pLocator); UpdateOptionsDtoOption options = to_bridge_opt(pOptions); ::rust::Box<::UpdateManagerOpaque> manager = bridge_new_update_manager(pszUrlOrString, options, locator); UpdateManagerOpaque* pOpaque = manager.into_raw(); - *pManager = pOpaque; + *pManager = reinterpret_cast(pOpaque); return true; } catch (const std::exception& e) { set_last_error(e.what()); @@ -251,22 +256,22 @@ VPKC_EXPORT bool VPKC_CALL vpkc_new_update_manager(const char* pszUrlOrString, v } } VPKC_EXPORT size_t VPKC_CALL vpkc_get_current_version(vpkc_update_manager_t* pManager, char* pszVersion, size_t cVersion) { - UpdateManagerOpaque* pOpaque = reinterpret_cast(*pManager); + UpdateManagerOpaque* pOpaque = reinterpret_cast(pManager); std::string version = (std::string)bridge_get_current_version(*pOpaque); return return_c_string(version, pszVersion, cVersion); } VPKC_EXPORT size_t VPKC_CALL vpkc_get_app_id(vpkc_update_manager_t* pManager, char* pszId, size_t cId) { - UpdateManagerOpaque* pOpaque = reinterpret_cast(*pManager); + UpdateManagerOpaque* pOpaque = reinterpret_cast(pManager); std::string id = (std::string)bridge_get_app_id(*pOpaque); return return_c_string(id, pszId, cId); } VPKC_EXPORT bool VPKC_CALL vpkc_is_portable(vpkc_update_manager_t* pManager) { - UpdateManagerOpaque* pOpaque = reinterpret_cast(*pManager); + UpdateManagerOpaque* pOpaque = reinterpret_cast(pManager); return bridge_is_portable(*pOpaque); } VPKC_EXPORT bool VPKC_CALL vpkc_update_pending_restart(vpkc_update_manager_t* pManager, vpkc_asset_t* pAsset) { - UpdateManagerOpaque* pOpaque = reinterpret_cast(*pManager); + UpdateManagerOpaque* pOpaque = reinterpret_cast(pManager); VelopackAssetDtoOption asset = bridge_update_pending_restart(*pOpaque); if (asset.has_data) { allocate_velopackasset(asset.data, pAsset); @@ -277,7 +282,7 @@ VPKC_EXPORT bool VPKC_CALL vpkc_update_pending_restart(vpkc_update_manager_t* pM VPKC_EXPORT vpkc_update_check_t VPKC_CALL vpkc_check_for_updates(vpkc_update_manager_t* pManager, vpkc_update_info_t* pUpdate) { clear_last_error(); try { - UpdateManagerOpaque* pOpaque = reinterpret_cast(*pManager); + UpdateManagerOpaque* pOpaque = reinterpret_cast(pManager); UpdateInfoDtoOption update = bridge_check_for_updates(*pOpaque); if (update.has_data) { allocate_updateinfo(update.data, pUpdate); @@ -298,7 +303,7 @@ VPKC_EXPORT bool VPKC_CALL vpkc_download_updates(vpkc_update_manager_t* pManager return false; } - UpdateManagerOpaque* pOpaque = reinterpret_cast(*pManager); + UpdateManagerOpaque* pOpaque = reinterpret_cast(pManager); UpdateInfoDto update = to_bridge(pUpdate); DownloadCallbackManager download{}; @@ -319,7 +324,7 @@ VPKC_EXPORT bool VPKC_CALL vpkc_wait_exit_then_apply_update(vpkc_update_manager_ return false; } - UpdateManagerOpaque* pOpaque = reinterpret_cast(*pManager); + UpdateManagerOpaque* pOpaque = reinterpret_cast(pManager); VelopackAssetDto asset = to_bridge(pAsset); ::rust::Vec<::rust::String> restartArgs{}; @@ -384,7 +389,7 @@ VPKC_EXPORT void VPKC_CALL vpkc_set_log(vpkc_log_callback_t cbLog) { bridge_set_logger_callback(&logMgr); } VPKC_EXPORT void VPKC_CALL vpkc_free_update_manager(vpkc_update_manager_t* pManager) { - UpdateManagerOpaque* pOpaque = reinterpret_cast(*pManager); + UpdateManagerOpaque* pOpaque = reinterpret_cast(pManager); auto box = ::rust::Box<::UpdateManagerOpaque>::from_raw(pOpaque); // this will free when the box goes out of scope } diff --git a/src/lib-cpp/src/lib.rs b/src/lib-cpp/src/lib.rs index b565afdb..42930a7a 100644 --- a/src/lib-cpp/src/lib.rs +++ b/src/lib-cpp/src/lib.rs @@ -144,6 +144,19 @@ struct UpdateManagerOpaque { obj: UpdateManager, } +impl UpdateManagerOpaque { + fn new(obj: UpdateManager) -> Self { + log::debug!("UpdateManagerOpaque allocated"); + UpdateManagerOpaque { obj } + } +} + +impl Drop for UpdateManagerOpaque { + fn drop(&mut self) { + log::debug!("UpdateManagerOpaque dropped"); + } +} + fn bridge_new_update_manager( url_or_path: &String, options: &ffi::UpdateOptionsDtoOption, @@ -153,7 +166,7 @@ fn bridge_new_update_manager( let options = updateoptions_to_core_option(options); let locator = velopacklocatorconfig_to_core_option(locator); let update_manager = UpdateManager::new(source, options, locator)?; - Ok(Box::new(UpdateManagerOpaque { obj: update_manager })) + Ok(Box::new(UpdateManagerOpaque::new(update_manager))) } fn bridge_get_current_version(manager: &UpdateManagerOpaque) -> String {