Convert Win32 BITMAP into WinUI3 C++/WinRT ImageSource - xaml

I want to display files in a custom list view, I did all I needed besides displaying the icon for every file extension in the list view.
I found out how to take the HICON/BITMAP for the file extension, but I need to display it in the custom list view like an Image or something like that.
How can I achieve that?
I am working with WinUI3 C++/WinRT, up to date packages.

With the help of IInspectable's comment I was able to find the answer for my own question, many thanks to him.
You will need these headers:
#include <winrt/Windows.Storage.h>
#include <winrt/Microsoft.Ui.Xaml.Media.Imaging.h>
This is the async method:
::winrt::Windows::Foundation::IAsyncOperation<::winrt::Microsoft::UI::Xaml::Media::ImageSource>
GetIconFromFile(
const ::winrt::hstring& file,
::winrt::Windows::Storage::FileProperties::ThumbnailMode thumbnailMode = ::winrt::Windows::Storage::FileProperties::ThumbnailMode::DocumentsView
) {
const auto& fileStorage = co_await ::winrt::Windows::Storage::StorageFile::GetFileFromPathAsync(file);
const auto& fileThumbnail = co_await fileStorage.GetThumbnailAsync(thumbnailMode);
::winrt::Microsoft::UI::Xaml::Media::Imaging::BitmapImage bitmapImage;
bitmapImage.SetSource(fileThumbnail);
co_return bitmapImage;
}
You can get rid of the namespaces by using them, but I decided not to do that because ambiguous errors could appear if you have things included from Windows namespace.
And you can use it like this:
// xaml
<Image x:Name="image" Width="40" Height="40"/>
//cpp
::winrt::Windows::Foundation::IAsyncAction LoadImageIcon() {
const auto& filePath = TEXT("<your file path>");
image().Source(co_await GetIconFromFile(filePath));
}

Here is a generic function that does that an HBITMAP => WinUI3 Image.Source conversion:
#pragma comment(lib, "gdi32")
#pragma comment(lib, "oleaut32")
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams; // for InMemoryRandomAccessStream
using namespace Windows::Graphics::Imaging; // for BitmapDecoder
using namespace Microsoft::UI::Xaml;
using namespace Microsoft::UI::Xaml::Controls; // for Image
using namespace Microsoft::UI::Xaml::Media::Imaging; // for SoftwareBitmapSource
IAsyncAction SetImageSourceFromHBITMAPAsync(HBITMAP hBitmap, Image image)
{
PICTDESC pd = {};
pd.cbSizeofstruct = sizeof(PICTDESC);
pd.picType = PICTYPE_BITMAP;
pd.bmp.hbitmap = hBitmap;
com_ptr<IPicture> picture;
if (FAILED(OleCreatePictureIndirect(&pd, IID_IPicture, FALSE, (LPVOID*)picture.put()))) // #include <olectl.h> in pch.h
return;
InMemoryRandomAccessStream memStream; // #include <winrt/Windows.Storage.Streams.h> in pch.h
com_ptr<IStream> stream;
if (FAILED(CreateStreamOverRandomAccessStream(winrt::get_unknown(memStream), IID_PPV_ARGS(&stream)))) // #include <shcore.h> in pch.h
return;
if (FAILED(picture->SaveAsFile(stream.get(), TRUE, nullptr)))
return;
auto decoder = co_await BitmapDecoder::CreateAsync(memStream); // #include <winrt/Windows.Graphics.Imaging.h> in pch.h
auto bitmap = co_await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat::Bgra8, BitmapAlphaMode::Premultiplied);
SoftwareBitmapSource bitmapSource; // #include <winrt/Microsoft.UI.Xaml.Media.Imaging.h> in pch.h
co_await bitmapSource.SetBitmapAsync(bitmap);
image.Source(bitmapSource);
}
And an example on how to use it:
namespace winrt::WinUIApp1CPP::implementation
{
MainWindow::MainWindow()
{
InitializeComponent();
}
void MainWindow::myButton_Click(IInspectable const&, RoutedEventArgs const&)
{
auto hBitmap = (HBITMAP)LoadImage(nullptr, L"D:\\Downloads\\dog.bmp", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE);
if (!hBitmap)
return;
auto op{ SetImageSourceFromHBITMAPAsync(hBitmap, myImage()) }; // add this XAML somewhere <Image x:Name="myImage" />
DeleteObject(hBitmap);
}
}

Related

C++/WinRT handling exceptions from StorageFolder::GetFileAsync

When no file exists the app throws an exception which I am unable to catch
In my example code below I create a directory "MyFolder" and save a file "" and save a file "sample.txt" to it.
I then call the function FindFileAsync twice - first using the already created file "sample.txt" which works fine and then using a non-existing file "nofile.txt" which fails with the error
Exception thrown at 0x00007FF94E39D759 in FindFile.exe: Microsoft C++ exception: winrt::hresult_error at memory location.
Is there any solution to this issue.
MainPage.h
#pragma once
#include "MainPage.g.h"
namespace winrt::FindFile::implementation
{
struct MainPage : MainPageT<MainPage>
{
MainPage();
Windows::Foundation::IAsyncAction FindFileAsync(hstring value);
Windows::Foundation::IAsyncAction GetFolderAsync(hstring const& value);
Windows::Foundation::IAsyncAction CreateFileAsync(hstring const& fname);
Windows::Foundation::IAsyncAction DeletefileAsync(Windows::Storage::StorageFile const & value);
void ClickHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args);
private:
hstring folderDir{ L"MyFolder" };
};
}
namespace winrt::FindFile::factory_implementation
{
struct MainPage : MainPageT<MainPage, implementation::MainPage>
{
};
}
MainPage.cpp
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::UI::Xaml;
using namespace Windows::Storage;
using namespace Windows::Storage::Streams;
using namespace Windows::UI::Popups;
namespace winrt::FindFile::implementation
{
MainPage::MainPage()
{
InitializeComponent();
}
Windows::Foundation::IAsyncAction MainPage::FindFileAsync(hstring value)
{
Windows::Storage::StorageFolder storageFolder{ Windows::Storage::ApplicationData::Current().LocalFolder() };
StorageFolder sampleFolder{ co_await storageFolder.CreateFolderAsync(folderDir, CreationCollisionOption::OpenIfExists) };
try
{
Windows::Storage::StorageFile manifest{ co_await sampleFolder.GetFileAsync(value) };
}
catch (winrt::hresult_error msg)
{
hstring ms{ msg.message() };
MessageDialog dlg(ms, L"File Error");
dlg.ShowAsync();
}
}
Windows::Foundation::IAsyncAction MainPage::GetFolderAsync(hstring const& value)
{
Windows::Storage::StorageFolder storageFolder{ Windows::Storage::ApplicationData::Current().LocalFolder() };
auto sampleFolder{ co_await storageFolder.CreateFolderAsync(value, CreationCollisionOption::OpenIfExists) };
}
IAsyncAction MainPage::CreateFileAsync(hstring const& fname)
{
Windows::Storage::StorageFolder storageFolder{ Windows::Storage::ApplicationData::Current().LocalFolder() };
auto sampleFolder{ co_await storageFolder.CreateFolderAsync(folderDir, CreationCollisionOption::OpenIfExists) };
auto sampleFile{ co_await sampleFolder.CreateFileAsync(L"sample.txt", Windows::Storage::CreationCollisionOption::ReplaceExisting) };
co_await Windows::Storage::FileIO::WriteTextAsync(sampleFile, L"Swift as a shadow");
}
IAsyncAction MainPage::DeletefileAsync(StorageFile const& value)
{
co_await value.DeleteAsync();
}
void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
hstring fname{ L"sample.txt" };
GetFolderAsync(folderDir);
CreateFileAsync(fname);
// The following statement is successful as the file exists
FindFileAsync(fname);
// The following ststement throws an exception error
FindFileAsync(L"nofile.txt");
}
}
In C++/WinRT, some exceptions, but not all, force the debugger to pause program execution despite the exception being handled (thrown inside a try block). However, you can resume it by pressing F5 or launching the app without a debugger (CTRL+F5 or via the start menu). I'm not sure whenever this is a bug or intended behavior.
I have found a solution that worked for me.
When the debugger triggers the exception the message includes a checked checkbox. If unchecked and the program is continued to the end the problem is resolved. When run again the error is not triggered and the catch can handle the "file not found". Not sure why the default setting is to trigger the exception.

Is it possible to programatically create a xaml page?

I might write no sense on the next lines:
I tried to derive a struct named mainpage from winrt::Windows::UI::Xaml::Controls::Page and pass it to winrt::xaml_typename<>(), in "<>" is mainpage.
It will be understandable when you look at the code:
Point number 1 "(1)" is the mainpage.h file, is very short.
Point number 2 "(2)" is the App.cpp file, it only has the necessary.
//(1) mainpage.h
#pragma once
#include "pch.h"
struct mainpage : winrt::Windows::UI::Xaml::Controls::Page
{
mainpage() {
winrt::Windows::UI::Xaml::Controls::Button thebutton = winrt::Windows::UI::Xaml::Controls::Button();
thebutton.Click([&](const IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& event_arg)
{
thebutton.Content(winrt::box_value(L"Clicked"));
});
}
};
//(2) App.cpp
#include "pch.h"
#include "mainpage.h"
struct App : winrt::Windows::UI::Xaml::ApplicationT<App>
{
void OnLaunched(const winrt::Windows::ApplicationModel::Activation::LaunchActivatedEventArgs& event_arg)
{
winrt::Windows::UI::Xaml::Controls::Frame root_frame{ nullptr };
winrt::Windows::UI::Xaml::UIElement content = winrt::Windows::UI::Xaml::Window::Current().Content();
if (content)
{
root_frame = content.try_as<winrt::Windows::UI::Xaml::Controls::Frame>();
}
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (root_frame == nullptr)
{
// Create a Frame to act as the navigation context and associate it with
// a SuspensionManager key
root_frame = winrt::Windows::UI::Xaml::Controls::Frame();
root_frame.NavigationFailed({ this, &App::OnNavigationFailed });
if (event_arg.PreviousExecutionState() == winrt::Windows::ApplicationModel::Activation::ApplicationExecutionState::Terminated)
{
// Restore the saved session state only when appropriate, scheduling the
// final launch steps after the restore is complete
}
if (event_arg.PrelaunchActivated() == false)
{
if (root_frame.Content() == nullptr)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
root_frame.Navigate(winrt::xaml_typename<mainpage>(), box_value(event_arg.Arguments()));
}
// Place the frame in the current Window
winrt::Windows::UI::Xaml::Window::Current().Content(root_frame);
// Ensure the current window is active
winrt::Windows::UI::Xaml::Window::Current().Activate();
}
}
else
{
if (event_arg.PrelaunchActivated() == false)
{
if (root_frame.Content() == nullptr)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
root_frame.Navigate(winrt::xaml_typename<mainpage>(), box_value(event_arg.Arguments()));
}
// Ensure the current window is active
winrt::Windows::UI::Xaml::Window::Current().Activate();
}
}
}
void App::OnNavigationFailed(const IInspectable&, const winrt::Windows::UI::Xaml::Navigation::NavigationFailedEventArgs& event_arg)
{
throw winrt::hresult_error(E_FAIL, winrt::hstring(L"Failed to load Page ") + event_arg.SourcePageType().Name);
}
};
int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int) {
winrt::Windows::UI::Xaml::Application::Start([](auto &&) { winrt::make<App>(); });
}
If it is not possible, how can I properly create a page?
It is simple if you have installed the CppWinRT VSIX. Then create a "View model" and edit the files. You shouldn't derive the struct from the winrt::Windows::UI::Xaml::Controls::Page class because winrt::xaml_typename<> needs a WinRT class in <>.

How to append text to file in a UWP app

I need to create a simple log class (just a "save to file" method) in a UWP app for debugging purpose but AppendTextAsync is much different from ofstream and I don't know how to use.
This is my class
#pragma once
ref class winRTLog sealed
{
public:
winRTLog();
void save(Platform::String^ log);
private:
Platform::String^ myFilename = L"myLog.txt";
Windows::Storage::StorageFolder^ myFolder;
Windows::Storage::StorageFile^ myFile;
Windows::Foundation::IAsyncOperation<Windows::Storage::StorageFile^>^ createMyFile;
concurrency::task<Windows::Storage::StorageFile^> myFileTask;
};
and here's the mess I've written so far trying to understand the documentation
#include "pch.h"
#include <ppltasks.h>
#include "winRTLog.h"
winRTLog::winRTLog()
{
myFolder = Windows::Storage::ApplicationData::Current->LocalFolder;
createMyFile = myFolder->CreateFileAsync(myFilename, Windows::Storage::CreationCollisionOption::OpenIfExists);
myFileTask = concurrency::create_task(myFolder->GetFileAsync(myFilename));
//how to make myFile point the file I've created?
}
void winRTLog::save(Platform::String^ log)
{
//??
myFileTask.then([&]()
{
//how to use Windows::Storage::FileIO::AppendTextAsync
//with myFile and log?
});
}
Don't use StorageFile APIs for saving data into local folder. Only use it if you need to: for APIs that require you passing them in a storage file or to access a place like pictures library which doesn't have a real path. StorageFile APIs are terribly slow compared to traditional APIs. Worst of all, all of the file operations are asynchronous, which means they're a pain to work with and even a bigger pain to debug.
For your scenario, I'd just use std::wofstream if you're familiar with it:
#include <fstream>
class winRTLog
{
public:
winRTLog(Platform::String^ fileName);
void save(Platform::String^ log);
private:
std::wofstream m_OutStream;
};
winRTLog::winRTLog(Platform::String^ fileName) :
m_OutStream(std::wstring(Windows::Storage::ApplicationData::Current->LocalFolder->Path->Data()) + L"\\" + fileName->Data(), std::ios::app)
{
if (!m_OutStream.is_open())
throw std::runtime_error("Failed to open the log file.");
}
void winRTLog::save(Platform::String^ log)
{
m_OutStream << log->Data();
if (m_OutStream.fail())
throw std::runtime_error("Failed to write to the log file.");
}
Alternatively, you can use Win32 file APIs:
#include <memory>
#include <string>
#include <windows.h>
class winRTLog
{
public:
winRTLog(Platform::String^ fileName);
~winRTLog();
void save(Platform::String^ log);
private:
HANDLE m_LogHandle;
};
winRTLog::winRTLog(Platform::String^ fileName)
{
auto filePath = std::wstring(Windows::Storage::ApplicationData::Current->LocalFolder->Path->Data()) + L"\\" + fileName->Data();
m_LogHandle = CreateFile2(filePath.c_str(), GENERIC_WRITE, 0, OPEN_ALWAYS, nullptr);
if (m_LogHandle == INVALID_HANDLE_VALUE)
throw std::runtime_error("Failed to open the log file: error code " + std::to_string(GetLastError()));
if (SetFilePointer(m_LogHandle, 0, nullptr, FILE_END) == INVALID_SET_FILE_POINTER)
throw std::runtime_error("Failed to set file pointer to the end of file: error code " + std::to_string(GetLastError()));
}
winRTLog::~winRTLog()
{
if (m_LogHandle != INVALID_HANDLE_VALUE)
CloseHandle(m_LogHandle);
}
void winRTLog::save(Platform::String^ log)
{
// Convert to UTF8
std::string utf8;
utf8.resize(4 * log->Length());
auto utf8Length = WideCharToMultiByte(CP_UTF8, 0, log->Data(), static_cast<int>(log->Length()), &utf8[0], static_cast<int>(4 * log->Length()), nullptr, nullptr);
if (utf8Length == 0)
throw std::runtime_error("Failed to convert log message to UTF8: error code " + std::to_string(GetLastError()));
utf8.resize(utf8Length);
// Write to actual log
DWORD bytesWritten;
auto writeResult = WriteFile(m_LogHandle, utf8.data(), static_cast<DWORD>(utf8.length()), &bytesWritten, nullptr);
if (writeResult == FALSE || bytesWritten != utf8.length())
throw std::runtime_error("Failed to write log message to the log file: error code " + std::to_string(GetLastError()));
}
Note, I used throwing std::runtime_error for error handling for example purposes: you might want to do it differently.
Based on the answer by #Sunius, but for C++/WinRT in a UWP app, noting that the log file will be in the %appdata%\..\Local\Packages\<YOUR_UWP_APP PACKAGE>\LocalState folder:
// WinRtLog.cpp
#include "WinRtLog.h"
#include "winrt/Windows.Storage.h"
using namespace winrt::Windows;
winRTLog::winRTLog(std::wstring fileName) :
m_OutStream(std::wstring(Storage::ApplicationData::Current().LocalFolder().Path().data()) + L"\\" + fileName, std::ios::app)
{
if (!m_OutStream.is_open())
throw std::runtime_error("Failed to open the log file.");
}
void winRTLog::save(std::string log)
{
m_OutStream << log.data();
if (m_OutStream.fail())
throw std::runtime_error("Failed to write to the log file.");
}
And the header:
// WinRtLog.h
#pragma once
#include <fstream>
#include "winrt/Windows.ApplicationModel.Core.h"
class winRTLog
{
public:
winRTLog(std::wstring fileName);
void save(std::string log);
private:
std::wofstream m_OutStream;
};
Note that the broadFileSystemAccess capability is needed in your UWP Package.appxmanifest if the log file is to be stored in an arbitrary folder -- this capability must be set in code (the Appx GUI will not show it) as shown in this SO post.

Immediate Access Violation when debugging Windows.Devices.Sensors project in Windows 7

I have a large solution with 50+ unmanaged projects in it. I have recently added a project with managed code in it to the solution. The managed code accesses Windows.Devices.Sensors in a .NET dll. This dll is eventually wrapped by unmanaged code and called from another unmanaged project.
My problem is that I get the following access violation before main() even executes.
Unhandled exception at 0x744b8ea0 in myApplication.exe: 0xC0000005: Access violation.
Managed code:
#using <Windows.winmd>
using namespace Windows::Devices::Sensors;
#include <math.h>
namespace TabletSensors
{
namespace NET
{
public ref class DotNetDllClass
{
public:
DotNetDllClass()
{
Initialization();
}
~DotNetDllClass()
{
}
float* GetQuaternion()
{
OrientationSensorReading^ reading = _orientation->GetCurrentReading();
if( reading != nullptr )
{
float* quat = new float[4];
quat[0] = reading->Quaternion->X;
quat[1] = reading->Quaternion->Y;
quat[2] = reading->Quaternion->Z;
quat[3] = reading->Quaternion->W;
return quat;
}
else
{
return NULL;
}
}
private:
void Initialization()
{
_orientation = OrientationSensor::GetDefault();
if( _orientation != nullptr )
{
_orientation->ReportInterval = 16;
}
else
{
// not good ... throw exception or something
}
}
OrientationSensor^ _orientation;
};
}
}
Wrapper header file:
namespace TabletSensors
{
namespace NETWrapper
{
class DLLEXPORT_SENSORS WrapperClass
{
public:
__stdcall WrapperClass();
__stdcall ~WrapperClass();
float* __stdcall GetQuaternion();
};
}
}
Wrapper cpp file:
#define MIXSENSORS_BUILD
#include <gcroot.h>
#include "DotNetWrapper.h"
#include "DotNetDll.h"
using namespace TabletSensors::NETWrapper;
using namespace TabletSensors::NET;
static gcroot<TabletSensors::NET::DotNetDllClass^> Sensors = nullptr;
static System::UInt16 refCount = 0;
#pragma managed
inline TabletSensors::NET::DotNetDllClass^ GetSensors(void)
{
return (TabletSensors::NET::DotNetDllClass^)Sensors;
}
void Init()
{
++refCount;
if(GetSensors() == nullptr)
{
Sensors = gcnew TabletSensors::NET::DotNetDllClass();
}
}
void CleanUp()
{
if( refCount > 0 )
{
--refCount;
}
}
float* GetQuaternion_()
{
return Sensors->GetQuaternion();
}
#pragma unmanaged
TabletSensors::NETWrapper::WrapperClass::WrapperClass()
{
Init();
}
TabletSensors::NETWrapper::WrapperClass::~WrapperClass()
{
CleanUp();
}
float* TabletSensors::NETWrapper::WrapperClass::GetQuaternion()
{
float* x = new float[4];
return GetQuaternion_();
}
#pragma managed
Unmanaged project referencing my wrapper class:
#include "DotNetWrapper.h"
.
.
.
void UnmanagedProject::Update()
{
// if this line is present, I get an access violation without hitting any breakpoints.
TabletSensors::NETWrapper::WrapperClass _tabletSensors;
.
.
.
}
Since the managed code is trying to access Tablet Sensors I understand why it doesn't work on my Windows 7 desktop. What I don't understand it why it won't even allow me to debug my code at all. No breakpoints are hit before the Access Violation occurs.
What I would really like to figure out is how to use exception handling or #ifdefs to keep this crash from happening. But I have had very little luck.
Any ideas?
The fix is to Delay Load the managed DLL. The allows the application to run until that DLL is explicitly called. Thanks to Ben Voight for his answer here: https://stackoverflow.com/a/28467701/1454861

Mono CSharp Evaluator : creating two Action<object> via two distinct Run() crashes

I'm trying to embed mono in a c++ executable, and mono crashes on the second evaluator.Run(..) as below. Any idea of what I missed ?
Using mono 3.0.3.
EmbeddedMonoTest.cpp
// EmbeddedMonoTest.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/exception.h>
#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>
int _tmain(int argc, _TCHAR* argv[])
{
MonoDomain* domain = mono_jit_init_version ("ClassLibrary1", "v4.0.30319");
MonoAssembly* assembly = mono_domain_assembly_open (domain, "ClassLibrary1.dll");
mono_assembly_get_image(mono_domain_assembly_open (domain, "Mono.CSharp.dll"));
MonoImage* image = mono_assembly_get_image (assembly);
MonoClass* klass = mono_class_from_name(image, "ClassLibrary1", "Class1");
MonoMethod* test = mono_class_get_method_from_name(klass, "Test", 0);
mono_runtime_invoke(test, NULL, NULL, NULL);
return 0;
}
Class1.cs
using System;
using System.Reflection;
using Mono.CSharp;
namespace ClassLibrary1
{
public class Class1
{
public static void Test()
{
var assembly = Assembly.GetAssembly(typeof(Class1));
CompilerSettings settings = new CompilerSettings();
ReportPrinter printer = new ConsoleReportPrinter();
CompilerContext context = new CompilerContext(settings, printer);
Evaluator evaluator = new Evaluator(context);
evaluator.ReferenceAssembly(assembly);
evaluator.Run("using System; using ClassLibrary1;");
evaluator.Run("Action<object> action = args => {{ 'x'.ToString(); }}; ");
evaluator.Run("Action<object> b = args => {{ 'x'.ToString(); }}; ");
}
}
}
The error :
Unhandled exception at 0x0274b00d in EmbeddedMonoTest.exe: 0xC0000005: Access violation reading location 0x00000000.