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
- Entry Point and Builder API
- Runtime Window API
- Event System
- Web Messaging and Message Routing
- Logging
- Native C++ Interface
- Known Photino Issues Addressed
- Removed or Replaced Features
Package and Namespace
| Aspect | Photino | InfiniFrame |
|---|---|---|
| NuGet package | Photino.NET |
InfiniLore.InfiniFrame |
| C# namespace | Photino.NET |
InfiniFrame |
| Native DLL | Photino.Native |
InfiniFrame.Native (internal) |
| C++ class | Photino |
InfiniFrameWindow |
| C++ init params | PhotinoInitParams |
InfiniFrameInitParams |
| Exported function prefix | Photino_ |
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
| Photino | InfiniFrame | Notes |
|---|---|---|
PhotinoWindow (monolithic) |
IInfiniFrameWindow (interface) + InfiniFrameWindow (concrete) |
Users interact through the interface |
| No builder | InfiniFrameWindowBuilder |
Separate builder class |
| No configuration type | InfiniFrameWindowConfiguration |
Separates build-time config from runtime |
PhotinoWindow(PhotinoWindow parent) constructor |
builder.SetParent(parentWindow) |
Parent passed through builder, not constructor |
Runtime Window API
Method renames and removals
| Photino | InfiniFrame | Notes |
|---|---|---|
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 struct |
InfiniMonitor record |
Type renamed. Collection changed from IReadOnlyList<Monitor> to ImmutableArray<InfiniMonitor> |
PhotinoDialogButtons / PhotinoDialogResult / PhotinoDialogIcon |
InfiniFrameDialogButtons / InfiniFrameDialogResult / InfiniFrameDialogIcon |
Renamed enums |
ShowSaveFile(title, defaultPath, filters, count) |
ShowSaveFile(title, defaultPath, filters, count, defaultFileName) |
Added defaultFileName parameter |
New APIs not in Photino
| API | Description |
|---|---|
IInfiniFrameWindow.Focused |
Query or set keyboard focus state |
IInfiniFrameWindow.WaitForCloseAsync() |
Async wait for window close |
IInfiniFrameWindow.ManagedThreadId |
Thread ID of the window's message loop |
IInfiniFrameWindow.InstanceHandle / NativeType |
Low-level native access |
IInfiniFrameWindow.CachedPreFullScreenBounds / CachedPreMaximizedBounds |
Saved geometry for restore |
RegisterCustomSchemeHandler() |
Returns IInfiniFrameWindow (fluent) — in Photino it returned void |
ZoomEnabled |
Separate 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:
| Event | Type | Description |
|---|---|---|
WindowClosingRequested |
InfiniFrameOrderedClosingEvent |
Fired when close is requested and returning true from any handler cancels the close |
WindowClosing |
InfiniFrameOrderedEvent |
Fired 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 messageId;payload protocol. Messages sent from JavaScript as "myEvent;some data" are dispatched to the handler registered for "myEvent":
window.MessageHandlers.RegisterMessageHandler("myEvent", (window, payload) => {
// payload is "some data"
});
A fallback RegisterWebMessageReceivedHandler is still available for raw unrouted messages, but the typed routing is the primary pattern
The JavaScript side must use the messageId;payload format:
window.external.sendMessage("myEvent;some data");
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
| Aspect | Photino | InfiniFrame |
|---|---|---|
| Build system | Visual Studio .vcxproj + Makefile |
CMake 4.0 |
| C++ standard | C++17 | C++23 |
| JSON library | json.hpp (nlohmann, bundled) |
simdjson via CMake FetchContent |
| String conversion | Custom ToWide/ToUTF8String methods |
simdutf via CMake FetchContent |
| Formatting | None | std::format (C++23 standard) |
| Sanitizers (Debug) | None | ASan / UBSan / LeakSan enabled |
Platform file layout
| Photino | InfiniFrame |
|---|---|
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 .mm |
AppDelegate, 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 Issue | Description | How InfiniFrame Addresses It |
|---|---|---|
| photino.native #173/174 | Custom scheme handlers completely broken on Windows (WebResourceRequested never fires) | Rewritten registration path. Scheme handlers tested end-to-end in examples |
| photino.native #165 | Memory leak in SendWebMessage on Windows |
Explicit InfiniFrame_FreeString ownership model. Pimpl isolates per-window native allocations |
| photino.native #158 | No way to programmatically focus a window | InfiniFrame_SetFocused / InfiniFrame_GetFocused exported. Exposed via IInfiniFrameWindow.Focused |
| photino.native #163 | UTF encoding bug in SetWebView2RuntimePath silently corrupts non-ASCII paths |
simdutf used for all UTF-8 ↔ UTF-16 conversions on Windows |
| photino.native #141 | Stack overflow in WaitForExit on Linux |
Per-window independent message loops. No shared global MessageLoopState lock |
| photino.NET #75 | RegisterWindowClosingHandler does not fire on Linux |
Closing handler rewritten using GTK delete-event signal correctly |
| photino.NET #257 | SetLogVerbosity(0) still logs a message |
Integer verbosity removed entirely. Replaced by ILogger<IInfiniFrameWindow> |
| photino.NET #232 | Custom scheme handlers break fetch/XHR (CORS interference) |
Scheme handler registration refactored. CORS headers handled correctly per platform |
| photino.native #175 | SetTopmost uses wrong Win32 style. null crash on Linux |
Fixed Win32 HWND_TOPMOST/HWND_NOTOPMOST usage. null guards added on Linux |
Removed or Replaced Features
The following Photino features are not present in InfiniFrame:
| Feature | Notes |
|---|---|
SetMinHeight / SetMinWidth / SetMaxHeight / SetMaxWidth individual methods |
Consolidated into SetMinSize(w, h) / SetMaxSize(w, h) |
LogVerbosity integer system |
Replaced by ILogger<IInfiniFrameWindow> |
MacOsVersion static property |
Removed |
IsWindowsPlatform / IsMacOsPlatform / IsLinuxPlatform static properties |
Internal. Not on public interface |
UseOsDefaultLocation / UseOsDefaultSize runtime properties |
Builder / config time only |
BrowserControlInitParameters runtime property |
Builder / config time only |
nlohmann/json.hpp bundled JSON header |
Replaced by simdjson |
fmt formatting library |
Replaced by std::format |