* fix: correct null check variable in TryResolveFileReferenceUri on macOS The null check on line 401 tested `fileUri` instead of `filePathUri`, making it dead code since `fileUri` was already checked on line 394. When `[fileUri filePathURL]` returned nil (non-file URL or unreachable resource), the nil `filePathUri` was passed to `[filePathUri absoluteString]` and then to `CreateAvnString`, causing a native crash. * fix: initialize NSError to nil in SaveBookmarkToBytes on macOS NSError* was declared without initialization, containing stack garbage. On the success path (bookmarkData non-nil), Cocoa does not guarantee zeroing the error out-parameter, so the subsequent `if (error != nil)` check could read garbage and incorrectly call CreateAvnString with a garbage pointer. Initialize to nil and restructure to `else if` so the error is only inspected when bookmarkData is nil (the failure path). * chore: retrigger CI * fix: guard out-parameters in SaveBookmarkToBytes Initialize *ppv to nullptr on entry so callers never read garbage on the nil-fileUri / nil-bookmarkData paths. Guard *err write with a nullptr check for callers that pass no error out-parameter. * chore: retrigger CI --------- Co-authored-by: Julien Lebosquain <julien@lebosquain.net> |
2 months ago | |
|---|---|---|
| .. | ||
| inc | Replaced raw COM pointers with smart ones (#21001) | 2 months ago |
| src/OSX | Fix macOS StorageProvider null pointer bugs (#21115) | 2 months ago |
| README.md | Replaced raw COM pointers with smart ones (#21001) | 2 months ago |
| generate-headers.sh | Added generate-headers.sh for convenience purposes (#17413) | 2 years ago |
README.md
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:
// 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.
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.
ComObjectWeakPtr<WindowBaseImpl> _parent; // correct
WindowBaseImpl* _parent; // WRONG
Access weak references with tryGet(), which returns a ComPtr<T> (null if the object was destroyed):
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.
static ComStaticPtr<IAvnGlDisplay> GlDisplay; // correct
static IAvnGlDisplay* GlDisplay; // WRONG
Assign via set():
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):
// 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.