Registering attached DependencyProperty of type DependencyProperty throws exception
Describe the bug
I have a cases that need to register an attached DependencyProperty which accepts a value of DependencyProperty. Doing this throws E_NOTIMPL on app startup.
Steps to reproduce the bug
- Create a new runtimeclass, inherit from
DependencyObject, with this idl
runtimeclass Interpolation : Microsoft.UI.Xaml.DependencyObject
{
Interpolation();
static Microsoft.UI.Xaml.DependencyProperty LinearProperty{ get; };
static void SetLinear(
Microsoft.UI.Xaml.UIElement element,
Microsoft.UI.Xaml.DependencyProperty value
);
static Microsoft.UI.Xaml.DependencyProperty GetLinear(
Microsoft.UI.Xaml.UIElement element
);
}
- Use this cpp
winrt::Microsoft::UI::Xaml::DependencyProperty Interpolation::s_linearProperty =
winrt::Microsoft::UI::Xaml::DependencyProperty::RegisterAttached(
L"Linear",
winrt::xaml_typename<winrt::Microsoft::UI::Xaml::DependencyProperty>(),
winrt::xaml_typename<class_type>(),
{nullptr}
);
- Build and run, app crashes
Expected behavior
No response
Screenshots
NuGet package version
WinUI 3 - Windows App SDK 1.4.4: 1.4.231219000
Windows version
Windows 11 (22H2): Build 22621
Additional context
No response
Hi I'm an AI powered bot that finds similar issues based off the issue title.
Please view the issues below to see if they solve your problem, and if the issue describes your problem please consider closing this one. Thank you!
Closed similar issues:
- DependencyProperty is broken (#4118), similarity score: 0.71
Note: You can give me feedback by thumbs upping or thumbs downing this comment.
Because you haven't provided any information, is the application packaged or unpackaged? Is this in your main executable or is it in a separate component? Is it self contained?
If it is unpackaged, not self contained and in an executable, it is highly likely that a dependency property defined as a global variable will fail to initialise. The reasoning behind this is simple, C++. If you clean your project and then rebuild it, look at the source files that the project compiles. An extract from an example project, with WindowsPackageType set to None shows the following files being compiled.
2>pch.cpp
2>App.xaml.cpp
2>MainWindow.xaml.cpp
2>module.g.cpp
2>XamlTypeInfo.Impl.g.cpp
2>MddBootstrapAutoInitializer.cpp <- This is where MddBootstrapInitialize2 is called
2>XamlMetaDataProvider.cpp
2>XamlTypeInfo.g.cpp
The big problem here is, suppose the dependency property is defined in MainWindow.xaml.cpp, the C++ language doesn't guarantee that MddBootstrapInitialize2 will be called before the dependency property initialisation. As you can imagine, trying to initialise a dependency property that uses the Windows App SDK/WinUI 3 when the Windows App Runtime isn't initialised/referenced will not end well. This is also a well known C++ issue, and this is caused by the fact that the standard does not define the order that objects defined at global scope will be initialised. Visual C++'s linker will do it in order of object input IIRC. The Windows App SDK props/targets adds the extra files to the end of the build list. This means that that the default project layout guarantees that the Windows App Runtime will never be loaded until after you try to construct the dependency property. The best way to do this is to initially construct the dependency property to null. C++/WinRT will not attempt to create an instance of the component in this case. You should then create the dependency property instances during the call to the App constructor. If you are manually calling MddBootstrapInitialize2 or some other means of referencing the Windows App Runtime, verify that it is initialised and loaded by the time the dependency property constructor is called.
This wouldn't affect packaged applications that properly reference the Windows App Runtime as a dependency. This also wouldn't affect it when the Windows App Runtime is self contained. Finally, this shouldn't affect a component in a separate .dll file since this would be loaded after the executable has initialised.
@DarranRowe Thanks for that insight, I've never thought of that. But it's packaged project. Update: I just tried to compile and install the packaged version, so it should "properly" reference the runtime sdk. Does not solve the issue. Then I tried to put the property as a function local static so that it initialize when first called.
winrt::Microsoft::UI::Xaml::DependencyProperty Interpolation::LinearProperty()
{
static auto s_linearProperty =
winrt::Microsoft::UI::Xaml::DependencyProperty::RegisterAttached(
L"Linear",
winrt::xaml_typename<winrt::Microsoft::UI::Xaml::DependencyProperty>(),
winrt::xaml_typename<class_type>(),
{ nullptr }
);
return s_linearProperty;
}
But it still exceptions on the register line.
Well, I also encountered similar problems and spent a lot of effort to solve it. But I haven’t encountered an error like yours. What I met is winrt::hresult_class_not_registered and Class not registered, and link error that the attached property cannot be found when linking.
For you I guess the call of your RegisterAttached may be something wrong? Here is my solution and it works for me.
As @DarranRowe said, you should initialize the static member property with null in Interpolation.cpp:
winrt::Microsoft::UI::Xaml::DependencyProperty Interpolation::s_linearProperty = nullptr;
Then at the App.xaml.h:
App::App() {
Interpolation::LinearProperty() = winrt::Microsoft::UI::Xaml::DependencyProperty::RegisterAttached(
L"Linear",
winrt::xaml_typename</* The value type of s_linearProperty */>(),
winrt::xaml_typename<WinUI3Example::Interpolation>(),
winrt::Microsoft::UI::Xaml::PropertyMetadata{winrt::box_value(/* The default value of s_linearProperty */)});
InitializeComponent();
}
As I written above, another Issuse WindowsAppSDK #3673 suggests intializing attached properties at the constructor of App and before the InitializeComponent function.
@sxlllslgh I uploaded a repro
@sxlllslgh I uploaded a repro
I cloned your repository and did some modifications, now it seems work (at least no exception).
The critical problem I think is the propertyType parameter of RegisterAttached should be the true data type rather than the DependencyProperty, even you input it:
linearProperty =
winrt::Microsoft::UI::Xaml::DependencyProperty::RegisterAttached(
L"Linear",
winrt::xaml_typename<bool>(),
winrt::xaml_typename<class_type>(),
{ nullptr }
);
The complete code is as follows:
Interpolation.idl:
namespace _6_DependencyPropertyType_CPP {
[bindable]
[default_interface]
runtimeclass Interpolation : Microsoft.UI.Xaml.DependencyObject {
static void Initialize();
static Microsoft.UI.Xaml.DependencyProperty LinearProperty{ get; };
static void SetLinear(Microsoft.UI.Xaml.DependencyObject target, Microsoft.UI.Xaml.DependencyProperty value);
static Microsoft.UI.Xaml.DependencyProperty GetLinear(Microsoft.UI.Xaml.DependencyObject target);
}
}
Interpolation.h:
namespace winrt::_6_DependencyPropertyType_CPP::implementation {
struct Interpolation : InterpolationT<Interpolation> {
private:
static winrt::Microsoft::UI::Xaml::DependencyProperty linearProperty;
public:
static void Initialize();
static winrt::Microsoft::UI::Xaml::DependencyProperty LinearProperty() { return linearProperty; }
static winrt::Microsoft::UI::Xaml::DependencyProperty GetLinear(const winrt::Microsoft::UI::Xaml::DependencyObject& target);
static void SetLinear(const winrt::Microsoft::UI::Xaml::DependencyObject& target, const winrt::Microsoft::UI::Xaml::DependencyProperty& value);
};
}
Interpolation.cpp:
namespace winrt::_6_DependencyPropertyType_CPP::implementation {
winrt::Microsoft::UI::Xaml::DependencyProperty Interpolation::linearProperty = nullptr;
void Interpolation::Initialize() {
linearProperty =
winrt::Microsoft::UI::Xaml::DependencyProperty::RegisterAttached(
L"Linear",
winrt::xaml_typename<bool>(),
winrt::xaml_typename<class_type>(),
{ nullptr }
);
}
winrt::Microsoft::UI::Xaml::DependencyProperty Interpolation::GetLinear(const winrt::Microsoft::UI::Xaml::DependencyObject& target) {
return target.GetValue(linearProperty).as<winrt::Microsoft::UI::Xaml::DependencyProperty>();
}
void Interpolation::SetLinear(const winrt::Microsoft::UI::Xaml::DependencyObject& target, const winrt::Microsoft::UI::Xaml::DependencyProperty& value) {
target.SetValue(linearProperty, value);
}
}
App.xaml.h:
App::App() {
Interpolation::Initialize();
// Other code.
}
@sxlllslgh I uploaded a repro
I cloned your repository and did some modifications, now it seems work (at least no exception).
The critical problem I think is the
propertyTypeparameter ofRegisterAttachedshould be the true data type rather than theDependencyProperty, even you input it:linearProperty = winrt::Microsoft::UI::Xaml::DependencyProperty::RegisterAttached( L"Linear", winrt::xaml_typename<bool>(), winrt::xaml_typename<class_type>(), { nullptr } );The complete code is as follows:
Interpolation.idl:namespace _6_DependencyPropertyType_CPP { [bindable] [default_interface] runtimeclass Interpolation : Microsoft.UI.Xaml.DependencyObject { static void Initialize(); static Microsoft.UI.Xaml.DependencyProperty LinearProperty{ get; }; static void SetLinear(Microsoft.UI.Xaml.DependencyObject target, Microsoft.UI.Xaml.DependencyProperty value); static Microsoft.UI.Xaml.DependencyProperty GetLinear(Microsoft.UI.Xaml.DependencyObject target); } }
Interpolation.h:namespace winrt::_6_DependencyPropertyType_CPP::implementation { struct Interpolation : InterpolationT<Interpolation> { private: static winrt::Microsoft::UI::Xaml::DependencyProperty linearProperty; public: static void Initialize(); static winrt::Microsoft::UI::Xaml::DependencyProperty LinearProperty() { return linearProperty; } static winrt::Microsoft::UI::Xaml::DependencyProperty GetLinear(const winrt::Microsoft::UI::Xaml::DependencyObject& target); static void SetLinear(const winrt::Microsoft::UI::Xaml::DependencyObject& target, const winrt::Microsoft::UI::Xaml::DependencyProperty& value); }; }
Interpolation.cpp:namespace winrt::_6_DependencyPropertyType_CPP::implementation { winrt::Microsoft::UI::Xaml::DependencyProperty Interpolation::linearProperty = nullptr; void Interpolation::Initialize() { linearProperty = winrt::Microsoft::UI::Xaml::DependencyProperty::RegisterAttached( L"Linear", winrt::xaml_typename<bool>(), winrt::xaml_typename<class_type>(), { nullptr } ); } winrt::Microsoft::UI::Xaml::DependencyProperty Interpolation::GetLinear(const winrt::Microsoft::UI::Xaml::DependencyObject& target) { return target.GetValue(linearProperty).as<winrt::Microsoft::UI::Xaml::DependencyProperty>(); } void Interpolation::SetLinear(const winrt::Microsoft::UI::Xaml::DependencyObject& target, const winrt::Microsoft::UI::Xaml::DependencyProperty& value) { target.SetValue(linearProperty, value); } }
App.xaml.h:App::App() { Interpolation::Initialize(); // Other code. }
That doesn't solve my issue. I was intended to automatically do a linear interpolation of some dependencyProperty when it's set to a value (hence the class name Interpolation and SetLinear). Therefore I will need that dependencyproperty itself (not the type its storing), add a event handler to it, record its new value then start an animation to it.