diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 77747cd07e..64f4b92749 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -232,6 +232,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/FirmwareDialog.hpp ${LIBDIR}/slic3r/Utils/Http.cpp ${LIBDIR}/slic3r/Utils/Http.hpp + ${LIBDIR}/slic3r/Utils/FixModelByWin10.cpp + ${LIBDIR}/slic3r/Utils/FixModelByWin10.hpp ${LIBDIR}/slic3r/Utils/OctoPrint.cpp ${LIBDIR}/slic3r/Utils/OctoPrint.hpp ${LIBDIR}/slic3r/Utils/Bonjour.cpp diff --git a/xs/src/slic3r/Utils/FixModelByWin10.cpp b/xs/src/slic3r/Utils/FixModelByWin10.cpp new file mode 100644 index 0000000000..e4b1952a4f --- /dev/null +++ b/xs/src/slic3r/Utils/FixModelByWin10.cpp @@ -0,0 +1,287 @@ +#ifdef HAS_WIN10SDK + +#include "FixModelByWin10.hpp" + +#include +#include +#include +// for ComPtr +#include +// from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/ +#include +#include +#include + +extern "C"{ + // from rapi.h + typedef HRESULT (__stdcall* FunctionRoInitialize)(int); + typedef HRESULT (__stdcall* FunctionRoUninitialize)(); + typedef HRESULT (__stdcall* FunctionRoActivateInstance)(HSTRING activatableClassId, IInspectable **instance); + typedef HRESULT (__stdcall* FunctionRoGetActivationFactory)(HSTRING activatableClassId, REFIID iid, void **factory); + // from winstring.h + typedef HRESULT (__stdcall* FunctionWindowsCreateString)(LPCWSTR sourceString, UINT32 length, HSTRING *string); + typedef HRESULT (__stdcall* FunctionWindowsDelteString)(HSTRING string); +} + +HMODULE s_hRuntimeObjectLibrary = nullptr; +FunctionRoInitialize s_RoInitialize = nullptr; +FunctionRoUninitialize s_RoUninitialize = nullptr; +FunctionRoActivateInstance s_RoActivateInstance = nullptr; +FunctionRoGetActivationFactory s_RoGetActivationFactory = nullptr; +FunctionWindowsCreateString s_WindowsCreateString = nullptr; +FunctionWindowsDelteString s_WindowsDeleteString = nullptr; + +bool winrt_load_runtime_object_library() +{ + if (s_hRuntimeObjectLibrary == nullptr) + s_hRuntimeObjectLibrary = LoadLibrary(L"ComBase.dll"); + if (s_hRuntimeObjectLibrary != nullptr) { + s_RoInitialize = (FunctionRoInitialize) GetProcAddress(s_hRuntimeObjectLibrary, "RoInitialize"); + s_RoUninitialize = (FunctionRoUninitialize) GetProcAddress(s_hRuntimeObjectLibrary, "RoUninitialize"); + s_RoActivateInstance = (FunctionRoActivateInstance) GetProcAddress(s_hRuntimeObjectLibrary, "RoActivateInstance"); + s_RoGetActivationFactory = (FunctionRoGetActivationFactory) GetProcAddress(s_hRuntimeObjectLibrary, "RoGetActivationFactory"); + s_WindowsCreateString = (FunctionWindowsCreateString) GetProcAddress(s_hRuntimeObjectLibrary, "WindowsCreateString"); + s_WindowsDeleteString = (FunctionWindowsDelteString) GetProcAddress(s_hRuntimeObjectLibrary, "WindowsDeleteString"); + } + return s_RoInitialize && s_RoUninitialize && s_RoActivateInstance && s_WindowsCreateString && s_WindowsDeleteString; +} + +static HRESULT winrt_activate_instance(const std::wstring &class_name, IInspectable **pinst) +{ + HSTRING hClassName; + HRESULT hr = (*s_WindowsCreateString)(class_name.c_str(), class_name.size(), &hClassName); + if (S_OK != hr) + return hr; + hr = (*s_RoActivateInstance)(hClassName, pinst); + (*s_WindowsDeleteString)(hClassName); + return hr; +} + +template +static HRESULT winrt_activate_instance(const std::wstring &class_name, TYPE **pinst) +{ + IInspectable *pinspectable = nullptr; + HRESULT hr = winrt_activate_instance(class_name, &pinspectable); + if (S_OK != hr) + return hr; + hr = pinspectable->QueryInterface(__uuidof(TYPE), (void**)pinst); + pinspectable->Release(); + return hr; +} + +static HRESULT winrt_get_activation_factory(const std::wstring &class_name, REFIID iid, void **pinst) +{ + HSTRING hClassName; + HRESULT hr = (*s_WindowsCreateString)(class_name.c_str(), class_name.size(), &hClassName); + if (S_OK != hr) + return hr; + hr = (*s_RoGetActivationFactory)(hClassName, iid, pinst); + (*s_WindowsDeleteString)(hClassName); + return hr; +} + +template +static HRESULT winrt_get_activation_factory(const std::wstring &class_name, TYPE **pinst) +{ + return winrt_get_activation_factory(class_name, __uuidof(TYPE), reinterpret_cast(pinst)); +} + +template +static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr &asyncAction, int blocking_tick_ms = 300) +{ + Microsoft::WRL::ComPtr asyncInfo; + asyncAction.As(&asyncInfo); + AsyncStatus status; + // Ugly blocking loop until the RepairAsync call finishes. +//FIXME replace with a callback. +// https://social.msdn.microsoft.com/Forums/en-US/a5038fb4-b7b7-4504-969d-c102faa389fb/trying-to-block-an-async-operation-and-wait-for-a-particular-time?forum=vclanguage + for (;;) { + asyncInfo->get_Status(&status); + if (status != AsyncStatus::Started) + return status; + ::Sleep(blocking_tick_ms); + } +} + +static HRESULT winrt_open_file_stream( + const std::wstring &path, + ABI::Windows::Storage::FileAccessMode mode, + ABI::Windows::Storage::Streams::IRandomAccessStream **fileStream) +{ + // Get the file factory. + Microsoft::WRL::ComPtr fileFactory; + HRESULT hr = winrt_get_activation_factory(L"Windows.Storage.StorageFile", fileFactory.GetAddressOf()); + if (FAILED(hr)) return hr; + + // Open the file asynchronously. + HSTRING hstr_path; + hr = (*s_WindowsCreateString)(path.c_str(), path.size(), &hstr_path); + if (FAILED(hr)) return hr; + Microsoft::WRL::ComPtr> fileOpenAsync; + hr = fileFactory->GetFileFromPathAsync(hstr_path, fileOpenAsync.GetAddressOf()); + if (FAILED(hr)) return hr; + (*s_WindowsDeleteString)(hstr_path); + + // Wait until the file gets open, get the actual file. + AsyncStatus status = winrt_async_await(fileOpenAsync); + Microsoft::WRL::ComPtr storageFile; + if (status == AsyncStatus::Completed) { + hr = fileOpenAsync->GetResults(storageFile.GetAddressOf()); + } else { + Microsoft::WRL::ComPtr asyncInfo; + hr = fileOpenAsync.As(&asyncInfo); + if (FAILED(hr)) return hr; + HRESULT err; + hr = asyncInfo->get_ErrorCode(&err); + return FAILED(hr) ? hr : err; + } + + Microsoft::WRL::ComPtr> fileStreamAsync; + hr = storageFile->OpenAsync(mode, fileStreamAsync.GetAddressOf()); + if (FAILED(hr)) return hr; + + status = winrt_async_await(fileStreamAsync); + if (status == AsyncStatus::Completed) { + hr = fileStreamAsync->GetResults(fileStream); + } else { + Microsoft::WRL::ComPtr asyncInfo; + hr = fileStreamAsync.As(&asyncInfo); + if (FAILED(hr)) return hr; + HRESULT err; + hr = asyncInfo->get_ErrorCode(&err); + if (!FAILED(hr)) + hr = err; + } + return hr; +} + +bool is_windows10() +{ + HKEY hKey; + LONG lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey); + if (lRes == ERROR_SUCCESS) { + WCHAR szBuffer[512]; + DWORD dwBufferSize = sizeof(szBuffer); + lRes = RegQueryValueExW(hKey, L"ProductName", 0, nullptr, (LPBYTE)szBuffer, &dwBufferSize); + if (lRes == ERROR_SUCCESS) + return wcsncmp(szBuffer, L"Windows 10", 10) == 0; + RegCloseKey(hKey); + } + return false; +} + +bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst) +{ + if (! is_windows10()) { + return false; + } + + if (! winrt_load_runtime_object_library()) { + printf("Failed to initialize the WinRT library. This should not happen on Windows 10. Exiting.\n"); + return -1; + } + + (*s_RoInitialize)(RO_INIT_MULTITHREADED); + { + Microsoft::WRL::ComPtr fileStream; + HRESULT hr = winrt_open_file_stream(L"D:\\3dprint\\bug.3mf", ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf()); + + Microsoft::WRL::ComPtr printing3d3mfpackage; + hr = winrt_activate_instance(L"Windows.Graphics.Printing3D.Printing3D3MFPackage", printing3d3mfpackage.GetAddressOf()); + + Microsoft::WRL::ComPtr> modelAsync; + hr = printing3d3mfpackage->LoadModelFromPackageAsync(fileStream.Get(), modelAsync.GetAddressOf()); + + AsyncStatus status = winrt_async_await(modelAsync); + Microsoft::WRL::ComPtr model; + if (status == AsyncStatus::Completed) { + hr = modelAsync->GetResults(model.GetAddressOf()); + } else { + printf("Failed loading the input model. Exiting.\n"); + return -1; + } + + Microsoft::WRL::ComPtr> meshes; + hr = model->get_Meshes(meshes.GetAddressOf()); + unsigned num_meshes = 0; + hr = meshes->get_Size(&num_meshes); + + Microsoft::WRL::ComPtr repairAsync; + hr = model->RepairAsync(repairAsync.GetAddressOf()); + status = winrt_async_await(repairAsync); + if (status != AsyncStatus::Completed) { + printf("Mesh repair failed. Exiting.\n"); + return -1; + } + printf("Mesh repair finished successfully.\n"); + repairAsync->GetResults(); + + // Verify the number of meshes returned after the repair action. + meshes.Reset(); + hr = model->get_Meshes(meshes.GetAddressOf()); + hr = meshes->get_Size(&num_meshes); + + // Save model to this class' Printing3D3MFPackage. + Microsoft::WRL::ComPtr saveToPackageAsync; + hr = printing3d3mfpackage->SaveModelToPackageAsync(model.Get(), saveToPackageAsync.GetAddressOf()); + status = winrt_async_await(saveToPackageAsync); + if (status != AsyncStatus::Completed) { + printf("Saving mesh into the 3MF container failed. Exiting.\n"); + return -1; + } + hr = saveToPackageAsync->GetResults(); + + Microsoft::WRL::ComPtr> generatorStreamAsync; + hr = printing3d3mfpackage->SaveAsync(generatorStreamAsync.GetAddressOf()); + status = winrt_async_await(generatorStreamAsync); + if (status != AsyncStatus::Completed) { + printf("Saving mesh into the 3MF container failed. Exiting.\n"); + return -1; + } + Microsoft::WRL::ComPtr generatorStream; + hr = generatorStreamAsync->GetResults(generatorStream.GetAddressOf()); + + // Go to the beginning of the stream. + generatorStream->Seek(0); + Microsoft::WRL::ComPtr inputStream; + hr = generatorStream.As(&inputStream); + + // Get the buffer factory. + Microsoft::WRL::ComPtr bufferFactory; + hr = winrt_get_activation_factory(L"Windows.Storage.Streams.Buffer", bufferFactory.GetAddressOf()); + + // Open the destination file. + FILE *fout = ::fopen("D:\\3dprint\\bug-repaired.3mf", "wb"); + + Microsoft::WRL::ComPtr buffer; + byte *buffer_ptr; + bufferFactory->Create(65536 * 2048, buffer.GetAddressOf()); + { + Microsoft::WRL::ComPtr bufferByteAccess; + buffer.As(&bufferByteAccess); + hr = bufferByteAccess->Buffer(&buffer_ptr); + } + uint32_t length; + hr = buffer->get_Length(&length); + + Microsoft::WRL::ComPtr> asyncRead; + for (;;) { + hr = inputStream->ReadAsync(buffer.Get(), 65536 * 2048, ABI::Windows::Storage::Streams::InputStreamOptions_ReadAhead, asyncRead.GetAddressOf()); + status = winrt_async_await(asyncRead); + if (status != AsyncStatus::Completed) { + printf("Saving mesh into the 3MF container failed. Exiting.\n"); + return -1; + } + hr = buffer->get_Length(&length); + if (length == 0) + break; + fwrite(buffer_ptr, length, 1, fout); + } + fclose(fout); + // Here all the COM objects will be released through the ComPtr destructors. + } + (*s_RoUninitialize)(); + return 0; +} + +#endif /* HAS_WIN10SDK */ diff --git a/xs/src/slic3r/Utils/FixModelByWin10.hpp b/xs/src/slic3r/Utils/FixModelByWin10.hpp new file mode 100644 index 0000000000..268b614efc --- /dev/null +++ b/xs/src/slic3r/Utils/FixModelByWin10.hpp @@ -0,0 +1,18 @@ +#ifndef slic3r_GUI_Utils_FixModelByWin10_hpp_ +#define slic3r_GUI_Utils_FixModelByWin10_hpp_ + +#include + +#ifdef HAS_WIN10SDK + +extern bool is_windows10(); +extern bool fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst); + +#else /* HAS_WIN10SDK */ + +inline bool is_windows10() { return false; } +inline bool fix_model_by_win10_sdk(const std::string &, const std::string &) { return false; } + +#endif /* HAS_WIN10SDK * + +#endif /* slic3r_GUI_Utils_FixModelByWin10_hpp_ */