Browse Source

Replaced raw COM pointers with smart ones (#21001)

* Bump MicroCOM to 0.11.4, add workaround for WinRT bug

* menu.h - use ComPtr for COM stored references

* Replaced raw COM pointers with smart ones

* fixes

* Use weak com ref from AvnMenuItem

* Better smart pointer handling

* Avalonia native readme update

* review

* missing dif

* Just call the ctor like a normal person
release/12.0.0-rc2
Nikita Tsukanov 6 months ago
committed by GitHub
parent
commit
3f73cc926c
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 3
      .github/copilot-instructions.md
  2. 1
      Avalonia.sln
  3. 6
      Directory.Packages.props
  4. 26
      build/MicroCOM.props
  5. 87
      native/Avalonia.Native/README.md
  6. 81
      native/Avalonia.Native/inc/comimpl.h
  7. 13
      native/Avalonia.Native/src/OSX/AvnString.mm
  8. 8
      native/Avalonia.Native/src/OSX/AvnWindow.mm
  9. 4
      native/Avalonia.Native/src/OSX/TopLevelImpl.mm
  10. 2
      native/Avalonia.Native/src/OSX/WindowBaseImpl.h
  11. 2
      native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
  12. 10
      native/Avalonia.Native/src/OSX/WindowImpl.mm
  13. 4
      native/Avalonia.Native/src/OSX/automation.mm
  14. 2
      native/Avalonia.Native/src/OSX/cgl.mm
  15. 37
      native/Avalonia.Native/src/OSX/clipboard.mm
  16. 3
      native/Avalonia.Native/src/OSX/common.h
  17. 1
      native/Avalonia.Native/src/OSX/cursor.h
  18. 34
      native/Avalonia.Native/src/OSX/cursor.mm
  19. 4
      native/Avalonia.Native/src/OSX/menu.h
  20. 24
      native/Avalonia.Native/src/OSX/menu.mm
  21. 2
      native/Avalonia.Native/src/OSX/metal.mm
  22. 4
      nukebuild/_build.csproj
  23. 2
      samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
  24. 3
      src/Avalonia.MicroCom/Avalonia.MicroCom.csproj
  25. 3
      src/Avalonia.Native/Avalonia.Native.csproj
  26. 6
      src/Avalonia.Native/avn.idl
  27. 3
      src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
  28. 17
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs
  29. 13
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionUtils.cs
  30. 15
      src/Windows/Avalonia.Win32/WinRT/WinRTInspectable.cs

3
.github/copilot-instructions.md

@ -0,0 +1,3 @@
When running code review, please refer to the following documents if they are relevant to the changed parts of the codebase:
- `native/Avalonia.Native/**` -> `native/Avalonia.Native/README.md`

1
Avalonia.sln

@ -99,6 +99,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\TrimmingEnable.props = build\TrimmingEnable.props
build\UnitTests.NetFX.props = build\UnitTests.NetFX.props
build\XUnit.props = build\XUnit.props
build\MicroCOM.props = build\MicroCOM.props
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}"

6
Directory.Packages.props

@ -15,9 +15,9 @@
<PackageVersion Include="HarfBuzzSharp" Version="8.3.1.3" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="8.3.1.3" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="8.3.1.3" />
<PackageVersion Include="MicroCom.CodeGenerator" Version="0.11.0" />
<PackageVersion Include="MicroCom.CodeGenerator.MSBuild" Version="0.11.0" />
<PackageVersion Include="MicroCom.Runtime" Version="0.11.0" />
<PackageVersion Include="MicroCom.CodeGenerator" Version="0.11.4" />
<PackageVersion Include="MicroCom.CodeGenerator.MSBuild" Version="0.11.4" />
<PackageVersion Include="MicroCom.Runtime" Version="0.11.4" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.10" />
<PackageVersion Include="Microsoft.Build.Framework" Version="18.0.2" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="15.1.548" />

26
build/MicroCOM.props

@ -0,0 +1,26 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- Uncomment to use local MicroCOM build -->
<!--<UseLocalMicroComBuild>true</UseLocalMicroComBuild>-->
<LocalMicroComPath Condition="'$(LocalMicroComPath)' == ''">$(MSBuildThisFileDirectory)/../../MicroCom</LocalMicroComPath>
</PropertyGroup>
<!-- Local build: delegate to MicroCom's own targets -->
<Import Condition="'$(UseLocalMicroComBuild)' == 'true'"
Project="$(LocalMicroComPath)/UseLocalBuild.targets" />
<!-- NuGet packages: include based on property flags -->
<ItemGroup Condition="'$(UseLocalMicroComBuild)' != 'true'">
<PackageReference Condition="'$(UseMicroCom)' == 'true'"
Include="MicroCom.Runtime" />
<PackageReference Condition="'$(UseMicroComGenerator)' == 'true'"
Include="MicroCom.CodeGenerator" />
<PackageReference Condition="'$(UseMicroComGeneratorMSBuild)' == 'true'"
Include="MicroCom.CodeGenerator.MSBuild"
PrivateAssets="all" />
<PackageReference Condition="'$(UseMicroComGeneratorRoslyn)' == 'true'"
Include="MicroCom.CodeGenerator.Roslyn"
PrivateAssets="all" />
</ItemGroup>
</Project>

87
native/Avalonia.Native/README.md

@ -0,0 +1,87 @@
# Avalonia.Native
This project implements the macOS native platform backend for Avalonia using Objective-C++ and COM (Component Object Model) interfaces.
## COM Reference Management
This codebase uses COM for cross-boundary object lifetime management. COM types are typically prefixed with `IAvn` (interfaces) or `Avn` (implementations). All COM objects ultimately derive from `IUnknown` and use reference counting (`AddRef`/`Release`).
**No raw COM pointer should ever be stored in class fields, instance variables, closures, or containers.** Raw COM pointers are only acceptable as function parameters and local variables with the lifetime of the current function call.
### COM Return Value Convention
By convention, any COM interface reference returned from a COM call has its reference counter incremented — it is the caller's responsibility to release it. With the `IFoo* GetFoo()` pattern it is hard to track this correctly, so all methods in `avn.idl` should use the out-parameter pattern instead:
```cpp
// WRONG — easy to leak or forget Release:
IFoo* GetFoo();
// CORRECT — use HRESULT + out-parameter:
HRESULT GetFoo(IFoo** ppv);
```
# Smart pointer types
### Objective-C Objects
For Objective-C objects, reference counting is provided by the compiler. Objective-C pointers _look_ like raw pointers, however every `NSObject*` reference is properly ref-counted by ARC (Automatic Reference Counting). No manual wrapping is needed for `NSObject*` fields.
### COM Smart Pointer Types
All retained COM references must use one of the following wrappers defined in `inc/comimpl.h`:
#### `ComPtr<T>` — Owning Reference
Use for any COM pointer that the holder needs to keep alive.
```cpp
ComPtr<IAvnWindow> _window; // correct
IAvnWindow* _window; // WRONG — raw COM pointer in a field
```
#### `ComObjectWeakPtr<T>` — Non-Owning Weak Reference
Use for intentional non-owning references to `ComObject`-derived objects (internal implementations). Allows safely referencing COM objects without extending their lifetime.
```cpp
ComObjectWeakPtr<WindowBaseImpl> _parent; // correct
WindowBaseImpl* _parent; // WRONG
```
Access weak references with `tryGet()`, which returns a `ComPtr<T>` (null if the object was destroyed):
```cpp
auto parent = _parent.tryGet();
if (parent) {
parent->DoSomething();
}
```
#### `ComStaticPtr<T>` — Process-Lifetime Static Reference
Use for static/global COM singletons that must live for the entire process lifetime. Intentionally does **not** Release in its destructor to avoid crashes during app teardown.
```cpp
static ComStaticPtr<IAvnGlDisplay> GlDisplay; // correct
static IAvnGlDisplay* GlDisplay; // WRONG
```
Assign via `set()`:
```cpp
GlDisplay.set(comnew<AvnGlDisplay>());
```
### `comnew<T>(args...)` — COM Object Factory
A convenience template similar to `std::make_shared`. Creates a new COM object and returns it wrapped in a `ComPtr<T>` with correct ownership (no double-AddRef):
```cpp
// Instead of:
ComPtr<Cursor> cursor(new Cursor(nsCursor), true);
// Write:
ComPtr<Cursor> cursor = comnew<Cursor>(nsCursor);
```
This works because `new T()` on a `ComObject`-derived type starts with refcount=1, and `comnew` wraps it in a `ComPtr` that takes ownership without an additional `AddRef`.

81
native/Avalonia.Native/inc/comimpl.h

@ -8,6 +8,7 @@
#include <cstring>
#include <memory>
#include <utility>
/**
START_COM_CALL causes AddRef to be called at the beginning of a function.
@ -110,6 +111,11 @@ public:
TInterface** getPPV()
{
if(_obj)
{
_obj->Release();
_obj = nullptr;
}
return &_obj;
}
@ -138,6 +144,78 @@ public:
}
};
template<class T, class... Args>
ComPtr<T> comnew(Args&&... args)
{
return ComPtr<T>(new T(std::forward<Args>(args)...), true);
}
/**
* ComStaticPtr is a COM smart pointer for process-lifetime static singletons.
* It AddRef/Release on set/reset, but intentionally does NOT Release in its destructor
* to avoid crashes during app teardown when destruction order is undefined.
*/
template<class TInterface>
class ComStaticPtr
{
private:
TInterface* _obj = nullptr;
public:
ComStaticPtr() = default;
~ComStaticPtr() = default;
ComStaticPtr(const ComPtr<TInterface>& ptr)
{
_obj = ptr;
if (_obj)
_obj->AddRef();
}
ComStaticPtr(const ComStaticPtr&) = delete;
ComStaticPtr(ComStaticPtr&&) = delete;
ComStaticPtr& operator=(const ComStaticPtr&) = delete;
ComStaticPtr& operator=(ComStaticPtr&&) = delete;
void set(TInterface* obj)
{
if (_obj == obj)
return;
if (_obj)
_obj->Release();
_obj = obj;
if (_obj)
_obj->AddRef();
}
void setNoAddRef(TInterface* obj)
{
if (_obj)
_obj->Release();
_obj = obj;
}
void reset()
{
if (_obj)
{
_obj->Release();
_obj = nullptr;
}
}
TInterface* getRaw() const { return _obj; }
template<class TCast> ComPtr<TCast> getComPtr()
{
if (_obj == nullptr)
return nullptr;
return dynamic_cast<TCast*>(_obj);
}
operator TInterface*() const { return _obj; }
TInterface* operator->() const { return _obj; }
};
class ComObjectWeakRefToken
{
public:
@ -249,6 +327,9 @@ public:
_rawPtr = nullptr;
_token = nullptr;
}
bool operator==(const ComObjectWeakPtr& other) const { return _rawPtr == other._rawPtr; }
bool operator!=(const ComObjectWeakPtr& other) const { return _rawPtr != other._rawPtr; }
};
#define FORWARD_IUNKNOWN() \

13
native/Avalonia.Native/src/OSX/AvnString.mm

@ -79,16 +79,14 @@ public:
class AvnStringArrayImpl : public virtual ComSingleObject<IAvnStringArray, &IID_IAvnStringArray>
{
private:
std::vector<ComPtr<IAvnString>> _list;
std::vector<ComPtr<AvnStringImpl>> _list;
public:
FORWARD_IUNKNOWN()
AvnStringArrayImpl(NSArray<NSString*>* array)
{
for(int c = 0; c < [array count]; c++)
{
ComPtr<IAvnString> s;
*s.getPPV() = new AvnStringImpl([array objectAtIndex:c]);
_list.push_back(s);
_list.push_back(comnew<AvnStringImpl>([array objectAtIndex:c]));
}
}
@ -96,17 +94,14 @@ public:
{
for(int c = 0; c < [array count]; c++)
{
ComPtr<IAvnString> s;
*s.getPPV() = new AvnStringImpl([array objectAtIndex:c].absoluteString);
auto s = comnew<AvnStringImpl>([array objectAtIndex:c].absoluteString);
_list.push_back(s);
}
}
AvnStringArrayImpl(NSString* string)
{
ComPtr<IAvnString> s;
*s.getPPV() = new AvnStringImpl(string);
_list.push_back(s);
_list.push_back(comnew<AvnStringImpl>(string));
}
virtual unsigned int GetCount() override

8
native/Avalonia.Native/src/OSX/AvnWindow.mm

@ -37,7 +37,7 @@
bool _isTransitioningToFullScreen;
bool _isTitlebarSession;
AvnMenu* _menu;
IAvnAutomationPeer* _automationPeer;
ComPtr<IAvnAutomationPeer> _automationPeer;
AvnAutomationNode* _automationNode;
}
@ -144,8 +144,6 @@
{
auto appMenu = ::GetAppMenu();
auto nativeAppMenu = dynamic_cast<AvnAppMenu*>(appMenu);
[[appMenuItem menu] removeItem:appMenuItem];
if(_menu != nullptr)
@ -153,9 +151,9 @@
[_menu setHasGlobalMenuItem:false];
}
[nativeAppMenu->GetNative() addItem:appMenuItem];
[appMenu->GetNative() addItem:appMenuItem];
[NSApp setMenu:nativeAppMenu->GetNative()];
[NSApp setMenu:appMenu->GetNative()];
}
}

4
native/Avalonia.Native/src/OSX/TopLevelImpl.mm

@ -311,7 +311,9 @@ HRESULT TopLevelImpl::BeginDragAndDropOperation(
for (auto i = 0; i < itemCount; ++i)
{
auto item = source->GetItem(i);
ComPtr<IAvnClipboardDataItem> item;
if(S_OK != source->GetItem(i, item.getPPV()))
continue;
auto writeableItem = [[WriteableClipboardItem alloc] initWithItem:item source:source];
auto draggingItem = [[NSDraggingItem alloc] initWithPasteboardWriter:writeableItem];
[draggingItem setDraggingFrame:dragItemRect contents:dragItemImage];

2
native/Avalonia.Native/src/OSX/WindowBaseImpl.h

@ -101,7 +101,7 @@ protected:
AutoFitContentView *StandardContainer;
AvnPoint lastPositionSet;
bool _shown;
std::list<WindowBaseImpl*> _children;
std::list<ComObjectWeakPtr<WindowBaseImpl>> _children;
public:
ComObjectWeakPtr<WindowBaseImpl> Parent = nullptr;

2
native/Avalonia.Native/src/OSX/WindowBaseImpl.mm

@ -27,7 +27,7 @@ WindowBaseImpl::~WindowBaseImpl() {
}
WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, bool usePanel) : TopLevelImpl(events) {
_children = std::list<WindowBaseImpl*>();
_children = std::list<ComObjectWeakPtr<WindowBaseImpl>>();
_shown = false;
_inResize = false;
BaseEvents = events;

10
native/Avalonia.Native/src/OSX/WindowImpl.mm

@ -100,11 +100,14 @@ void WindowImpl::ZOrderChildWindows()
{
for(auto iterator = _children.begin(); iterator != _children.end(); iterator++)
{
auto window = (*iterator)->Window;
auto child = (*iterator).tryGet();
if (!child) continue;
auto window = child->Window;
// #9565: Only bring window to front if it's on the currently active space
if ([window isOnActiveSpace]) {
(*iterator)->BringToFront();
child->BringToFront();
}
}
}
@ -113,7 +116,8 @@ bool WindowImpl::CanBecomeKeyWindow()
{
for(auto iterator = _children.begin(); iterator != _children.end(); iterator++)
{
if((*iterator)->IsModal())
auto child = (*iterator).tryGet();
if(child && child->IsModal())
{
return false;
}

4
native/Avalonia.Native/src/OSX/automation.mm

@ -8,7 +8,7 @@
@implementation AvnAccessibilityElement
{
IAvnAutomationPeer* _peer;
ComPtr<IAvnAutomationPeer> _peer;
AvnAutomationNode* _node;
NSMutableArray* _children;
NSArray<NSString*>* _attributeNames;
@ -70,7 +70,7 @@
return [NSString stringWithFormat:@"%@ '%@' (%p)",
GetNSStringAndRelease(_peer->GetClassName()),
GetNSStringAndRelease(_peer->GetName()),
_peer];
_peer.getRaw()];
}
- (IAvnAutomationPeer *)peer

2
native/Avalonia.Native/src/OSX/cgl.mm

@ -219,7 +219,7 @@ public:
}
};
static IAvnGlDisplay* GlDisplay = new AvnGlDisplay();
static ComStaticPtr<AvnGlDisplay> GlDisplay(comnew<AvnGlDisplay>());
extern IAvnGlDisplay* GetGlDisplay()

37
native/Avalonia.Native/src/OSX/clipboard.mm

@ -183,7 +183,9 @@ public:
for (auto i = 0; i < count; ++i)
{
auto item = source->GetItem(i);
ComPtr<IAvnClipboardDataItem> item;
if(S_OK != source->GetItem(i, item.getPPV()))
continue;
auto writeableItem = [[WriteableClipboardItem alloc] initWithItem:item source:source];
[writeableItems addObject:writeableItem];
}
@ -219,8 +221,8 @@ extern IAvnClipboard* CreateClipboard(NSPasteboard* pb)
@implementation WriteableClipboardItem
{
IAvnClipboardDataItem* _item;
IAvnClipboardDataSource* _source;
ComPtr<IAvnClipboardDataItem> _item;
ComPtr<IAvnClipboardDataSource> _source;
}
- (nonnull WriteableClipboardItem*) initWithItem:(nonnull IAvnClipboardDataItem*)item source:(nonnull IAvnClipboardDataSource*)source
@ -229,9 +231,6 @@ extern IAvnClipboard* CreateClipboard(NSPasteboard* pb)
_item = item;
_source = source;
// Each item references its source so it doesn't get disposed too early.
source->AddRef();
return self;
}
@ -283,8 +282,9 @@ NSString* TryConvertFormatToUti(NSString* format)
- (nonnull NSArray<NSPasteboardType>*) writableTypesForPasteboard:(nonnull NSPasteboard*)pasteboard
{
auto formats = _item->ProvideFormats();
if (formats == nullptr)
ComPtr<IAvnStringArray> formats;
auto hr = _item->ProvideFormats(formats.getPPV());
if (hr != S_OK || formats == nullptr)
return [NSArray array];
auto count = formats->GetCount();
@ -304,7 +304,6 @@ NSString* TryConvertFormatToUti(NSString* format)
if (uti != nil)
[utis addObject:uti];
}
formats->Release();
[utis addObject:GetAvnCustomDataType()];
@ -323,8 +322,9 @@ NSString* TryConvertFormatToUti(NSString* format)
if ([type isEqualToString:GetAvnCustomDataType()])
return @"";
ComPtr<IAvnClipboardDataValue> value(_item->GetValue([type UTF8String]), true);
if (value.getRaw() == nullptr)
ComPtr<IAvnClipboardDataValue> value;
HRESULT hr = _item->GetValue([type UTF8String], value.getPPV());
if (hr != S_OK || value.getRaw() == nullptr)
return nil;
if (value->IsString())
@ -336,19 +336,4 @@ NSString* TryConvertFormatToUti(NSString* format)
return [NSData dataWithBytesNoCopy:buffer length:length];
}
- (void) dealloc
{
if (_item != nullptr)
{
_item->Release();
_item = nullptr;
}
if (_source != nullptr)
{
_source->Release();
_source = nullptr;
}
}
@end

3
native/Avalonia.Native/src/OSX/common.h

@ -36,7 +36,8 @@ extern IAvnPlatformRenderTimer* CreatePlatformRenderTimer();
extern IAvnNativeObjectsMemoryManagement* CreateMemoryManagementHelper();
extern void SetAppMenu(IAvnMenu *menu);
extern void SetServicesMenu (IAvnMenu* menu);
extern IAvnMenu* GetAppMenu ();
class AvnAppMenu;
extern AvnAppMenu* GetAppMenu ();
extern NSMenuItem* GetAppMenuItem ();
extern void SetDockMenu(NSMenu* menu);

1
native/Avalonia.Native/src/OSX/cursor.h

@ -28,5 +28,4 @@ public:
}
};
extern std::map<AvnStandardCursorType, Cursor*> s_cursorMap;
#endif /* cursor_h */

34
native/Avalonia.Native/src/OSX/cursor.mm

@ -3,24 +3,24 @@
class CursorFactory : public ComSingleObject<IAvnCursorFactory, &IID_IAvnCursorFactory>
{
Cursor* arrowCursor = new Cursor([NSCursor arrowCursor]);
Cursor* crossCursor = new Cursor([NSCursor crosshairCursor]);
Cursor* resizeUpCursor = new Cursor([NSCursor resizeUpCursor]);
Cursor* resizeDownCursor = new Cursor([NSCursor resizeDownCursor]);
Cursor* resizeUpDownCursor = new Cursor([NSCursor resizeUpDownCursor]);
Cursor* dragCopyCursor = new Cursor([NSCursor dragCopyCursor]);
Cursor* openHandCursor = new Cursor([NSCursor openHandCursor]);
Cursor* dragLinkCursor = new Cursor([NSCursor dragLinkCursor]);
Cursor* pointingHandCursor = new Cursor([NSCursor pointingHandCursor]);
Cursor* contextualMenuCursor = new Cursor([NSCursor contextualMenuCursor]);
Cursor* IBeamCursor = new Cursor([NSCursor IBeamCursor]);
Cursor* resizeLeftCursor = new Cursor([NSCursor resizeLeftCursor]);
Cursor* resizeRightCursor = new Cursor([NSCursor resizeRightCursor]);
Cursor* resizeWestEastCursor = new Cursor([NSCursor resizeLeftRightCursor]);
Cursor* operationNotAllowedCursor = new Cursor([NSCursor operationNotAllowedCursor]);
Cursor* noCursor = new Cursor([NSCursor arrowCursor], true);
ComPtr<Cursor> arrowCursor = comnew<Cursor>([NSCursor arrowCursor]);
ComPtr<Cursor> crossCursor = comnew<Cursor>([NSCursor crosshairCursor]);
ComPtr<Cursor> resizeUpCursor = comnew<Cursor>([NSCursor resizeUpCursor]);
ComPtr<Cursor> resizeDownCursor = comnew<Cursor>([NSCursor resizeDownCursor]);
ComPtr<Cursor> resizeUpDownCursor = comnew<Cursor>([NSCursor resizeUpDownCursor]);
ComPtr<Cursor> dragCopyCursor = comnew<Cursor>([NSCursor dragCopyCursor]);
ComPtr<Cursor> openHandCursor = comnew<Cursor>([NSCursor openHandCursor]);
ComPtr<Cursor> dragLinkCursor = comnew<Cursor>([NSCursor dragLinkCursor]);
ComPtr<Cursor> pointingHandCursor = comnew<Cursor>([NSCursor pointingHandCursor]);
ComPtr<Cursor> contextualMenuCursor = comnew<Cursor>([NSCursor contextualMenuCursor]);
ComPtr<Cursor> IBeamCursor = comnew<Cursor>([NSCursor IBeamCursor]);
ComPtr<Cursor> resizeLeftCursor = comnew<Cursor>([NSCursor resizeLeftCursor]);
ComPtr<Cursor> resizeRightCursor = comnew<Cursor>([NSCursor resizeRightCursor]);
ComPtr<Cursor> resizeWestEastCursor = comnew<Cursor>([NSCursor resizeLeftRightCursor]);
ComPtr<Cursor> operationNotAllowedCursor = comnew<Cursor>([NSCursor operationNotAllowedCursor]);
ComPtr<Cursor> noCursor = comnew<Cursor>([NSCursor arrowCursor], true);
std::map<AvnStandardCursorType, Cursor*> s_cursorMap =
std::map<AvnStandardCursorType, ComPtr<Cursor>> s_cursorMap =
{
{ CursorArrow, arrowCursor },
{ CursorAppStarting, arrowCursor },

4
native/Avalonia.Native/src/OSX/menu.h

@ -29,8 +29,8 @@ class AvnAppMenuItem : public ComSingleObject<IAvnMenuItem, &IID_IAvnMenuItem>
{
private:
NSMenuItem* _native; // here we hold a pointer to an AvnMenuItem
IAvnActionCallback* _callback;
IAvnPredicateCallback* _predicate;
ComPtr<IAvnActionCallback> _callback;
ComPtr<IAvnPredicateCallback> _predicate;
bool _isCheckable;
public:

24
native/Avalonia.Native/src/OSX/menu.mm

@ -35,7 +35,7 @@
@implementation AvnMenuItem
{
AvnAppMenuItem* _item;
ComObjectWeakPtr<AvnAppMenuItem> _item;
}
- (id) initWithAvnAppMenuItem: (AvnAppMenuItem*)menuItem
@ -61,13 +61,19 @@
{
return YES;
}
auto item = _item.tryGet();
if(item == nullptr)
return NO;
return _item->EvaluateItemEnabled();
return item->EvaluateItemEnabled();
}
- (void)didSelectItem:(nullable id)sender
{
_item->RaiseOnClicked();
auto item = _item.tryGet();
if(item == nullptr)
return;
item->RaiseOnClicked();
}
@end
@ -478,17 +484,15 @@ extern IAvnMenuItem* CreateAppMenuItemSeparator()
}
}
static IAvnMenu* s_appMenu = nullptr;
static ComStaticPtr<AvnAppMenu> s_appMenu;
static NSMenuItem* s_appMenuItem = nullptr;
extern void SetAppMenu(IAvnMenu *menu)
{
s_appMenu = menu;
s_appMenu.set(dynamic_cast<AvnAppMenu*>(menu));
if(s_appMenu != nullptr)
{
auto nativeMenu = dynamic_cast<AvnAppMenu*>(s_appMenu);
auto currentMenu = [s_appMenuItem menu];
if (currentMenu != nullptr)
@ -496,7 +500,7 @@ extern void SetAppMenu(IAvnMenu *menu)
[currentMenu removeItem:s_appMenuItem];
}
s_appMenuItem = [nativeMenu->GetNative() itemAtIndex:0];
s_appMenuItem = [s_appMenu->GetNative() itemAtIndex:0];
if (currentMenu == nullptr)
{
@ -524,9 +528,9 @@ extern void SetServicesMenu (IAvnMenu* menu)
[NSApplication sharedApplication].servicesMenu = nativeMenu->GetNative();
}
extern IAvnMenu* GetAppMenu ()
extern AvnAppMenu* GetAppMenu ()
{
return s_appMenu;
return s_appMenu.getRaw();
}
extern NSMenuItem* GetAppMenuItem ()

2
native/Avalonia.Native/src/OSX/metal.mm

@ -304,7 +304,7 @@ public:
}
};
static AvnMetalDisplay* _display = new AvnMetalDisplay();
static ComStaticPtr<AvnMetalDisplay> _display(comnew<AvnMetalDisplay>());
extern IAvnMetalDisplay* GetMetalDisplay()
{

4
nukebuild/_build.csproj

@ -7,11 +7,11 @@
<NoWarn>$(NoWarn);CS0649;CA1847</NoWarn>
<NukeTelemetryVersion>1</NukeTelemetryVersion>
<TargetFramework>$(AvsCurrentTargetFramework)</TargetFramework>
<UseMicroComGenerator>true</UseMicroComGenerator>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Nuke.Common" />
<PackageReference Include="MicroCom.CodeGenerator" />
<PackageReference Include="Numerge" />
<!-- Keep in sync with Avalonia.Build.Tasks -->
<PackageReference Include="Mono.Cecil" />
@ -22,6 +22,8 @@
<PackageDownload Include="dotnet-ilrepack" Version="[2.0.44]" />
</ItemGroup>
<Import Project="..\build\MicroCOM.props" />
<ItemGroup>
<NukeMetadata Include="**\*.json" Exclude="bin\**;obj\**" />
<NukeExternalFiles Include="**\*.*.ext" Exclude="bin\**;obj\**" />

2
samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj

@ -5,6 +5,7 @@
<TargetFramework>$(AvsCurrentTargetFramework)</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<UseMicroCom>true</UseMicroCom>
</PropertyGroup>
<ItemGroup>
@ -37,4 +38,5 @@
<Import Project="..\..\build\SampleApp.props" />
<Import Project="..\..\build\ReferenceCoreLibraries.props" />
<Import Project="..\..\build\MicroCOM.props" />
</Project>

3
src/Avalonia.MicroCom/Avalonia.MicroCom.csproj

@ -2,13 +2,14 @@
<PropertyGroup>
<TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks)</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<UseMicroCom>true</UseMicroCom>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MicroCom.Runtime" />
<ProjectReference Include="..\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj"
ReferenceOutputAssembly="false"
PrivateAssets="all" />
</ItemGroup>
<Import Project="..\..\build\MicroCOM.props" />
<Import Project="..\..\build\BuildTargets.targets" />
<Import Project="..\..\build\TrimmingEnable.props" />
<Import Project="..\..\build\NullableEnable.props" />

3
src/Avalonia.Native/Avalonia.Native.csproj

@ -7,6 +7,7 @@
<IsPackable Condition="'$(ForcePackAvaloniaNative)' == 'True'">True</IsPackable>
<TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks)</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<UseMicroComGeneratorMSBuild>true</UseMicroComGeneratorMSBuild>
</PropertyGroup>
<ItemGroup Condition="'$(PackAvaloniaNative)' == 'true'">
@ -21,10 +22,10 @@
<ItemGroup>
<ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
<PackageReference Include="MicroCom.CodeGenerator.MSBuild" PrivateAssets="all" />
<MicroComIdl Include="avn.idl" CSharpInteropPath="Interop.Generated.cs" />
</ItemGroup>
<Import Project="..\..\build\MicroCOM.props" />
<Import Project="..\..\build\DevAnalyzers.props" />
<Import Project="..\..\build\TrimmingEnable.props" />
<Import Project="..\..\build\NullableEnable.props" />

6
src/Avalonia.Native/avn.idl

@ -1004,14 +1004,14 @@ interface IAvnClipboard : IUnknown
interface IAvnClipboardDataSource : IUnknown
{
int GetItemCount();
IAvnClipboardDataItem* GetItem(int index);
HRESULT GetItem(int index, IAvnClipboardDataItem**ppv);
}
[uuid(e40f36d9-69f4-45fd-9ca2-6e64e80feb6d)]
interface IAvnClipboardDataItem : IUnknown
{
IAvnStringArray* ProvideFormats();
IAvnClipboardDataValue* GetValue([const] char* format);
HRESULT ProvideFormats(IAvnStringArray** ppv);
HRESULT GetValue([const] char* format, IAvnClipboardDataValue** ppv);
}
[uuid(e97f24f6-1c84-4d95-8ffe-5b2c72e016ed)]

3
src/Windows/Avalonia.Win32/Avalonia.Win32.csproj

@ -3,6 +3,7 @@
<TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks)</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<EnableRuntimeMarshalling>true</EnableRuntimeMarshalling>
<UseMicroComGeneratorMSBuild>true</UseMicroComGeneratorMSBuild>
</PropertyGroup>
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0'))">
<PackageReference Include="System.Numerics.Vectors" />
@ -17,13 +18,13 @@
<ItemGroup>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
<PackageReference Include="Avalonia.Angle.Windows.Natives" />
<PackageReference Include="MicroCom.CodeGenerator.MSBuild" PrivateAssets="all" />
<MicroComIdl Include="WinRT\winrt.idl" CSharpInteropPath="WinRT\WinRT.Generated.cs" />
<MicroComIdl Include="Win32Com\win32.idl" CSharpInteropPath="Win32Com\Win32.Generated.cs" />
<MicroComIdl Include="DirectX\directx.idl" CSharpInteropPath="DirectX\directx.Generated.cs" />
<MicroComIdl Include="DComposition\dcomp.idl" CSharpInteropPath="DComposition\DComp.Generated.cs" />
<ProjectReference Include="..\Avalonia.Win32.Automation\Avalonia.Win32.Automation.csproj" />
</ItemGroup>
<Import Project="..\..\..\build\MicroCOM.props" />
<Import Project="..\..\..\build\NullableEnable.props" />
<Import Project="..\..\..\build\DevAnalyzers.props" />
<Import Project="..\..\..\build\SourceGenerators.props" />

17
src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs

@ -41,15 +41,16 @@ namespace Avalonia.Win32.WinRT.Composition
}
public uint SourceCount => (uint)(_sources?.Length ?? 0);
public override void OnUnreferencedFromNative()
protected override void Destroyed()
{
if (_sources == null)
return;
/*foreach(var s in _sources)
s.Dispose();*/
_sources = null;
if (_sources != null)
{
foreach (var source in _sources)
source.Dispose();
}
base.Destroyed();
}
}

13
src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionUtils.cs

@ -83,12 +83,21 @@ internal static class WinUiCompositionUtils
using var backDropParameter =
backDropParameterFactory.Create(backdropString.Handle);
using var backDropParameterAsSource = backDropParameter.QueryInterface<IGraphicsEffectSource>();
var blurEffect = new WinUIGaussianBlurEffect(backDropParameterAsSource);
using var blurEffect = new WinUIGaussianBlurEffect(backDropParameterAsSource);
// WinRT has broken reference counting for effects returned from GetSource. It will Release returned ref
// and attempt to use it afterward. This usually works because the wrapper effect DOES hold a reference
// to the COM object, so invalid memory access succeeds.
// In our case MicroCOM will rightfully release native memory once the latest native reference is gone.
// Another problem is WinUIEffectBase destroying its sources when it's no longer referenced by anything.
// So we need to force-keep a native pointer until we are done.
using var blurLease = MicroComRuntime.LeaseNativePointerForCall<IGraphicsEffectSource>(blurEffect);
using var blurEffectFactory = compositor.CreateEffectFactory(blurEffect);
using var compositionEffectBrush = blurEffectFactory.CreateBrush();
using var backdropBrush = CreateBackdropBrush(compositor);
var saturateEffect = new SaturationEffect(blurEffect);
using var saturateEffect = new SaturationEffect(blurEffect);
using var satEffectFactory = compositor.CreateEffectFactory(saturateEffect);
using var sat = satEffectFactory.CreateBrush();
compositionEffectBrush.SetSourceParameter(backdropString.Handle, backdropBrush);

15
src/Windows/Avalonia.Win32/WinRT/WinRTInspectable.cs

@ -8,13 +8,8 @@ using MicroCom.Runtime;
namespace Avalonia.Win32.WinRT
{
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
internal class WinRTInspectable : IInspectable, IMicroComShadowContainer
internal class WinRTInspectable : Avalonia.MicroCom.CallbackBase, IInspectable
{
public virtual void Dispose()
{
}
public unsafe void GetIids(ulong* iidCount, Guid** iids)
{
var interfaces = GetType().GetInterfaces().Where(typeof(IUnknown).IsAssignableFrom)
@ -28,13 +23,5 @@ namespace Avalonia.Win32.WinRT
public IntPtr RuntimeClassName => NativeWinRTMethods.WindowsCreateString(GetType().FullName!);
public TrustLevel TrustLevel => TrustLevel.BaseTrust;
public MicroComShadow? Shadow { get; set; }
public virtual void OnReferencedFromNative()
{
}
public virtual void OnUnreferencedFromNative()
{
}
}
}

Loading…
Cancel
Save