diff --git a/MsixCore/msixmgr/CommandLineInterface.cpp b/MsixCore/msixmgr/CommandLineInterface.cpp index 9fa4ea36e..e9c1bb134 100644 --- a/MsixCore/msixmgr/CommandLineInterface.cpp +++ b/MsixCore/msixmgr/CommandLineInterface.cpp @@ -175,6 +175,19 @@ std::map CommandLineInterface::s_opt return S_OK; }), }, + { + L"-output", + Option(true, IDS_STRING_HELP_OPTION_UNPACK_OUTPUT, + [&](CommandLineInterface* commandLineInterface, const std::string& outPath) + { + if (commandLineInterface->m_operationType != OperationType::Unpack) + { + return E_INVALIDARG; + } + commandLineInterface->m_unpackOutputLoggingPath = utf8_to_utf16(outPath); + return S_OK; + }), + }, { L"-validateSignature", Option(false, IDS_STRING_HELP_OPTION_UNPACK_VALIDATESIGNATURE, diff --git a/MsixCore/msixmgr/CommandLineInterface.hpp b/MsixCore/msixmgr/CommandLineInterface.hpp index 3d212dc8a..8cb7f4beb 100644 --- a/MsixCore/msixmgr/CommandLineInterface.hpp +++ b/MsixCore/msixmgr/CommandLineInterface.hpp @@ -108,6 +108,7 @@ class CommandLineInterface OperationType GetOperationType() { return m_operationType; } std::wstring GetOperationTypeAsString(); ULONGLONG GetVHDSize() { return m_vhdSize; } + std::wstring GetOutputPath() { return m_unpackOutputLoggingPath; } private: int m_argc = 0; char ** m_argv = nullptr; @@ -118,6 +119,7 @@ class CommandLineInterface std::wstring m_packageFilePath; std::wstring m_packageFullName; std::wstring m_unpackDestination; + std::wstring m_unpackOutputLoggingPath; std::wstring m_rootDirectory; std::wstring m_mountImagePath; std::wstring m_volumeId; diff --git a/MsixCore/msixmgr/msixmgr.cpp b/MsixCore/msixmgr/msixmgr.cpp index ba353bfbf..183cf504c 100644 --- a/MsixCore/msixmgr/msixmgr.cpp +++ b/MsixCore/msixmgr/msixmgr.cpp @@ -17,6 +17,7 @@ #include "Util.hpp" #include "..\msixmgrLib\GeneralUtil.hpp" #include "resource.h" +#include #include #include "UnpackProvider.hpp" #include "ApplyACLsProvider.hpp" @@ -124,55 +125,81 @@ void RelaunchAsAdmin(int argc, char * argv[]) ShellExecuteExW(&shellExecuteInfo); } -void OutputUnpackFailures( +void GetUnpackOutputFailures( _In_ std::wstring packageSource, _In_ std::vector skippedFiles, _In_ std::vector failedPackages, - _In_ std::vector failedPackagesErrors) + _In_ std::vector failedPackagesErrors, + _In_ std::wstringstream& unpackFailureStringStream) { if (!skippedFiles.empty()) { - std::wcout << std::endl; - std::wcout << "[WARNING] The following items from " << packageSource << " were ignored because they are not packages or bundles " << std::endl; - std::wcout << std::endl; + unpackFailureStringStream << std::endl; + unpackFailureStringStream << "[WARNING] The following items from " << packageSource << " were ignored because they are not packages or bundles " << std::endl; + unpackFailureStringStream << std::endl; for (int i = 0; i < skippedFiles.size(); i++) { - std::wcout << skippedFiles.at(i) << std::endl; + unpackFailureStringStream << skippedFiles.at(i) << std::endl; } - std::wcout << std::endl; + unpackFailureStringStream << std::endl; } if (!failedPackages.empty()) { - std::wcout << std::endl; - std::wcout << "[WARNING] The following packages from " << packageSource << " failed to get unpacked. Please try again: " << std::endl; - std::wcout << std::endl; + unpackFailureStringStream << std::endl; + unpackFailureStringStream << "[WARNING] The following packages from " << packageSource << " failed to get unpacked. Please try again: " << std::endl; + unpackFailureStringStream << std::endl; for (int i = 0; i < failedPackages.size(); i++) { HRESULT hr = failedPackagesErrors.at(i); - std::wcout << L"Failed with HRESULT 0x" << std::hex << hr << L" when trying to unpack " << failedPackages.at(i) << std::endl; + unpackFailureStringStream << L"Failed with HRESULT 0x" << std::hex << hr << L" when trying to unpack " << failedPackages.at(i) << std::endl; if (hr == static_cast(MSIX::Error::CertNotTrusted)) { - std::wcout << L"Please confirm that the certificate has been installed for this package" << std::endl; + unpackFailureStringStream << L"Please confirm that the certificate has been installed for this package" << std::endl; } else if (hr == static_cast(MSIX::Error::FileWrite)) { - std::wcout << L"The tool encountered a file write error. If you are unpacking to a VHD, please try again with a larger VHD, as file write errors may be caused by insufficient disk space." << std::endl; + unpackFailureStringStream << L"The tool encountered a file write error. If you are unpacking to a VHD, please try again with a larger VHD, as file write errors may be caused by insufficient disk space." << std::endl; } else if (hr == E_INVALIDARG) { - std::wcout << "Please confirm the given package path is an .appx, .appxbundle, .msix, or .msixbundle file" << std::endl; + unpackFailureStringStream << "Please confirm the given package path is an .appx, .appxbundle, .msix, or .msixbundle file" << std::endl; } - std::wcout << std::endl; + unpackFailureStringStream << std::endl; } } } +void OutputUnpackFailures( + _In_ std::wstring packageSource, + _In_ std::vector skippedFiles, + _In_ std::vector failedPackages, + _In_ std::vector failedPackagesErrors, + _In_ std::wstring outfilePath) +{ + std::wofstream outfile; + std::wstringstream unpackFailureStringStream; + GetUnpackOutputFailures(packageSource, skippedFiles, failedPackages, failedPackagesErrors, unpackFailureStringStream); + std::wstring unpackFailureString = unpackFailureStringStream.str(); + if (!outfilePath.empty()) + { + std::wofstream outFile; + outFile.open(outfilePath); + outFile << unpackFailureString << std::endl; + outFile.close(); + wcout << "Wrote error output to file: " << outfilePath; + } + else + { + std::wcout << unpackFailureString << std::endl; + } +} + int main(int argc, char * argv[]) { // Register the providers @@ -478,7 +505,7 @@ int main(int argc, char * argv[]) // Telemetry : Unpack Workflow Log msixmgrTraceLogging::TraceLogUnpackWorkflow(workflowId.c_str(), msixmgrTraceLogging::ExtractPackageNameFromFilePath(cli.GetPackageFilePathToInstall()).c_str(), cli.GetFileTypeAsString().c_str(), cli.GetVHDSize(), cli.IsCreate(), cli.IsApplyACLs()); - + auto outputFilePath = cli.GetOutputPath(); auto packageSourcePath = cli.GetPackageFilePathToInstall(); auto unpackDestination = cli.GetUnpackDestination(); auto rootDirectory = cli.GetRootDirectory(); @@ -645,7 +672,7 @@ int main(int argc, char * argv[]) std::wcout << "Successfully created the CIM file: " << unpackDestination << std::endl; std::wcout << std::endl; - OutputUnpackFailures(packageSourcePath, skippedFiles, failedPackages, failedPackagesErrors); + OutputUnpackFailures(packageSourcePath, skippedFiles, failedPackages, failedPackagesErrors, outputFilePath); // Telemetry : Workflow Log QueryPerformanceCounter(&msixMgrLoad_EndCounter); @@ -761,7 +788,8 @@ int main(int argc, char * argv[]) std::wcout << std::endl; } - OutputUnpackFailures(packageSourcePath, skippedFiles, failedPackages, failedPackagesErrors); + OutputUnpackFailures(packageSourcePath, skippedFiles, failedPackages, failedPackagesErrors, outputFilePath); + std::wcout << std::endl; std::wcout << "Finished unpacking packages to: " << unpackDestination << std::endl; @@ -801,7 +829,7 @@ int main(int argc, char * argv[]) std::wcout << "Finished unpacking packages to: " << unpackDestination << std::endl; std::wcout << std::endl; - OutputUnpackFailures(packageSourcePath, skippedFiles, failedPackages, failedPackagesErrors); + OutputUnpackFailures(packageSourcePath, skippedFiles, failedPackages, failedPackagesErrors, outputFilePath); // Telemetry : Workflow Log QueryPerformanceCounter(&msixMgrLoad_EndCounter); diff --git a/MsixCore/msixmgr/msixmgr.rc b/MsixCore/msixmgr/msixmgr.rc index 659095679..7933941c7 100644 --- a/MsixCore/msixmgr/msixmgr.rc +++ b/MsixCore/msixmgr/msixmgr.rc @@ -134,7 +134,7 @@ BEGIN IDS_STRING_HELP_OPTION_MOUNT_FILETYPE "the type of file to mount or unmount. The following file types are currently supported: {VHD, VHDX, CIM}" IDS_STRING_HELP_OPTION_UNPACK_VHDSIZE "the desired size of the VHD or VHDX file in MB. Must be between 5 and 2040000 MB. Use only for VHD or VHDX files" IDS_STRING_HELP_OPTION_MOUNT_READONLY "boolean (true of false) indicating whether a VHD(X) should be mounted as read only. If not specified, the image is mounted as read-only by default" - + IDS_STRING_HELP_OPTION_UNPACK_OUTPUT "optional parameter selects logging location to write the command line output to" END #endif // English (United States) resources diff --git a/MsixCore/msixmgr/resource.h b/MsixCore/msixmgr/resource.h index b81409bb7..8dd967d01 100644 --- a/MsixCore/msixmgr/resource.h +++ b/MsixCore/msixmgr/resource.h @@ -66,7 +66,7 @@ #define IDS_STRING_HELP_OPTION_MOUNT_FILETYPE 161 #define IDS_STRING_HELP_OPTION_UNPACK_VHDSIZE 162 #define IDS_STRING_HELP_OPTION_MOUNT_READONLY 163 - +#define IDS_STRING_HELP_OPTION_UNPACK_OUTPUT 164 // Next default values for new objects // #ifdef APSTUDIO_INVOKED diff --git a/MsixCore/msixmgrLib/resourceConfig.rcconfig.xml b/MsixCore/msixmgrLib/resourceConfig.rcconfig.xml index d0cef6fb4..9c9befa55 100644 --- a/MsixCore/msixmgrLib/resourceConfig.rcconfig.xml +++ b/MsixCore/msixmgrLib/resourceConfig.rcconfig.xml @@ -3,6 +3,7 @@ +