Calling Windows ShareUI Dialog from Unity on Windows 10-11 on Non-UWP Build Target

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
	}
}