Skip to main content

Breaking Changes vs Photino.NET

InfiniFrame is a complete, independent rework of Photino.NET and photino.native. It is not a drop-in replacement — this document covers every API, naming, and behavioral difference to assist with migration

Table of Contents

Package and Namespace

AspectPhotinoInfiniFrame
NuGet packagePhotino.NETInfiniLore.InfiniFrame
C# namespacePhotino.NETInfiniFrame
Native DLLPhotino.NativeInfiniFrame.Native (internal)
C++ classPhotinoInfiniFrameWindow
C++ init paramsPhotinoInitParamsInfiniFrameInitParams
Exported function prefixPhotino_InfiniFrame_
Default window title"Photino""InfiniFrame"
Default user agent"Photino WebView""InfiniFrame WebView"
Default temp path%LocalAppData%\Photino (Windows-only)%TEMP%\infiniframe (all platforms)

All type names that were prefixed Photino are now prefixed InfiniFrame. If you have any code targeting the native DLL directly via P/Invoke, all exported symbol names must be updated from Photino_* to InfiniFrame_*

Entry Point and Builder API

Photino — direct construction

var window = new PhotinoWindow()
.SetTitle("My App")
.SetDevToolsEnabled(true)
.Load(new Uri("https://example.com"));
window.WaitForClose();

PhotinoWindow is constructed directly. All configuration is done by calling methods on the object after construction. There is no separate builder class

InfiniFrame — explicit builder

IInfiniFrameWindow window = InfiniFrameWindowBuilder.Create()
.SetTitle("My App")
.SetDevToolsEnabled(true)
.SetStartUrl("https://example.com")
.Build();
window.WaitForClose();

Configuration must be set before calling Build(). The builder accepts an optional IServiceProvider for DI integration. The resulting type is IInfiniFrameWindow, not a concrete class

Configuration from appsettings.json

InfiniFrame supports sourcing window configuration from IConfiguration under an "InfiniFrame" key — not available in Photino:

{
"InfiniFrame": {
"Title": "My App",
"Width": 1280,
"Height": 720
}
}

Type and class changes

PhotinoInfiniFrameNotes
PhotinoWindow (monolithic)IInfiniFrameWindow (interface) + InfiniFrameWindow (concrete)Users interact through the interface
No builderInfiniFrameWindowBuilderSeparate builder class
No configuration typeInfiniFrameWindowConfigurationSeparates build-time config from runtime
PhotinoWindow(PhotinoWindow parent) constructorbuilder.SetParent(parentWindow)Parent passed through builder, not constructor

Runtime Window API

Method renames and removals

PhotinoInfiniFrameNotes
PhotinoWindow.Load(Uri) / Load(string)IInfiniFrameWindow.Load(Uri) / Load(string)Renamed and available at runtime; initial URL can also be set via SetStartUrl() in the builder
PhotinoWindow.LoadRawString(string)IInfiniFrameWindow.LoadRawString(string)Available at runtime; initial HTML can also be set via SetStartString() in the builder
PhotinoWindow.Center()IInfiniFrameWindow.Center() / CenterOnCurrentMonitor() / CenterOnMonitor(int)Available at runtime
PhotinoWindow.MoveTo(Point, bool) / Offset(Point)window.SetLocation(x, y) / window.Offset(x, y)
PhotinoWindow.SetMinHeight(int) / SetMinWidth(int)Removed — use SetMinSize(width, height)Consolidated
PhotinoWindow.SetMaxHeight(int) / SetMaxWidth(int)Removed — use SetMaxSize(width, height)Consolidated
PhotinoWindow.SetLogVerbosity(int)Removed — see Logging
PhotinoWindow.Win32SetWebView2Path(string)Internal — not on public interface
PhotinoWindow.MacOsVersion (static)Removed
PhotinoWindow.IsWindowsPlatform / IsMacOsPlatform / IsLinuxPlatform (static)Removed from public interface
PhotinoWindow.WaitForClose()IInfiniFrameWindow.WaitForClose() and WaitForCloseAsync()Async variant added
Monitor structInfiniMonitor recordType renamed. Collection changed from IReadOnlyList<Monitor> to ImmutableArray<InfiniMonitor>
PhotinoDialogButtons / PhotinoDialogResult / PhotinoDialogIconInfiniFrameDialogButtons / InfiniFrameDialogResult / InfiniFrameDialogIconRenamed enums
ShowSaveFile(title, defaultPath, filters, count)ShowSaveFile(title, defaultPath, filters, count, defaultFileName)Added defaultFileName parameter

New APIs not in Photino

APIDescription
IInfiniFrameWindow.FocusedQuery or set keyboard focus state
IInfiniFrameWindow.WaitForCloseAsync()Async wait for window close
IInfiniFrameWindow.ManagedThreadIdThread ID of the window's message loop
IInfiniFrameWindow.InstanceHandle / NativeTypeLow-level native access
IInfiniFrameWindow.CachedPreFullScreenBounds / CachedPreMaximizedBoundsSaved geometry for restore
RegisterCustomSchemeHandler()Returns IInfiniFrameWindow (fluent) — in Photino it returned void
ZoomEnabledSeparate bool controlling whether user can zoom, distinct from Zoom level

Event System

Photino uses standard .NET EventHandler<T> delegates. Assigning a new handler replaces the previous one:

// Photino — last assignment wins
window.RegisterWindowClosingHandler((sender, args) => { ... });

InfiniFrame uses InfiniFrameOrderedEvent<T> — an ordered multi-subscriber system with deterministic execution order. Handlers are added via .Add() and execute in registration order:

// InfiniFrame — all handlers run in order
window.Events.WindowClosing.Add((window, args) => { ... });
window.Events.WindowClosing.Add((window, args) => { ... }); // also runs

Closing event split

Photino has a single RegisterWindowClosingHandler. InfiniFrame splits this into two events:

EventTypeDescription
WindowClosingRequestedInfiniFrameOrderedClosingEventFired when close is requested and returning true from any handler cancels the close
WindowClosingInfiniFrameOrderedEventFired when the window is definitively closing and cannot be cancelled

Event handler DI injection

InfiniFrame event handlers support DI-resolved parameters when a IServiceProvider is provided to Build():

window.Events.WindowClosing.Add((MyService svc, IInfiniFrameWindow w) => { ... });

This is not available in Photino.

Web Messaging and Message Routing

Photino — single raw handler

window.RegisterWebMessageReceivedHandler((sender, message) => {
// message is the full raw string from JS
});

One handler. The full message string is passed as-is.

InfiniFrame — typed message routing

InfiniFrame uses a versioned JSON envelope protocol. Messages sent from JavaScript as {"id":"myEvent","data":"some data","version":1} are dispatched to the handler registered for "myEvent":

window.MessageHandlers.RegisterMessageHandler("myEvent", (window, payload) => {
// payload is "some data"
});

RegisterWebMessageReceivedHandler can still be used for raw message handling if needed, but protocol format is unchanged: JavaScript must send JSON envelopes.

The JavaScript side must use the envelope format:

window.infiniframe.host.postMessage({ id: "myEvent", data: "some data", version: 1 });

Legacy messageId;payload is out of support and not supported by InfiniFrame messaging contracts.

Logging

Photino — integer verbosity

window.SetLogVerbosity(2); // 0 = silent, higher = more verbose

Logs were written to Console.Out. Setting verbosity would itself log a message even when verbosity was 0 (known bug #257).

InfiniFrame — ILogger

The integer verbosity system is removed entirely. InfiniFrame integrates with Microsoft.Extensions.Logging:

var window = InfiniFrameWindowBuilder.Create()
.Build(serviceProvider); // ILogger<IInfiniFrameWindow> resolved from DI

Log output respects the configured logging provider and log-level filtering

Native C++ Interface

This section is only relevant if you extend InfiniFrame at the native level or have code that calls the native DLL directly

Pimpl architecture

Photino's Photino class exposes platform-specific fields (_hWnd, GtkWidget*, NSWindow*) directly in its class declaration, requiring platform headers to be included everywhere the class is used.

InfiniFrame uses the Pimpl idiom throughout: a struct Impl held by std::unique_ptr<Impl> is defined per-platform in the .cpp/.mm file. The InfiniFrameWindow.h header is entirely platform-agnostic

Build system and dependencies

AspectPhotinoInfiniFrame
Build systemVisual Studio .vcxproj + MakefileCMake 4.0
C++ standardC++17C++23
JSON libraryjson.hpp (nlohmann, bundled)simdjson via CMake FetchContent
String conversionCustom ToWide/ToUTF8String methodssimdutf via CMake FetchContent
FormattingNonestd::format (C++23 standard)
Sanitizers (Debug)NoneASan / UBSan / LeakSan enabled

Platform file layout

PhotinoInfiniFrame
Photino.Windows.cpp (all Windows code)Platform/Windows/Window.cpp
Photino.Linux.cpp (all Linux code)Platform/Linux/Window.cpp
Photino.Mac.mm (all macOS code)Platform/Mac/Window.mm + separate delegate files
macOS delegates inline in .mmAppDelegate, UiDelegate, WindowDelegate, NavigationDelegate, UrlSchemeHandler, NSWindowBorderless in separate files

P/Invoke generation

Photino uses manual [DllImport] attributes. InfiniFrame uses source-generated [LibraryImport] (requires .NET 7+):

// Photino
[DllImport("Photino.Native", ...)]
static extern void Photino_SetTitle(IntPtr instance, string title);

// InfiniFrame
[LibraryImport("InfiniFrame.Native", ...)]
static partial void InfiniFrame_SetTitle(IntPtr instance, ...);

String ownership

Photino has no explicit API for freeing native-allocated strings, which leads to memory leaks in long-running applications

InfiniFrame exports explicit free functions that must be called on any string returned from the native layer:

InfiniFrame_FreeString(ptr);
InfiniFrame_FreeStringArray(ptr, count);

These are called automatically by the managed wrapper — but if you call native exports directly, you are responsible for invoking them

SaveFileDialog signature change

The native SaveFileDialog export gained a defaultFileName parameter:

// Photino
Photino_ShowSaveFile(title, defaultPath, filters, count)

// InfiniFrame
InfiniFrame_ShowSaveFile(title, defaultPath, filters, count, defaultFileName)

Known Photino Issues Addressed

The following are open or previously reported issues in the Photino repositories that are resolved in InfiniFrame:

Photino IssueDescriptionHow InfiniFrame Addresses It
photino.native #173/174Custom scheme handlers completely broken on Windows (WebResourceRequested never fires)Rewritten registration path. Scheme handlers tested end-to-end in examples
photino.native #165Memory leak in SendWebMessage on WindowsExplicit InfiniFrame_FreeString ownership model. Pimpl isolates per-window native allocations
photino.native #158No way to programmatically focus a windowInfiniFrame_SetFocused / InfiniFrame_GetFocused exported. Exposed via IInfiniFrameWindow.Focused
photino.native #163UTF encoding bug in SetWebView2RuntimePath silently corrupts non-ASCII pathssimdutf used for all UTF-8 ↔ UTF-16 conversions on Windows
photino.native #141Stack overflow in WaitForExit on LinuxPer-window independent message loops. No shared global MessageLoopState lock
photino.NET #75RegisterWindowClosingHandler does not fire on LinuxClosing handler rewritten using GTK delete-event signal correctly
photino.NET #257SetLogVerbosity(0) still logs a messageInteger verbosity removed entirely. Replaced by ILogger<IInfiniFrameWindow>
photino.NET #232Custom scheme handlers break fetch/XHR (CORS interference)Scheme handler registration refactored. CORS headers handled correctly per platform
photino.native #175SetTopmost uses wrong Win32 style. null crash on LinuxFixed Win32 HWND_TOPMOST/HWND_NOTOPMOST usage. null guards added on Linux

Removed or Replaced Features

The following Photino features are not present in InfiniFrame:

FeatureNotes
SetMinHeight / SetMinWidth / SetMaxHeight / SetMaxWidth individual methodsConsolidated into SetMinSize(w, h) / SetMaxSize(w, h)
LogVerbosity integer systemReplaced by ILogger<IInfiniFrameWindow>
MacOsVersion static propertyRemoved
IsWindowsPlatform / IsMacOsPlatform / IsLinuxPlatform static propertiesInternal. Not on public interface
UseOsDefaultLocation / UseOsDefaultSize runtime propertiesBuilder / config time only
BrowserControlInitParameters runtime propertyBuilder / config time only
nlohmann/json.hpp bundled JSON headerReplaced by simdjson
fmt formatting libraryReplaced by std::format