Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Registering attached DependencyProperty of type DependencyProperty throws exception #9313

Open
HO-COOH opened this issue Feb 2, 2024 · 7 comments
Labels
bug Something isn't working team-Reach Issue for the Reach team

Comments

@HO-COOH
Copy link

HO-COOH commented Feb 2, 2024

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

  1. 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
        );
    }
  1. 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}
		);
  1. Build and run, app crashes

Expected behavior

No response

Screenshots

image

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

@HO-COOH HO-COOH added the bug Something isn't working label Feb 2, 2024
Copy link

github-actions bot commented Feb 2, 2024

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:

Note: You can give me feedback by thumbs upping or thumbs downing this comment.

@microsoft-github-policy-service microsoft-github-policy-service bot added the needs-triage Issue needs to be triaged by the area owners label Feb 2, 2024
@DarranRowe
Copy link

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.

@HO-COOH
Copy link
Author

HO-COOH commented Feb 3, 2024

@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.

@codendone codendone added team-Reach Issue for the Reach team and removed needs-triage Issue needs to be triaged by the area owners labels Feb 22, 2024
@sxlllslgh
Copy link

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.

@HO-COOH
Copy link
Author

HO-COOH commented Apr 27, 2024

@sxlllslgh I uploaded a repro

@sxlllslgh
Copy link

@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.
}

@HO-COOH
Copy link
Author

HO-COOH commented Apr 29, 2024

@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.
}

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working team-Reach Issue for the Reach team
Projects
None yet
Development

No branches or pull requests

4 participants