Hello everyone,
I’m currently working on a Unity project where I’m trying to call the Windows 10/11 Share UI dialog. My plan is to create a bridge DLL to interface between Unity and Windows, specifically targeting Windows 10 and 11. I’ve been experimenting with WPF for this task, and I was able to successfully call the modern Windows Share API using Windows App SDK support.
I found helpful information from this Stack Overflow discussion: How to call up Windows 10 share dialog box from PowerShell or CMD and https://github.com/andrewleader/WindowsAppSDKGallery. I managed to get it working, as shown in the image below:
The plan is to use the DLL along with PowerShell or CMD to pass the necessary information to Windows, triggering the Share UI. I came across this GitHub project, Unity-ToastSender, which implements a similar bridge, but it only works for Toast Notifications.
Has anyone tried something similar or has knowledge about calling the Share UI dialog from Unity via a bridge DLL? Any help or direction would be greatly appreciated!
WPF Side Code:
using Microsoft.Windows.AppNotifications;
using Microsoft.Windows.AppNotifications.Builder;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using Windows.ApplicationModel.DataTransfer;
using Windows.Storage;
using Windows.Storage.Streams;
using WinRT;
namespace WindowsShareCall
{
public partial class MainWindow : System.Windows.Window
{
private static DataTransferManager _dataTransferManager;
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// Initialize the DataTransferManager
if (_dataTransferManager == null)
{
IntPtr hwnd = WindowHelper.GetHwndForCurrentWindow();
if (hwnd != IntPtr.Zero)
{
_dataTransferManager = DataTransferManagerHelper.GetForWindow(hwnd);
_dataTransferManager.DataRequested += _dataTransferManager_DataRequested;
}
else
{
MessageBox.Show("Failed to get window handle.");
}
}
}
private void ButtonToastNotification_Click(object sender, RoutedEventArgs e)
{
ShowToastNotification();
}
private void ButtonShareContent_Click(object sender, RoutedEventArgs e)
{
IntPtr hwnd = WindowHelper.GetHwndForCurrentWindow();
if (hwnd != IntPtr.Zero)
{
DataTransferManagerHelper.ShowShareUIForWindow(hwnd);
}
else
{
MessageBox.Show("Failed to get window handle.");
}
}
// Display a toast notification
private void ShowToastNotification()
{
var toast = new AppNotificationBuilder()
.AddText("Hello from Toast Notification!")
.AddText("This is an improved message.")
.BuildNotification();
AppNotificationManager.Default.Show(toast);
}
private async void _dataTransferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
DataRequest request = args.Request;
request.Data.Properties.Title = "Share Image";
request.Data.Properties.Description = "Sharing an image.";
request.Data.Properties.ApplicationName = "WindowsShareCall";
// Add text to ensure Social Media appears in the share options
request.Data.SetText("Check out this image!");
try
{
// Set the image content
string imagePath = "photo.png"; // Update path as needed
if (File.Exists(imagePath))
{
// Get the file and create a stream reference
StorageFile imageFile = await StorageFile.GetFileFromPathAsync(imagePath);
RandomAccessStreamReference imageStreamRef = RandomAccessStreamReference.CreateFromFile(imageFile);
// Set the image as the bitmap
request.Data.SetBitmap(imageStreamRef);
// Explicitly add the file to the request
request.Data.SetStorageItems(new[] { imageFile });
}
else
{
request.FailWithDisplayText("Image file not found.");
}
}
catch (Exception ex)
{
request.FailWithDisplayText($"Failed to add image: {ex.Message}");
}
}
}
public static class DataTransferManagerHelper
{
static readonly Guid _dtm_iid = new Guid(0xa5caee9b, 0x8708, 0x49d1, 0x8d, 0x36, 0x67, 0xd2, 0x5a, 0x8d, 0xa0, 0x0c);
static IDataTransferManagerInterop DataTransferManagerInterop => DataTransferManager.As<IDataTransferManagerInterop>();
public static DataTransferManager GetForWindow(IntPtr hwnd)
{
IntPtr result = DataTransferManagerInterop.GetForWindow(hwnd, _dtm_iid);
return MarshalInterface<DataTransferManager>.FromAbi(result);
}
public static void ShowShareUIForWindow(IntPtr hwnd)
{
DataTransferManagerInterop.ShowShareUIForWindow(hwnd);
}
[ComImport]
[Guid("3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDataTransferManagerInterop
{
IntPtr GetForWindow([In] IntPtr appWindow, [In] ref Guid riid);
void ShowShareUIForWindow(IntPtr appWindow);
}
}
public static class WindowHelper
{
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetForegroundWindow(IntPtr hWnd);
public static IntPtr GetHwndForCurrentWindow()
{
return new System.Windows.Interop.WindowInteropHelper(Application.Current.MainWindow).Handle;
}
}
}
DLL WPF Code:
using Microsoft.Windows.AppNotifications;
using Microsoft.Windows.AppNotifications.Builder;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using Windows.ApplicationModel.DataTransfer;
using Windows.Storage;
using Windows.Storage.Streams;
using WinRT;
namespace ShareUIHelper
{
public static class Class1
{
private static DataTransferManager _dataTransferManager;
public static void InitializeShareUI()
{
IntPtr hwnd = WindowHelper.GetHwndForCurrentWindow();
if (hwnd != IntPtr.Zero)
{
_dataTransferManager = DataTransferManagerHelper.GetForWindow(hwnd);
_dataTransferManager.DataRequested += _dataTransferManager_DataRequested;
}
else
{
MessageBox.Show("Failed to get window handle.");
}
}
public static void ShowShareUI()
{
IntPtr hwnd = WindowHelper.GetHwndForCurrentWindow();
if (hwnd != IntPtr.Zero)
{
DataTransferManagerHelper.ShowShareUIForWindow(hwnd);
}
else
{
MessageBox.Show("Failed to get window handle.");
}
}
private static async void _dataTransferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
DataRequest request = args.Request;
request.Data.Properties.Title = "Share Image";
request.Data.Properties.Description = "Sharing an image.";
request.Data.Properties.ApplicationName = "WindowsShareCall";
// Add text to ensure Social Media appears in the share options
request.Data.SetText("Check out this image!");
try
{
// Set the image content
string imagePath = "photo.png"; // Update path as needed
if (File.Exists(imagePath))
{
// Get the file and create a stream reference
StorageFile imageFile = await StorageFile.GetFileFromPathAsync(imagePath);
RandomAccessStreamReference imageStreamRef = RandomAccessStreamReference.CreateFromFile(imageFile);
// Set the image as the bitmap
request.Data.SetBitmap(imageStreamRef);
// Explicitly add the file to the request
request.Data.SetStorageItems(new[] { imageFile });
}
else
{
request.FailWithDisplayText("Image file not found.");
}
}
catch (Exception ex)
{
request.FailWithDisplayText($"Failed to add image: {ex.Message}");
}
}
}
public static class DataTransferManagerHelper
{
static readonly Guid _dtm_iid = new Guid(0xa5caee9b, 0x8708, 0x49d1, 0x8d, 0x36, 0x67, 0xd2, 0x5a, 0x8d, 0xa0, 0x0c);
static IDataTransferManagerInterop DataTransferManagerInterop => DataTransferManager.As<IDataTransferManagerInterop>();
public static DataTransferManager GetForWindow(IntPtr hwnd)
{
IntPtr result = DataTransferManagerInterop.GetForWindow(hwnd, _dtm_iid);
return MarshalInterface<DataTransferManager>.FromAbi(result);
}
public static void ShowShareUIForWindow(IntPtr hwnd)
{
DataTransferManagerInterop.ShowShareUIForWindow(hwnd);
}
[ComImport]
[Guid("3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDataTransferManagerInterop
{
IntPtr GetForWindow([In] IntPtr appWindow, [In] ref Guid riid);
void ShowShareUIForWindow(IntPtr appWindow);
}
}
public static class WindowHelper
{
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetForegroundWindow(IntPtr hWnd);
public static IntPtr GetHwndForCurrentWindow()
{
return new System.Windows.Interop.WindowInteropHelper(Application.Current.MainWindow).Handle;
}
}
}
Unity Side:
using System.Runtime.InteropServices;
using UnityEngine;
public class ShareDialogDummy : MonoBehaviour
{
#if UNITY_STANDALONE_WIN
[DllImport("WindowsShareCall.dll", EntryPoint = "InitializeShareUI")]
public static extern void InitializeShareUI();
[DllImport("WindowsShareCall.dll", EntryPoint = "ShowShareUI")]
public static extern void ShowShareUI();
#endif
void Start()
{
#if UNITY_STANDALONE_WIN
InitializeShareUI();
#endif
}
public void OnShareButtonClicked()
{
#if UNITY_STANDALONE_WIN
ShowShareUI();
#endif
}
}