Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 49 additions & 7 deletions src/WinUIEx/HwndExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
using System;
using System.Runtime.CompilerServices;
using System.ComponentModel;
using System.Runtime.InteropServices;
using Microsoft.UI;
using Windows.ApplicationModel.DataTransfer;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.WindowsAndMessaging;
using Windows.Win32.Graphics.Gdi;
using WinRT;
using Microsoft.UI;
using Microsoft.UI.Xaml.Controls.Primitives;
using System.ComponentModel;
using Windows.Win32.UI.WindowsAndMessaging;

namespace WinUIEx
{
Expand Down Expand Up @@ -372,6 +370,29 @@ public static void SetLayeredWindowAttributes(IntPtr hWnd, byte chromaKeyRed, by
if (!PInvoke.SetLayeredWindowAttributes(handle, color, alpha, LAYERED_WINDOW_ATTRIBUTES_FLAGS.LWA_COLORKEY | LAYERED_WINDOW_ATTRIBUTES_FLAGS.LWA_ALPHA))
Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
}*/

private static readonly Guid dtmIid = new(0xa5caee9b, 0x8708, 0x49d1, 0x8d, 0x36, 0x67, 0xd2, 0x5a, 0x8d, 0xa0, 0x0c);

/// <summary>
/// Gets the DataTransferManager for the specified window handle
/// </summary>
/// <param name="hwnd">Window handle</param>
/// <returns>The DataTransferManager associated with the specified window handle.</returns>
public static DataTransferManager GetDataTransferManagerForWindow(IntPtr hwnd)
{
IDataTransferManagerInterop interop = DataTransferManager.As<IDataTransferManagerInterop>();
return WinRT.MarshalInterface<DataTransferManager>.FromAbi(interop.GetForWindow(hwnd, dtmIid));
}

/// <summary>
/// Displays the Windows Share UI for the specified window handle.
/// </summary>
/// <param name="hwnd">Window handle</param>
public static void ShowShareUIForWindow(IntPtr hwnd)
{
IDataTransferManagerInterop interop = DataTransferManager.As<IDataTransferManagerInterop>();
interop.ShowShareUIForWindow(hwnd);
}
}

/// <summary>
Expand Down Expand Up @@ -441,7 +462,7 @@ public enum WindowStyle : int
Visible = 0x10000000,
/// <summary>The window has a vertical scroll bar.</summary>
VScroll = 0x00200000,
}
}

/// <summary>
/// Flags used for ToggleExtendedWindowStyle method
Expand Down Expand Up @@ -512,4 +533,25 @@ public enum ExtendedWindowStyle : int
/// <summary>The window has a border with a raised edge.</summary>
WindowEdge = 0x00000100,
}

[System.Runtime.InteropServices.ComImport]
[System.Runtime.InteropServices.Guid("3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8")]
[System.Runtime.InteropServices.InterfaceType(
System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)]
interface IDataTransferManagerInterop
{
/// <summary>
/// Initializes the Share UI for the specified window.
/// </summary>
/// <param name="appWindow">The window for which to initialize the Share UI.</param>
/// <param name="riid">The interface ID of the IDataTransferManager interface.</param>
/// <returns>The IDataTransferManager interface for the specified window.</returns>
IntPtr GetForWindow([System.Runtime.InteropServices.In] IntPtr appWindow, [System.Runtime.InteropServices.In] ref Guid riid);

/// <summary>
/// Displays the Share UI for the specified window.
/// </summary>
/// <param name="appWindow">The window for which to display the Share UI.</param>
void ShowShareUIForWindow(IntPtr appWindow);
Comment thread
dotMorten marked this conversation as resolved.
}
}
38 changes: 36 additions & 2 deletions src/WinUIEx/WindowExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using Microsoft.UI;
using Microsoft.UI.Windowing;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.ApplicationModel.DataTransfer;
using Windows.Win32;

namespace WinUIEx
Expand Down Expand Up @@ -121,7 +123,7 @@ private static void UpdateOverlappedPresenter(this Microsoft.UI.Xaml.Window wind
var appwindow = window.AppWindow;
if (appwindow.Presenter is OverlappedPresenter overlapped)
action(overlapped);
else
else
throw new NotSupportedException($"Not supported with a {appwindow.Presenter.Kind} presenter");
}
private static T GetOverlappedPresenterValue<T>(this Microsoft.UI.Xaml.Window window, Func<OverlappedPresenter?,T> action)
Expand Down Expand Up @@ -361,7 +363,7 @@ public static void SetWindowChromaKey(this Microsoft.UI.Xaml.Window window, Wind
/// <param name="window">window</param>
/// <param name="chromaKey">The color that specifies the transparency color key to be used when composing the layered window. All pixels painted by the window in this color will be transparent.</param>
/// <param name="alpha">Alpha value used to describe the opacity of the layered window. When <paramref name="alpha"/> is 0, the window is completely transparent. When <paramref name="alpha"/> is 255, the window is opaque.</param>
public static void SetLayeredWindowAttributes(this Microsoft.UI.Xaml.Window window, Windows.UI.Color chromaKey, byte alpha)
public static void SetLayeredWindowAttributes(this Microsoft.UI.Xaml.Window window, Windows.UI.Color chromaKey, byte alpha)
=> HwndExtensions.SetLayeredWindowAttributes(GetWindowHandle(window), chromaKey.R, chromaKey.G, chromaKey.B, alpha);*/

/// <summary>
Expand All @@ -385,5 +387,37 @@ public static void SetRegion(this Microsoft.UI.Xaml.Window window, Region? regio
PInvoke.DeleteObject(rgn);
}
}

/// <summary>
/// Gets the <see cref="DataTransferManager"/> for the specified window.
/// </summary>
/// <param name="window">The window for which to get the <see cref="DataTransferManager"/>.</param>
/// <returns>The <see cref="DataTransferManager"/> associated with the specified window.</returns>
public static DataTransferManager GetDataTransferManager(this Microsoft.UI.Xaml.Window window) => HwndExtensions.GetDataTransferManagerForWindow(window.GetWindowHandle());

/// <summary>
/// Displays the Windows Share UI for the specified window.
/// </summary>
/// <param name="window">The window for which to display the Share UI.</param>
public static void ShowShareUI(this Microsoft.UI.Xaml.Window window) => HwndExtensions.ShowShareUIForWindow(window.GetWindowHandle());

/// <summary>
/// A convenience method that displays the Windows Share UI with the specified <see cref="DataPackage"/>.
/// </summary>
/// <param name="window">The window for which to display the Share UI.</param>
/// <param name="data">The <see cref="DataPackage"/> to share.</param>
public static void Share(this Microsoft.UI.Xaml.Window window, DataPackage data)
{
var dtm = window.GetDataTransferManager();

void handler(DataTransferManager sender, DataRequestedEventArgs args)
{
dtm.DataRequested -= handler;
args.Request.Data = data;
}

dtm.DataRequested += handler;
window.ShowShareUI();

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could there be a case here where the dtm/handler goes out of scope and gets garbage collected before the event fires?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that won't happen because we are marshalling a COM object, and it is stable per window. Also, please correct me if I'm wrong, since we are keeping a reference of dtm in handler, it should not be recycled before the handler runs?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. Is there a case where the handler won't run, and thus the handler is now leaking? Or could multiple calls to this method in parallel cause the handler to trigger multiple times?

@AkazaRenn AkazaRenn Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For leaking, I think it has a risk of happening, though I'm not certain whether or how it can be triggered. The COM wrapper dtm and handlsr will be referencing each other until the event is fired for once. However, once the event is finally fired, the unregistration happens and they both get recycled. So the damage should be minimal.

As for multiple calls, I think it's safe, since each handler is designed to run only once and unregister itself.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually we could probably just remove this helper function if you feel really worried. Don't feel like it is something that must be in this project.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would the datarequested call just not happen if the user cancels the dialog? There's also the chance that window.ShowShareUI throws and again would cause it to never unhook. I could imagine that happens with a stale/closed window instance.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it happens fast enough maybe? As for windows closed while there are uninvoked handlers, yeah if that happens it would be a leak without any way to recover... Might just should remove it in this case?

}
}
}
1 change: 1 addition & 0 deletions src/WinUIExSample/Pages/Dialogs.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<Button Content="Show dialog" Click="ShowDialog_Click" />
<Button Content="Show OK/Cancel" Click="ShowDialog2_Click" />
<Button Content="Show 3-option dialog" Click="ShowDialog3_Click" />
<Button Content="Show share sheet" Click="ShowShareSheet_Click" />
<TextBlock x:Name="resultText" />
</StackPanel>
</Grid>
Expand Down
10 changes: 10 additions & 0 deletions src/WinUIExSample/Pages/Dialogs.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,15 @@ private async void ShowDialog3_Click(object sender, RoutedEventArgs e)
resultText.Text = "You chose: " + result.Label;
}

private async void ShowShareSheet_Click(object sender, RoutedEventArgs e)
{
resultText.Text = "";
var data = new Windows.ApplicationModel.DataTransfer.DataPackage();
data.Properties.Title = "WinUIEx Sample Share";
data.SetUri(new Uri("https://github.com/dotMorten/WinUIEx"));
data.SetText("Hello from WinUIEx!");
MainWindow.Share(data);
}

}
}
Loading