Skip to content

Complete branding in-code rename & fix native bugs#75

Merged
AnnaSasDev merged 20 commits intoInfiniLore:corefrom
freakdaniel:bugfix
Mar 10, 2026
Merged

Complete branding in-code rename & fix native bugs#75
AnnaSasDev merged 20 commits intoInfiniLore:corefrom
freakdaniel:bugfix

Conversation

@freakdaniel
Copy link
Contributor

@freakdaniel freakdaniel commented Mar 9, 2026

Description

Complete Photino-to-InfiniFrame rename across native and managed code, fix critical native bugs on all platforms, fix DragArea double-click race condition, and improve test infrastructure reliability

Changes have not been fully tested in production-ready projects. Unit tests pass locally on Linux (131 passed and 110 skipped, 0 failures), but end-to-end validation in example apps is pending

Development System

  • OS: Debian 13 (Trixie)
  • WM: KDE 6.3.6
  • Session: Wayland

Type of Change

  • Bug fix (non-breaking change fixing an issue)
  • Code style update (formatting, renaming)
  • Refactoring (no functional changes, no API changes)

Changes Made

Rename (Photino to InfiniFrame)

  • Renamed all C++ source/header files from Photino.* to InfiniFrame.*
  • Renamed C++ class Photino to InfiniFrame, struct PhotinoInitParams to InfiniFrameInitParams
  • Renamed constructor/destructor on all platforms (InfiniFrame::InfiniFrame / ~InfiniFrame)
  • Renamed all C exports from Photino_* to InfiniFrame_* (Exports.cpp, Exports.Tests.cpp)
  • Renamed PhotinoDialog to InfiniFrameDialog
  • Renamed JS interop channel from photinointerop to infiniFrameInterop
  • Windows CLASS_NAME changed from L"Photino" to L"InfiniFrame"
  • Updated CMakeLists.txt source file references
  • Updated all C# P/Invoke entry points in NativeDll.cs and InfiniFrameNative.cs
  • Updated Blazor component injection from PhotinoWindow to Window
  • Updated comments across BlazorWebView, WebServer, Shared, Enums, FluentApi
  • Updated Directory.Build.props author field
  • Renamed delegate member variable photino to infiniFrame in Mac delegate headers

Header / all platforms (InfiniFrame.h, *.cpp, *.mm)

  • Replaced typedef function pointer declarations with using aliases
  • Added const qualifier to getter methods across all platforms
  • Added [[nodiscard]] to key getters (GetDialog, GetUserAgent, GetIconFileName, GetTitle, GetScreenDpi)
  • Added noexcept to all Invoke* methods
  • Moved _contextMenuEnabled / _zoomEnabled from public: to private:; updated Linux static callback to use the getter
  • Replaced C-style casts with static_cast / reinterpret_cast / const_cast across Mac, Windows and Linux files
  • Replaced remaining NULL occurrences with nullptr (Windows.cpp, Mac.UrlSchemeHandler.mm, Linux.Dialog.cpp)
  • Changed std::vector<Monitor*> with manual new/delete to std::vector<Monitor> by value (Mac)
  • Replaced strtok with thread-safe strtok_r in Linux dialog filter parsing
  • Removed redundant #ifndef/#define/#endif guards from InfiniFrame.Dialog.h and InfiniFrame.Windows.ToastHandler.h (kept #pragma once)

RAII / thread safety

  • GetDevToolsEnabled (Linux): removed stale cache mutation, now queries WebKit directly (compatible with const)
  • Mac GetPosition/SetPosition: eliminated delete loops after switching GetMonitors() to value semantics

C# adjustments

  • Converted using (var x = ...) blocks to modern using var declarations (in InfiniFrameWindow.cs)
  • Converted legacy [DllImport] methods to [LibraryImport] with [MarshalAs(UnmanagedType.I1)] (GetResizable, GetMaximized, GetMinimized, GetZoomEnabled)

Code Cleanup

  • Removed dead exit(0) after new operator
  • Removed unused escape_json function (Linux), replaced by nlohmann::json
  • Removed unused EnsureInvoke function (macOS)
  • Removed commented-out debug traces, MessageBox calls, PRINTF macros, paint code, Edit menu, SetPreference overloads
  • Removed self-include in header
  • Removed unused #include <cstdlib> from Exports.cpp
  • Replaced all NULL with nullptr in macOS code
  • Renamed local variable shadowing class name (Photino* Photino to InfiniFrame* instance in WindowProc)

CMake

  • Fixed OUTPUT_ARCH_DIR undefined on Linux (was producing empty path)
  • Separated test sources into TEST_SOURCES variable

Fixed Bugs

Native (Windows)

  • SetMaxSize: fixed copy-paste bug comparing currWidth instead of currHeight against _maxHeight
  • GetTopmost/SetTopmost: fixed checking GWL_STYLE instead of GWL_EXSTYLE
  • GetMaximized/GetMinimized/GetResizable: out-parameter now initialized on both true and false paths
  • SetFullScreen: removed duplicate SetPosition/SetSize calls

Native (Linux)

  • Constructor: _zoomEnabled now reads from initParams instead of hardcoded true
  • SetMaximized: removed incorrect _isFullScreen = maximized assignment
  • set_webkit_customsettings: fixed G_TYPE_BOOLEAN to G_TYPE_DOUBLE for float property values
  • on_permission_request: now checks _grantBrowserPermissions instead of always granting

Native (macOS)

  • GetMaximized: now uses [_window isZoomed] with fullscreen guard instead of checking NSWindowStyleMaskFullScreen
  • Destructor: removed [NSApp release] (shared singleton), added cleanup for leaked string fields
  • Constructor: _contextMenuEnabled/_zoomEnabled now read from initParams instead of hardcoded true
  • Custom scheme names: ownership transferred to _ownedCustomSchemeNames vector for proper cleanup
  • SetUserAgent: now copies string with new char[] + strcpy instead of raw pointer assignment
  • GetPosition/SetPosition: Monitor objects from GetMonitors() now freed after use

DragArea Double-Click Race Condition (#19)

  • Uncommented OnMaximized()/OnRestored() event calls in ToggleMaximized() so _suppressGrabbing flag is properly toggled
  • Added Detail >= 2 guard in OnPointerDown to skip the second pointerdown during a double-click sequence
  • Fixed OnPointerUp to always reset _isGrabbing and release pointer capture regardless of _suppressGrabbing state

Test Infrastructure Lifecycle (#24)

  • InfiniFrameWindowTestUtility.Dispose() now tracks and waits for the message loop Task before proceeding, preventing MessageLoopState leaks between tests
  • Removed stale [SkipOnWindows] from ContextMenu and DevTools tests (transport connection issue was caused by the lifecycle bug)
  • Fixed typo and improved skip reason messages in TransparentTests and ZoomTests

Related Issues

Checklist

  • My code follows the project's coding standards
  • I have commented my code, particularly in hard-to-understand areas
  • I have updated the documentation accordingly (marked as done because of current state of documentation)
  • My changes generate no new warnings
  • All tests pass locally
  • I have added tests that prove my fix/feature works (passing on existing tests)
  • New and existing unit tests pass locally
  • Any dependent changes have been merged and published

Rename
- Renamed all C++ source/header files from Photino.* to InfiniFrame.*
- Renamed C++ class Photino to InfiniFrame, struct PhotinoInitParams to InfiniFrameInitParams
- Renamed constructor/destructor on all platforms (InfiniFrame::InfiniFrame / ~InfiniFrame)
- Renamed all C exports from Photino_* to InfiniFrame_* (Exports.cpp, Exports.Tests.cpp)
- Renamed PhotinoDialog to InfiniFrameDialog
- Renamed JS interop channel from photinointerop to infiniFrameInterop
- Windows CLASS_NAME changed from L"Photino" to L"InfiniFrame"
- Updated CMakeLists.txt source file references
- Updated all C# P/Invoke entry points in NativeDll.cs and InfiniFrameNative.cs
- Updated Blazor component injection from PhotinoWindow to Window
- Updated comments across BlazorWebView, WebServer, Shared, Enums, FluentApi
- Updated Directory.Build.props author field
- Renamed delegate member variable photino to infiniFrame in Mac delegate headers

Bug fixes (Windows)
- SetMaxSize: fixed copy-paste bug comparing currWidth instead of currHeight against _maxHeight
- GetTopmost/SetTopmost: fixed checking GWL_STYLE instead of GWL_EXSTYLE
- GetMaximized/GetMinimized/GetResizable: out-parameter now initialized on both true and false paths
- SetFullScreen: removed duplicate SetPosition/SetSize calls

Bug fixes (Linux)
- Constructor: _zoomEnabled now reads from initParams instead of hardcoded true
- SetMaximized: removed incorrect _isFullScreen = maximized assignment
- set_webkit_customsettings: fixed G_TYPE_BOOLEAN to G_TYPE_DOUBLE for float property values
- on_permission_request: now checks _grantBrowserPermissions instead of always granting

Bug fixes (macOS)
- GetMaximized: now uses [_window isZoomed] with fullscreen guard instead of checking NSWindowStyleMaskFullScreen
- Destructor: removed [NSApp release] (shared singleton), added cleanup for leaked string fields
- Constructor: _contextMenuEnabled/_zoomEnabled now read from initParams instead of hardcoded true
- Custom scheme names: ownership transferred to _ownedCustomSchemeNames vector for proper cleanup
- SetUserAgent: now copies string with new char[] + strcpy instead of raw pointer assignment
- GetPosition/SetPosition: Monitor objects from GetMonitors() now freed after use

Code cleanup
- Removed dead exit(0) after new operator
- Removed unused escape_json function (Linux), replaced by nlohmann::json
- Removed unused EnsureInvoke function (macOS)
- Removed commented-out debug traces, MessageBox calls, PRINTF macros, paint code, Edit menu, SetPreference overloads
- Removed self-include in header
- Removed unused #include <cstdlib> from Exports.cpp
- Replaced all NULL with nullptr in macOS code
- Renamed local variable shadowing class name (Photino* Photino -> InfiniFrame* instance in WindowProc)

C# imports
- Converted legacy [DllImport] methods to [LibraryImport] with [MarshalAs(UnmanagedType.I1)] (GetResizable, GetMaximized, GetMinimized, GetZoomEnabled)

CMake
- Fixed OUTPUT_ARCH_DIR undefined on Linux (was producing empty path)
- Separated test sources into TEST_SOURCES variable
- ToggleMaximized events: Uncommented OnMaximized()/OnRestored() so _suppressGrabbing is properly toggled
- Double-click guard: Added Detail >= 2 check in OnPointerDown to skip the second pointerdown during double-click
- OnPointerUp cleanup: Always resets _isGrabbing and releases pointer capture regardless of _suppressGrabbing
- Test utility lifecycle: Dispose() now tracks and waits for the message loop Task before proceeding
- Skip messages: Removed stale SkipOnWindows from ContextMenu/DevTools tests, fixed typos and improved skip reason messages
C# improvements:
- MessageHandlers: Dictionary → ConcurrentDictionary for thread safety
- InvokeUtilities: Debug.Assert contract + XML documentation
- WebApp Thread: IsBackground = true for proper shutdown
- Max/Min Width/Height: backing fields with native SetMaxSize/SetMinSize sync

C++ modernization:
- CMake: move C++20 to C++23
- Header: NativeString typedef (std::string/std::wstring), std::unique_ptr for _dialog and _toastHandler, removed _ownedCustomSchemeNames vectors
- All platforms: new char[]/strcpy/delete[] to NativeString assignment, C-style casts to reinterpret_cast/static_cast/const_cast, std::make_unique for RAII, .empty() instead of nullptr checks
C++ (InfiniFrame.h + Linux/Mac/Windows implementations):
- Replace typedef function pointers with modern using aliases
- Add const qualifier to getter methods across all platforms
- Add [[nodiscard]] to key getters, noexcept to Invoke* methods
- Move _contextMenuEnabled/_zoomEnabled to private section
- Replace C-style casts with static_cast/reinterpret_cast/const_cast
- Replace NULL with nullptr in remaining locations
- Change vector<Monitor*> with new/delete to vector<Monitor> by value (Mac)
- Replace strtok with thread-safe strtok_r (Linux dialog)
- Remove redundant #ifndef/#define guards where #pragma once exists

C# (InfiniFrameWindow.cs):
- Modernize using statements to using declarations
@AnnaSasDev
Copy link
Member

AnnaSasDev commented Mar 9, 2026

In the latest updates, you have started to add a lot of braking API changes on the C# side, like the setters for properties for MaxHeight MinHeight MaxWidth MaxWidth. If you keep going with this branch, please split this PR into multiple smaller ones to keep reviewing on my side as easy as possible, else it will become too big to review by a singular person.

Thanks for all the good work so far though!

@AnnaSasDev
Copy link
Member

One extra thing, the "I have updated the documentation accordingly" can be ignored for now, unless it is inline documentation. There is currently no set documentation yet as this is still in preview

@freakdaniel
Copy link
Contributor Author

Otherwise, subsequent changes may be too extensive within the scope of this PR (I intend to remove numerous auto-variables from the code for complete memory management and replace the remaining C-like code). Therefore, it can be said that I am finished at this point. All that remains is to test it...

@freakdaniel freakdaniel marked this pull request as ready for review March 9, 2026 18:49
@freakdaniel freakdaniel requested a review from AnnaSasDev March 9, 2026 18:49
@freakdaniel freakdaniel changed the title [WIP] Complete branding in-code rename & fix native bugs Complete branding in-code rename & fix native bugs Mar 9, 2026
@AnnaSasDev
Copy link
Member

There seems to be an issue with the visibily of the checks, am resovling it in PR #80
The builds failed for MacOS and Windows environemnts, please check the githubs actions here https://github.com/InfiniLore/InfiniFrame/actions/runs/22869628130 to resolve the build errors

@freakdaniel
Copy link
Contributor Author

freakdaniel commented Mar 9, 2026

Problems arose due to the merger with the bump bot changes. From what I saw in the lastest pipeline:

  1. PR ymlUpdates was created when core was at commit d5c4a6c0 (Chore: Bump), which still contained the old code with Photino_ctor and Photino.Linux.cpp
  2. You merged 3b1f15f (my current branch) into core
  3. The CMake cache on CI was recreated from the new core, and Exports.cpp.o with InfiniFrame_ctor ended up there
  4. PR ran CI — refs/pull/80/merge merges ymlUpdates. In the old d5c4a6c0:
  • CMake cache is restored (with InfiniFrame_ctor object files)
  • Only changed files are compiled: Photino.Linux.cpp and Photino.Linux.Dialog.cpp
  • Exports.cpp.o from the cache is not recompiled (the filename is the same, CMake sees it as “up to date”)
  • Links: Exports.cpp.o (cached, exports InfiniFrame_ctor) + new .o
  • Result: .so does not have Photino_ctor
  1. C# test on old code searches for Photino_ctor and crashes with EntryPointNotFoundException

Rebase bugfix branch and problem will be solved

freakdaniel and others added 6 commits March 9, 2026 21:25
Rename
- Renamed all C++ source/header files from Photino.* to InfiniFrame.*
- Renamed C++ class Photino to InfiniFrame, struct PhotinoInitParams to InfiniFrameInitParams
- Renamed constructor/destructor on all platforms (InfiniFrame::InfiniFrame / ~InfiniFrame)
- Renamed all C exports from Photino_* to InfiniFrame_* (Exports.cpp, Exports.Tests.cpp)
- Renamed PhotinoDialog to InfiniFrameDialog
- Renamed JS interop channel from photinointerop to infiniFrameInterop
- Windows CLASS_NAME changed from L"Photino" to L"InfiniFrame"
- Updated CMakeLists.txt source file references
- Updated all C# P/Invoke entry points in NativeDll.cs and InfiniFrameNative.cs
- Updated Blazor component injection from PhotinoWindow to Window
- Updated comments across BlazorWebView, WebServer, Shared, Enums, FluentApi
- Updated Directory.Build.props author field
- Renamed delegate member variable photino to infiniFrame in Mac delegate headers

Bug fixes (Windows)
- SetMaxSize: fixed copy-paste bug comparing currWidth instead of currHeight against _maxHeight
- GetTopmost/SetTopmost: fixed checking GWL_STYLE instead of GWL_EXSTYLE
- GetMaximized/GetMinimized/GetResizable: out-parameter now initialized on both true and false paths
- SetFullScreen: removed duplicate SetPosition/SetSize calls

Bug fixes (Linux)
- Constructor: _zoomEnabled now reads from initParams instead of hardcoded true
- SetMaximized: removed incorrect _isFullScreen = maximized assignment
- set_webkit_customsettings: fixed G_TYPE_BOOLEAN to G_TYPE_DOUBLE for float property values
- on_permission_request: now checks _grantBrowserPermissions instead of always granting

Bug fixes (macOS)
- GetMaximized: now uses [_window isZoomed] with fullscreen guard instead of checking NSWindowStyleMaskFullScreen
- Destructor: removed [NSApp release] (shared singleton), added cleanup for leaked string fields
- Constructor: _contextMenuEnabled/_zoomEnabled now read from initParams instead of hardcoded true
- Custom scheme names: ownership transferred to _ownedCustomSchemeNames vector for proper cleanup
- SetUserAgent: now copies string with new char[] + strcpy instead of raw pointer assignment
- GetPosition/SetPosition: Monitor objects from GetMonitors() now freed after use

Code cleanup
- Removed dead exit(0) after new operator
- Removed unused escape_json function (Linux), replaced by nlohmann::json
- Removed unused EnsureInvoke function (macOS)
- Removed commented-out debug traces, MessageBox calls, PRINTF macros, paint code, Edit menu, SetPreference overloads
- Removed self-include in header
- Removed unused #include <cstdlib> from Exports.cpp
- Replaced all NULL with nullptr in macOS code
- Renamed local variable shadowing class name (Photino* Photino -> InfiniFrame* instance in WindowProc)

C# imports
- Converted legacy [DllImport] methods to [LibraryImport] with [MarshalAs(UnmanagedType.I1)] (GetResizable, GetMaximized, GetMinimized, GetZoomEnabled)

CMake
- Fixed OUTPUT_ARCH_DIR undefined on Linux (was producing empty path)
- Separated test sources into TEST_SOURCES variable
- ToggleMaximized events: Uncommented OnMaximized()/OnRestored() so _suppressGrabbing is properly toggled
- Double-click guard: Added Detail >= 2 check in OnPointerDown to skip the second pointerdown during double-click
- OnPointerUp cleanup: Always resets _isGrabbing and releases pointer capture regardless of _suppressGrabbing
- Test utility lifecycle: Dispose() now tracks and waits for the message loop Task before proceeding
- Skip messages: Removed stale SkipOnWindows from ContextMenu/DevTools tests, fixed typos and improved skip reason messages
C# improvements:
- MessageHandlers: Dictionary → ConcurrentDictionary for thread safety
- InvokeUtilities: Debug.Assert contract + XML documentation
- WebApp Thread: IsBackground = true for proper shutdown
- Max/Min Width/Height: backing fields with native SetMaxSize/SetMinSize sync

C++ modernization:
- CMake: move C++20 to C++23
- Header: NativeString typedef (std::string/std::wstring), std::unique_ptr for _dialog and _toastHandler, removed _ownedCustomSchemeNames vectors
- All platforms: new char[]/strcpy/delete[] to NativeString assignment, C-style casts to reinterpret_cast/static_cast/const_cast, std::make_unique for RAII, .empty() instead of nullptr checks
C++ (InfiniFrame.h + Linux/Mac/Windows implementations):
- Replace typedef function pointers with modern using aliases
- Add const qualifier to getter methods across all platforms
- Add [[nodiscard]] to key getters, noexcept to Invoke* methods
- Move _contextMenuEnabled/_zoomEnabled to private section
- Replace C-style casts with static_cast/reinterpret_cast/const_cast
- Replace NULL with nullptr in remaining locations
- Change vector<Monitor*> with new/delete to vector<Monitor> by value (Mac)
- Replace strtok with thread-safe strtok_r (Linux dialog)
- Remove redundant #ifndef/#define guards where #pragma once exists

C# (InfiniFrameWindow.cs):
- Modernize using statements to using declarations
@freakdaniel
Copy link
Contributor Author

freakdaniel commented Mar 9, 2026

Now i see real errors. Working on it :)

@freakdaniel
Copy link
Contributor Author

freakdaniel commented Mar 9, 2026

Try to run tests again please

- Set* methods now update cached fields before checking _webviewWindow,
  so Get* can return correct values even before WebView2 initializes
- Get* methods fall back to cached fields when _webviewWindow/_webviewController
  is null, preventing false negatives in FullIntegration tests
- SetTransparentEnabled/GetTransparentEnabled now guard against null controller
@freakdaniel
Copy link
Contributor Author

So let me explain the current problem in the assembler. After running Github Actions, I came to the following conclusion:

For some reason specifically for .NET 9.0 the GC collector is aggressive towards delegates

We have InfiniFrameNativeParameters, which are provided as struct, so during compilation with [MarshalAs(UnmanagedType.FunctionPtr)], the fields are recreated and GC doesn't see explicit linking in memory (again, not in all cases, only .NET 9.0 tests failed)

public struct InfiniFrameNativeParameters : IEquatable<InfiniFrameNativeParameters>

I added anchors in InfiniFrameWindow for parameters, which allows the GC to understand the explicit link

// InfiniFrameWindow.cs
//
// Keeps native callback delegates alive for the lifetime of the window
// Required because P/Invoke marshal-back of ref structs can replace delegate
// fields with new objects, leaving the originals eligible for GC
internal object[]? NativeCallbackAnchors;

The anchor for InfiniFrameWindow is filled with parameters from InfiniFrameWindowBuilder

// InfiniFrameWindowBuilder.cs
//
// Pin all native callback delegates so the GC cannot collect them after
// P/Invoke marshal-back replaces the struct fields with new delegate objects
window.NativeCallbackAnchors = [
     startupParameters.ClosingHandler!,
     startupParameters.ResizedHandler!,
     startupParameters.MaximizedHandler!,
     startupParameters.RestoredHandler!,
     startupParameters.MinimizedHandler!,
     startupParameters.MovedHandler!,
     startupParameters.FocusInHandler!,
     startupParameters.FocusOutHandler!,
     startupParameters.WebMessageReceivedHandler!,
     startupParameters.CustomSchemeHandler!,
];

In reality this solution is a compromise, and ideally here are like 3 methods to resolve this in more clear way:

  1. InfiniFrameNativeParameters should be converted from struct to class in the future. So then marshal-back will not recreate the fields, and GC will see a live reference to the class. But this affects the C++ side and requires more changes. In this case, the P/Invoke signature changes from ref to simple pass-by-value (since class is already a reference type)
// InfiniFrameNativeParameters.cs

// Before
[StructLayout(LayoutKind.Sequential)]
public struct InfiniFrameNativeParameters { ... }

// After
[StructLayout(LayoutKind.Sequential)]
public class InfiniFrameNativeParameters { ... }
// InfiniFrameNative.cs

// Before
internal static extern IntPtr Constructor(ref InfiniFrameNativeParameters parameters);

// After
internal static partial void Constructor(InfiniFrameNativeParameters parameters);
  1. Instead of object[], each delegate should be stored in its own field with the correct type
// InfiniFrameWindow.cs
//
// Explicit fields so GC sees direct references, no magic
private CppClosingDelegate? _nativeClosingHandler;
private CppFocusInDelegate? _nativeFocusInHandler;
private CppFocusOutDelegate? _nativeFocusOutHandler;
private CppResizedDelegate? _nativeResizedHandler;
private CppMaximizedDelegate? _nativeMaximizedHandler;
private CppRestoredDelegate? _nativeRestoredHandler;
private CppMinimizedDelegate? _nativeMinimizedHandler;
private CppMovedDelegate? _nativeMovedHandler;
private CppWebMessageReceivedDelegate?  _nativeWebMessageReceivedHandler;
private CppWebResourceRequestedDelegate? _nativeCustomSchemeHandler;
// InfiniFrameWindowBuilder.cs
//
// Create delegate objects explicitly and immediately save them to fields
window._nativeClosingHandler = Events.OnWindowClosing;
window._nativeFocusInHandler = Events.OnFocusIn;
window._nativeFocusOutHandler = Events.OnFocusOut;
window._nativeResizedHandler = Events.OnSizeChanged;
window._nativeMaximizedHandler = Events.OnMaximized;
window._nativeRestoredHandler = Events.OnRestored;
window._nativeMinimizedHandler = Events.OnMinimized;
window._nativeMovedHandler = Events.OnLocationChanged;
window._nativeWebMessageReceivedHandler = Events.OnWebMessageReceived;
window._nativeCustomSchemeHandler = window.OnCustomScheme;

// Fill in the struct from the saved fields
startupParameters.ClosingHandler = window._nativeClosingHandler;
startupParameters.FocusOutHandler = window._nativeFocusOutHandler;
// ...
window.StartupParameters = startupParameters;
  1. GCHandle.Alloc is an explicit runtime instruction: “do not touch this object.” Unlike object[] this is not just a reference so GC actually locks the object (this way requires disposing/closing for handlers)
// InfiniFrameWindow.cs

private GCHandle[] _delegateHandles = [];

// Called from Builder before Initialize()
internal void PinNativeCallbacks(params Delegate?[] delegates)
{
    _delegateHandles = delegates
        .Where(d => d is not null)
        .Select(d => GCHandle.Alloc(d!))
        .ToArray();
}

// MUST be in Dispose/Close otherwise there will be a memory leak
private void FreePinnedCallbacks()
{
    foreach (var handle in _delegateHandles)
        if (handle.IsAllocated)
            handle.Free();
    _delegateHandles = [];
}

public void Close() {
    FreePinnedCallbacks(); // <- must be before or after closing the native window
    // ...
}

@freakdaniel
Copy link
Contributor Author

LMAO while I was figuring it out, I found another solution where you can remove anchors...

Look at the difference between the two functions:

// InfiniFrameNative.cs
//
// Constructor ref WITHOUT [In]:
internal static extern IntPtr Constructor(ref InfiniFrameNativeParameters parameters);
// InfiniWindowNative.cs
//
// NativeParametersReturnAsIs ref WITH [In]:
private static extern void NativeParametersReturnAsIs(
    [In] ref InfiniFrameNativeParameters parameters, // <--- [In] is present!
    out IntPtr newParameters
);

In case of using [In] means something like: “only send data to natives and do not marshal back”
It is precisely because of the absence of [In] in constructor that the marshaler overwrites the struct fields after the call. Delegates are replaced with new objects and the old ones become garbage

NativeParametersReturnAsIs() with [In] is already protected from this problem and ref would not be needed with class either

So the simplest solution is to just add [In] In InfiniFrameNative

// InfiniFrameNative.cs

// Was:
internal static extern IntPtr Constructor(ref InfiniFrameNativeParameters parameters);

// Needed:
internal static extern IntPtr Constructor([In] ref InfiniFrameNativeParameters parameters);

C++ in Constructor does not write anything back to params it only reads from it when creating a window. This means that marshal-back is not needed at all, and [In] is semantically correct here and after that we can remove NativeCallbackAnchors from InfiniFrameWindow and InfiniFrameWindowBuilder which i added previously. They will be no longer needed

@freakdaniel
Copy link
Contributor Author

@AnnaSasDev AnnaSasDev merged commit 7330527 into InfiniLore:core Mar 10, 2026
7 checks passed
@AnnaSasDev
Copy link
Member

Thanks for the work on the PR!

@freakdaniel
Copy link
Contributor Author

As I said earlier... Thank you for all your hard work!

I will prepare the next PR soon. I am drawing up a plan. There are still a lot of compromises and assumptions in the code

@AnnaSasDev
Copy link
Member

As I said earlier... Thank you for all your hard work!

I will prepare the next PR soon. I am drawing up a plan. There are still a lot of compromises and assumptions in the code

absolutely!
Best to write out multiple Issues for them then, then we can discus together before you start work on a full PR to define things nicely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BugReport | DragArea has undefined behaviour when double clicking and returning from a maximized state

2 participants