I am trying to figure out how to access the WinRT API via WRL in C++, using visual studio 2022 with the latest update 17.1.2.
(Background: I've done a fair amount of old-fashioned Windows desktop programming with the Win32 API. However, I now wish to access parts of the Windows API that are supported only in WinRT, such as access to Bluetooth LE devices. Note that I am trying to avoid UWP applications, as I fear it may cause my applications to be bloated, and am not sure what needed functionality it might remove from my programs. Further, I have tried Microsoft's C++/WinRT, but was having lots of trouble at points where the Microsoft compiler was telling me there was a problem up in the generated C++ headers--not my own code. So I am now trying to work closer to the ground with WRL.)
So, starting with an example provided in MS documentation (uri winRT class, which I can get to work as they wrote it), I have tried to try use of the ActivateInstance WRL class (which is not used in their example).
It seems not to be working for me. But I am probably doing something wrong. Can anyone help?
I have pared down the code to show the precise issue:
#include <roapi.h>
#include <combaseapi.h>
#include <iostream>
#include <windows.foundation.h>
#include <windows.foundation.collections.h>
#include <wrl\wrappers\corewrappers.h>
#include <wrl\client.h>
#include <stdio.h>
using namespace ABI::Windows::Foundation;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
int dummy;
ComPtr<IUriRuntimeClassFactory> uriFactory;
ComPtr<IUriRuntimeClass> uri;
HRESULT hr, hr2;
int main()
{
// Initialize the Windows Runtime.
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
// Get the activation factory for the IUriRuntimeClass interface.
hr = GetActivationFactory(HStringReference(L"Windows.Foundation.Uri").Get(), &uriFactory);
hr2 = ActivateInstance (HStringReference(L"Windows.Foundation.Uri").Get(), &uri);
dummy = 1;
}
What happens is hr returns as S_OK. (Creating the factory. I don't use it here, but it is apparently a valid factory as it works when I leave in the rest of the Microsoft WRL example.)
But, what is confusing me is that hr2 is returning "E_NOTIMPl (not implemented)", and I thought it would get me a smart pointer to the uri class.
(Referencing this MS documentation for ActivateInstance:
and observing that the argument types are the same as
for GetActivationFactory
Thanks
Related
I develop an UMDF2.0 driver using VS2019.
Inside this driver I need to communicate with an BLE device.
I have to use BluetoothLEDevice class to do this. This is a WinRT Api.
I'm completely lost on how to call C++/WinRT from my driver.
Does anyone have experienced this situation ?
Thank a lot for your great support,
EDIT 1#
I use the following simple test code in new cpp file into umdf2 sample project:
#include <windows.devices.h>
#include <windows.devices.bluetooth.h>
#include <windows.devices.bluetooth.genericattributeprofile.h>
using namespace ABI::Windows::Devices::Bluetooth;
void testBle()
{
BluetoothLEDevice dev;
}
I have the following error :
Error C2079
'dev' uses a class of 'ABI::Windows::Devices::Bluetooth::BluetoothLEDevice' not defined
EDIT 2
I found one usefull project on GitHub that help me a lot to make all this work. Please find the link below :
https://github.com/bucienator/ble-win-cpp
Thank you again for your help
You cannot instantiate the BluetoothLEDevice with default constructor because there is no such constructor in this case.
Please use this code snippet in instantiate the BT device.
#include <winrt/windows.devices.h>
#include <winrt/Windows.Devices.Bluetooth.h>
using namespace Windows::Devices::Bluetooth;
void testBle()
{
BluetoothLEDevice btDev = NULL;
IAsyncOperation<BluetoothLEDevice> launchOp =
BluetoothLEDevice::FromIdAsync(L"test");
btDev = launchOp.GetResults();
}
(This is the next iterative question so I can figure out how to use WRL, after this one was answered I believe correctly.)
So I'm trying to build up to being able to access Bluetooth LE devices through WinRT via WRL.
Continuing in the pattern of a Microsoft example (which is for the uri WinRT class), I am trying at this point just to get the class factory for the DeviceWatcher Class. ( Documentation for that class seems to be here ), this is my pared-down example code:
#include <roapi.h>
#include <combaseapi.h>
#include <iostream>
#include <windows.foundation.h>
#include <windows.foundation.collections.h>
#include <wrl\wrappers\corewrappers.h>
#include <wrl\client.h>
#include <stdio.h>
#include <windows.devices.enumeration.h>
using namespace ABI::Windows::Foundation;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace ABI::Windows::Devices::Enumeration;
int dummy;
ComPtr<IUriRuntimeClassFactory> uriFactory;
ComPtr<IUriRuntimeClass> uri;
ComPtr<IDeviceWatcherRuntimeClassFactory> devWatcherFactory; //This line produces the only error, "IDeviceWatcherRuntimeClassFactory undefined"
//(When the line is omitted, the code compiles, links, and produces the smart pointer to a uri class)
HRESULT hr, hr2, hr3;
int main()
{
// Initialize the Windows Runtime.
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
// Get the activation factory for the IUriRuntimeClass interface.
ComPtr<IUriRuntimeClassFactory> uriFactory;
HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_Uri).Get(), &uriFactory);
// Create a string that represents a URI.
HString uriHString;
hr = uriHString.Set(L"http://www.microsoft.com");
// Create the IUriRuntimeClass object.
ComPtr<IUriRuntimeClass> uri;
hr2 = uriFactory->CreateUri(uriHString.Get(), &uri);
dummy = 1;
}
It is the MS example, up to the point of its uri WinRT class construction, and compiles and links and correctly constructs the uri class smart pointer when I leave out my added line that declares the ComPtr for IDeviceWatcherRuntimeClassFactory
The line is my attempt to just create a smart-pointer for the DeviceWatcher class factory, and it fails with identified "IDeviceWatcherRuntimeClassFactory undefined". (It also fails if I use small "d" as the second letter of the type argument.
In using "IDeviceWatcherRuntimeClassFactory", I'm trying to proceed by exact analogy to the MS example.
Besides the MS online documentation for the DeviceWatcher class (3), which doesn't seem to help me, I've also tried looking at what VS2022 solution explorer shows as the expansion of the header windows.devices.enumeration.h (which also does not seem to be telling me what name to use as far as I can figure out).
I've also tried using the VS2022 Object Browser, pointed to a custom component set of all .winmd files in C:\Windows\System32\WinMetaData, and again the information is not helping me, given my level of knowledge.
Can someone give me a sound method, for an arbitrary WinRT class, to figure out the type to use for the smart pointer to the class factory?
And, to save another iteration, once I can make a smart pointer to the factory, I will
need to make a smart pointer to the class itself (in my case, to DeviceWatcher). Can someone tell me what exact name to use for the ComPtr type?
Thanks.
I have a Win32 programm, where I want to add some winRT calls. Among other things I want to open a file without a GUI interface.
I use the async file open call from the StorageFile class, because the next call needs an IStorageFile interface.
#include <roapi.h>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Foundation.h>
void openFile()
{
using namespace winrt;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Storage;
HRESULT rtn = RoInitialize(RO_INIT_MULTITHREADED);
winrt::hstring path{ L"C:\\Users...\\mytextfile.txt"};
//wait for open the file
auto file = co_await StorageFile::GetFileFromPathAsync(path);
//IStorageFile interface needed
}
int main()
{
openFile();
return 0;
}
At the moment, the compiler complains that the co_await expression requires a suitable "await_ready" function and none was found.
I`m not sure if this is the case due to a missing header include or if "co_await" can not be used within a win32 application.
Edit:
My visual studio project setup is:
- use c++17, add cppwinrt.exe to my include directories, link against windowsapp.lib and use windows sdk version 10.0.17134.0.
The problem is that the openFile() function does not have the proper return type to handle co_await.
See the research and work that went into the answer I created for C++11 threads to update MFC application windows. SendMessage(), PostMessage() required? which contains a list of recommendations for various approaches to coroutines.
This question was about using C++/WinRT with MFC but the material also applies with WinAPI.
See as well synchronizing SDK with Windows 10 update and using WinRT with Standard C++ which contains a simple console application example using the Web Syndication async functionality to retrieve a list of URLs from an RSS feed. There are a number of links to documentation, some of which is a bit out dated now.
Addendum: Sample Console application
I created the following simple console application using Visual Studio 2017. I created the text file and then ran this in the debugger. I then renamed the text file and ran it again in the debugger and an exception was thrown since the file with that name no longer existed.
See also C++/WinRT, part of Windows SDK 17134 is not compatible with Visual Studio 15.8 Preview 3 which describes a compiler option you may need to change. I did.
// console_winrt.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
// Requires the following changes to the project properties in the C++ compiler section.
// - C++ language standard must be set to C++17
// - Add /await to the Additional options
#include "pch.h"
#pragma comment(lib, "windowsapp")
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Foundation.h>
#include <iostream>
winrt::Windows::Foundation::IAsyncAction openMyFile()
{
winrt::hstring path{ L"D:\\Users\\rickc\\mytextfile.txt" };
//wait for open the file
auto file = co_await winrt::Windows::Storage::StorageFile::GetFileFromPathAsync(path);
//IStorageFile interface needed
auto xDate = file.DateCreated();
std::cout << "file was found " << std::endl;
}
int main()
{
// initialize the WinRT apartment.
winrt::init_apartment();
auto x = openMyFile();
// wait on the file access since that is all we are doing and we need to give it time.
x.get();
return 0;
}
I used the following properties settings.
I'm working in a native C++ enviroment on WP8.1.
Let's say I want to call a functions like
Microsoft.Phone.Info::DeviceExtendedProperties.GetValue( "DeviceUniqueId" );
The problem is no matter how I try, it didn't pass compile.
I know "Microsoft.Phone.Info" is a name space,
in C# ppl wrote:
using Microsoft.Phone.Info;
but in C++, I tried
using namespace Microsoft.Phone.Info;
void func()
{
DeviceExtendedProperties.GetValue("DeviceUniqueId");
}
didn't pass compile. or
void func()
{
Microsoft.Phone.Info::DeviceExtendedProperties.GetValue("DeviceUniqueId");
}
didn't pass compile.
It keeps telling me something like this
'Microsoft' : illegal use of namespace identifier in expression
so how do I use this namespace properly?
Thank you guys for the reading and answering. :-)
I can't find a C++ example for retriving the device ID. :-P
Although many of the properties from DeviceInformation can be found on EasClientDeviceInformation, the device ID is not one of them. Instead, you should look at using the ASHWID which will get you an app-specific hardware ID.
In native WP runtime, using a namespace follows this syntax
using namespace Windows::Phone::Info;
But you will find out that the namespace does not exist in runtime. Only in WP Silverlight.
So you can look at this MSDN webpage that tells you where they moved all the functionality from 8.0 SL to 8.1 runtime.
Windows Phone Silverlight to Windows Runtime namespace and class mappings
Then if you search for "Windows.Phone.Info" it will lead you to the new functions you should call.
Here is the ID : EasClientDeviceInformation class
I guess I got it:
EasClientDeviceInformation^ deviceInfo = ref new EasClientDeviceInformation();
deviceInfo->Id; //<- is what I want
The explanation of the problem is a little long-winded, please bear with me.
I have an unmanaged C++ static library that is used for financial application. It has business day conventions, swap conventions, bond conventions, etc. Most of the conventions rely on static global variables, which are initialized on first use. The library also initializes the holiday calendars on startup by running some queries against a SQL Server database using ODBC.
I have to interact with third-party software using web services. The only way to do this realistically is through C#. That isn't an issue, and I was making good progress. However, I hit a stumbling block when it became necessary to do some date calculations in C#. Since I didn't want to port all my C++ code to C#, I figured the most efficient way to achieve this would be by writing a Managed C++ Class Library DLL that is a wrapper around my unmanaged static library. Everything seems to work fine, I get no compile-time or link-time errors, and I can add the reference to the wrapper and see all the proper object definitions. However, when I try to run my application, it just hangs. I have tried playing around with a bunch of compiler setting for the wrapper DLL, to no avail. If I remove the project dependency on my unmanaged library, everything works fine. I have a strong suspicion that my liberal use of global static variables is causing issues. Is there are way to solve this problem, are at least figure out where the issue is? Example code is below.
Thanks,
Marc.
// FSAManaged.h
#pragma once
using namespace System;
//class XLDate;
namespace FSAManaged {
public ref class Futures
{
public:
static DateTime Expiry(String ^ code, DateTime date);
};
}
The implementation does not even rely on a dependency to the unmanaged static library:
// This is the main DLL file.
#include "stdafx.h"
#include "FSAManaged.h"
namespace FSAManaged
{
DateTime Futures::Expiry(String ^ code, DateTime date) {
return DateTime::Today;
}
}
For completeness' sake, here is AssemblyInfo.cpp:
#include "stdafx.h"
using namespace System;
using namespace System::Reflection;
using namespace System::Runtime::CompilerServices;
using namespace System::Runtime::InteropServices;
using namespace System::Security::Permissions;
//
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
//
[assembly:AssemblyTitleAttribute("FSAManaged")];
[assembly:AssemblyDescriptionAttribute("")];
[assembly:AssemblyConfigurationAttribute("")];
[assembly:AssemblyCompanyAttribute("?????")];
[assembly:AssemblyProductAttribute("FSAManaged")];
[assembly:AssemblyCopyrightAttribute("??????")];
[assembly:AssemblyTrademarkAttribute("")];
[assembly:AssemblyCultureAttribute("")];
//
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the value or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly:AssemblyVersionAttribute("1.0.*")];
[assembly:ComVisible(false)];
[assembly:CLSCompliantAttribute(true)];
[assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)];
Use the debugger. If you test this from C# then Project + Properties, Debug, tick "Enabled unmanaged code debugging". Setting up the symbol server in Tools + Options, Debugging, Symbols is strongly recommended. Run.
When it hangs use Debug + Break All. Debug + Windows + Threads and double-click the thread that is supposed to be doing the job. Debug + Windows + Call stack to see what is going on. Post the stack trace in your question if you can't figure it out. Anything you see in the Output window and the Visual Studio status bar is relevant too.
Static C++ variables are initialized from DllMain. There are lot's of things you should not do in DllMain; triggering the load of yet another Dll being the most important one. This is easy to break if you call into other peoples libraries in from DllMain.
I suggest you make an Init function on your Dll, which you call after the dll is up and running.