CPF/CPF.Windows/WindowsPlatform.cs
2023-11-21 23:05:03 +08:00

524 lines
20 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using CPF;
using CPF.Drawing;
using CPF.Input;
using CPF.Platform;
using static CPF.Windows.UnmanagedMethods;
using System.Threading.Tasks;
using CPF.Controls;
using System.Linq;
namespace CPF.Windows
{
public class WindowsPlatform : RuntimePlatform
{
public override PixelPoint MousePosition
{
get
{
POINT pt = new POINT();
GetCursorPos(out pt);
return new PixelPoint(pt.X, pt.Y);
}
}
public override TimeSpan DoubleClickTime
{
get
{
return TimeSpan.FromSeconds(0.4);
}
}
public override IClipboard GetClipboard()
{
return new ClipboardImpl();
}
public override IPopupImpl CreatePopup()
{
return new PopupImpl();
}
//public override IViewImpl CreateView()
//{
// return new WindowImpl();
//}
public override IWindowImpl CreateWindow()
{
return new WindowImpl();
}
public override SynchronizationContext GetSynchronizationContext()
{
return new WindowsSynchronizationContext();
}
public override void Run()
{
MSG msg = new MSG();
while (Application.Main != null && GetMessage(ref msg, IntPtr.Zero, 0, 0))
{
CPF.Threading.DispatcherTimer.SetTimeTick();
TranslateMessage(ref msg);
DispatchMessage(ref msg);
}
}
public override void Run(CancellationToken cancellation)
{
MSG msg = new MSG();
while (Application.Main != null && !cancellation.IsCancellationRequested && GetMessage(ref msg, IntPtr.Zero, 0, 0))
{
CPF.Threading.DispatcherTimer.SetTimeTick();
TranslateMessage(ref msg);
DispatchMessage(ref msg);
}
}
internal static bool registerForMarshalling = true;
/// <summary>
/// 初始化Windows平台
/// </summary>
/// <param name="RegisterForMarshalling">net5以上才有用的为了兼容aot自动注册COM的。 如果你要使用Winform控件出现com问题你可以把这个改成false</param>
public WindowsPlatform(bool RegisterForMarshalling)
{
registerForMarshalling = RegisterForMarshalling;
#if !Net4
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
#endif
{
var w = WindowImpl.Window;
SetDpiAwareness();
}
}
public WindowsPlatform()
{
#if !Net4
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
#endif
{
var w = WindowImpl.Window;
SetDpiAwareness();
}
}
private static void SetDpiAwareness()
{
var user32 = LoadLibrary("user32.dll");
var method = GetProcAddress(user32, nameof(SetProcessDpiAwarenessContext));
if (method != IntPtr.Zero)
{
if (SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) ||
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE))
{
return;
}
}
var shcore = LoadLibrary("shcore.dll");
method = GetProcAddress(shcore, nameof(SetProcessDpiAwareness));
if (method != IntPtr.Zero)
{
SetProcessDpiAwareness(PROCESS_DPI_AWARENESS.PROCESS_PER_MONITOR_DPI_AWARE);
}
else
{
if (Environment.OSVersion.Version.Major > 5)
{
SetProcessDPIAware();
}
}
}
#if Net4
public override IList<Screen> GetAllScreen()
#else
public override IReadOnlyList<Screen> GetAllScreen()
#endif
{
//return new ScreenImpl(new CPF.Drawing.Rect(), new CPF.Drawing.Rect(), false, IntPtr.Zero).GetAllScreens();
var ScreenCount = GetSystemMetrics(SystemMetric.SM_CMONITORS);
List<Screen> screens = new List<Screen>();
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero,
(IntPtr monitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr data) =>
{
screens.Add(ScreenImpl.FromMonitor(monitor, hdcMonitor));
return true;
}, IntPtr.Zero);
return screens;
}
public override object GetCursor(Cursors cursorType)
{
IntPtr rv;
if (!Cache.TryGetValue(cursorType, out rv))
{
Cache[cursorType] = rv = UnmanagedMethods.LoadCursor(IntPtr.Zero, new IntPtr(CursorTypeMapping[cursorType]));
}
return rv;
}
private const UnmanagedMethods.FOS DefaultDialogOptions = UnmanagedMethods.FOS.FOS_FORCEFILESYSTEM | UnmanagedMethods.FOS.FOS_NOVALIDATE |
UnmanagedMethods.FOS.FOS_NOTESTFILECREATE | UnmanagedMethods.FOS.FOS_DONTADDTORECENT;
const int FILEBUFSIZE = 8192;
public override Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent)
{
var hWnd = (parent as WindowImpl)?.Handle ?? IntPtr.Zero;
return Task.Factory.StartNew(() =>
{
// var result = Array.Empty<string>();
var result = new string[0];
try
{
Guid clsid = dialog is OpenFileDialog ? ShellIds.OpenFileDialog : ShellIds.SaveFileDialog;
Guid iid = ShellIds.IFileDialog;
//兼容xp
#if Net4
CPF.Threading.Dispatcher.MainThread.Invoke(() =>
{
string filler = "";
if (dialog.Filters != null && dialog.Filters.Count > 0)
{
foreach (var item in dialog.Filters)
{
if (!string.IsNullOrWhiteSpace(item.Extensions))
{
filler += item.Name + "|*." + string.Join(";*.", item.Extensions.Split(',')) + "|";
}
}
filler = filler.TrimEnd('|');
}
if (dialog is OpenFileDialog open)
{
using (var openFileDialog = new System.Windows.Forms.OpenFileDialog
{
Multiselect = open.AllowMultiple,
Title = open.Title,
FileName = open.InitialFileName,
InitialDirectory = open.Directory,
Filter = filler
})
{
if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
result = openFileDialog.FileNames;
}
}
}
else if (dialog is SaveFileDialog save)
{
using (var saveFileDialog = new System.Windows.Forms.SaveFileDialog
{
Title = save.Title,
FileName = save.InitialFileName,
InitialDirectory = save.Directory,
Filter = filler
})
{
if (saveFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
result = new string[] { saveFileDialog.FileName };
}
}
}
});
#else
CPF.Threading.Dispatcher.MainThread.Invoke(() =>
{
CoInitializeEx(IntPtr.Zero, COINIT_APARTMENTTHREADED);
var r = CoCreateInstance(ref clsid, null, 1, ref iid, out var unk);
var frm = (IFileDialog)unk;
var openDialog = dialog as OpenFileDialog;
uint options;
frm.GetOptions(out options);
options |= (uint)(DefaultDialogOptions);
if (openDialog?.AllowMultiple == true)
options |= (uint)UnmanagedMethods.FOS.FOS_ALLOWMULTISELECT;
frm.SetOptions(options);
var defaultExtension = (dialog as SaveFileDialog)?.DefaultExtension ?? "";
frm.SetDefaultExtension(defaultExtension);
frm.SetFileName(dialog.InitialFileName ?? "");
if (!string.IsNullOrEmpty(dialog.Title))
{
frm.SetTitle(dialog.Title);
}
var filters = new List<COMDLG_FILTERSPEC>();
if (dialog.Filters != null)
{
foreach (var filter in dialog.Filters)
{
if (!string.IsNullOrWhiteSpace(filter.Extensions))
{
var extMask = string.Join(";", filter.Extensions.Split(',').Where(a => !string.IsNullOrWhiteSpace(a)).Select(e => "*." + e.Trim()));
filters.Add(new COMDLG_FILTERSPEC { pszName = filter.Name, pszSpec = extMask });
}
}
}
if (filters.Count == 0)
filters.Add(new COMDLG_FILTERSPEC { pszName = "All files", pszSpec = "*.*" });
frm.SetFileTypes((uint)filters.Count, filters.ToArray());
frm.SetFileTypeIndex(0);
if (dialog.Directory != null)
{
IShellItem directoryShellItem;
Guid riid = UnmanagedMethods.ShellIds.IShellItem;
if (UnmanagedMethods.SHCreateItemFromParsingName(dialog.Directory, IntPtr.Zero, ref riid, out directoryShellItem) == (uint)HRESULT.S_OK)
{
frm.SetFolder(directoryShellItem);
frm.SetDefaultFolder(directoryShellItem);
}
}
if (frm.Show(hWnd) == (uint)HRESULT.S_OK)
{
if (openDialog?.AllowMultiple == true)
{
IShellItemArray shellItemArray;
((IFileOpenDialog)frm).GetResults(out shellItemArray);
uint count;
shellItemArray.GetCount(out count);
result = new string[count];
for (uint i = 0; i < count; i++)
{
IShellItem shellItem;
shellItemArray.GetItemAt(i, out shellItem);
result[i] = GetAbsoluteFilePath(shellItem);
}
}
else
{
IShellItem shellItem;
if (frm.GetResult(out shellItem) == (uint)HRESULT.S_OK)
{
result = new string[] { GetAbsoluteFilePath(shellItem) };
}
}
}
});
#endif
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
return result;
});
}
public override Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent)
{
return Task.Factory.StartNew(() =>
{
string result = string.Empty;
var hWnd = (parent as WindowImpl)?.Handle ?? IntPtr.Zero;
Guid clsid = ShellIds.OpenFileDialog;
Guid iid = ShellIds.IFileDialog;
#if Net4
CPF.Threading.Dispatcher.MainThread.Invoke(() =>
{
using (var folderBrowser = new System.Windows.Forms.FolderBrowserDialog
{
SelectedPath = dialog.Directory,
Description = dialog.Title,
ShowNewFolderButton = true,
})
{
if (folderBrowser.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
result = folderBrowser.SelectedPath;
}
}
});
#else
CPF.Threading.Dispatcher.MainThread.Invoke(() =>
{
var r = CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out var unk);
var frm = (IFileDialog)unk;
uint options;
frm.GetOptions(out options);
options |= (uint)(UnmanagedMethods.FOS.FOS_PICKFOLDERS | DefaultDialogOptions);
frm.SetOptions(options);
if (!string.IsNullOrEmpty(dialog.Title))
{
frm.SetTitle(dialog.Title);
}
if (dialog.Directory != null)
{
IShellItem directoryShellItem;
Guid riid = UnmanagedMethods.ShellIds.IShellItem;
if (UnmanagedMethods.SHCreateItemFromParsingName(dialog.Directory, IntPtr.Zero, ref riid, out directoryShellItem) == (uint)HRESULT.S_OK)
{
frm.SetFolder(directoryShellItem);
}
}
if (dialog.Directory != null)
{
IShellItem directoryShellItem;
Guid riid = UnmanagedMethods.ShellIds.IShellItem;
if (UnmanagedMethods.SHCreateItemFromParsingName(dialog.Directory, IntPtr.Zero, ref riid, out directoryShellItem) == (uint)HRESULT.S_OK)
{
frm.SetDefaultFolder(directoryShellItem);
}
}
if (frm.Show(hWnd) == (uint)HRESULT.S_OK)
{
IShellItem shellItem;
if (frm.GetResult(out shellItem) == (uint)HRESULT.S_OK)
{
result = GetAbsoluteFilePath(shellItem);
}
}
});
#endif
return result;
});
}
private string GetAbsoluteFilePath(IShellItem shellItem)
{
IntPtr pszString;
if (shellItem.GetDisplayName(UnmanagedMethods.SIGDN_FILESYSPATH, out pszString) == (uint)HRESULT.S_OK)
{
if (pszString != IntPtr.Zero)
{
try
{
return Marshal.PtrToStringAuto(pszString);
}
finally
{
Marshal.FreeCoTaskMem(pszString);
}
}
}
return "";
}
static Dictionary<KeyGesture, PlatformHotkey> keyValuePairs = new Dictionary<KeyGesture, PlatformHotkey>() {
{ new KeyGesture(Keys.C,InputModifiers.Control),PlatformHotkey.Copy},
{ new KeyGesture(Keys.X,InputModifiers.Control),PlatformHotkey.Cut},
{ new KeyGesture(Keys.V,InputModifiers.Control),PlatformHotkey.Paste},
{ new KeyGesture(Keys.Y,InputModifiers.Control),PlatformHotkey.Redo},
{ new KeyGesture(Keys.A,InputModifiers.Control),PlatformHotkey.SelectAll},
{ new KeyGesture(Keys.Z,InputModifiers.Control),PlatformHotkey.Undo},
};
public override PlatformHotkey Hotkey(KeyGesture keyGesture)
{
keyValuePairs.TryGetValue(keyGesture, out PlatformHotkey platformHotkey);
return platformHotkey;
}
public override DragDropEffects DoDragDrop(DragDropEffects allowedEffects, params (DataFormat, object)[] data)
{
Threading.Dispatcher.MainThread.VerifyAccess();
try
{
OleDragSource src = new OleDragSource();
DataObject dataObject = new DataObject(data);
int allowed = (int)ConvertDropEffect(allowedEffects);
int[] finalEffect = new int[1];
UnmanagedMethods.DoDragDrop(dataObject, src, allowed, finalEffect);
return ConvertDropEffect((DropEffect)finalEffect[0]);
}
catch (Exception e)
{
Console.WriteLine(e);
System.Diagnostics.Debug.WriteLine(e);
}
return DragDropEffects.None;
}
public static DropEffect ConvertDropEffect(DragDropEffects operation)
{
DropEffect result = DropEffect.None;
if (operation.HasFlag(DragDropEffects.Copy))
result |= DropEffect.Copy;
if (operation.HasFlag(DragDropEffects.Move))
result |= DropEffect.Move;
if (operation.HasFlag(DragDropEffects.Link))
result |= DropEffect.Link;
return result;
}
public static DragDropEffects ConvertDropEffect(DropEffect effect)
{
DragDropEffects result = DragDropEffects.None;
if (effect.HasFlag(DropEffect.Copy))
result |= DragDropEffects.Copy;
if (effect.HasFlag(DropEffect.Move))
result |= DragDropEffects.Move;
if (effect.HasFlag(DropEffect.Link))
result |= DragDropEffects.Link;
return result;
}
public override INativeImpl CreateNative()
{
return new NativeHost();
}
public override INotifyIconImpl CreateNotifyIcon()
{
return new NotifyIcon();
}
private static readonly Dictionary<Cursors, IntPtr> Cache =
new Dictionary<Cursors, IntPtr>();
private static readonly Dictionary<Cursors, int> CursorTypeMapping = new Dictionary
<Cursors, int>
{
{Cursors.AppStarting, 32650},
{Cursors.Arrow, 32512},
{Cursors.Cross, 32515},
{Cursors.Hand, 32649},
{Cursors.Help, 32651},
{Cursors.Ibeam, 32513},
{Cursors.No, 32648},
{Cursors.SizeAll, 32646},
{Cursors.UpArrow, 32516},
{Cursors.SizeNorthSouth, 32645},
{Cursors.SizeWestEast, 32644},
{Cursors.Wait, 32514},
//Same as SizeNorthSouth
{Cursors.TopSide, 32645},
{Cursors.BottomSide, 32645},
//Same as SizeWestEast
{Cursors.LeftSide, 32644},
{Cursors.RightSide, 32644},
//Using SizeNorthWestSouthEast
{Cursors.TopLeftCorner, 32642},
{Cursors.BottomRightCorner, 32642},
//Using SizeNorthEastSouthWest
{Cursors.TopRightCorner, 32643},
{Cursors.BottomLeftCorner, 32643},
// Fallback, should have been loaded from ole32.dll
{Cursors.DragMove, 32516},
{Cursors.DragCopy, 32516},
{Cursors.DragLink, 32516},
};
}
}