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;
///
/// 初始化Windows平台
///
/// net5以上才有用的,为了兼容aot,自动注册COM的。 如果你要使用Winform控件出现com问题,你可以把这个改成false
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 GetAllScreen()
#else
public override IReadOnlyList GetAllScreen()
#endif
{
//return new ScreenImpl(new CPF.Drawing.Rect(), new CPF.Drawing.Rect(), false, IntPtr.Zero).GetAllScreens();
var ScreenCount = GetSystemMetrics(SystemMetric.SM_CMONITORS);
List screens = new List();
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 ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent)
{
var hWnd = (parent as WindowImpl)?.Handle ?? IntPtr.Zero;
return Task.Factory.StartNew(() =>
{
// var result = Array.Empty();
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();
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 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 keyValuePairs = new Dictionary() {
{ 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 Cache =
new Dictionary();
private static readonly Dictionary CursorTypeMapping = new Dictionary
{
{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},
};
}
}