255 lines
8.2 KiB
C#
255 lines
8.2 KiB
C#
// Copyright Epic Games, Inc. All Rights Reserved.
|
||
|
||
using EnvDTE80;
|
||
using Microsoft.VisualStudio.Shell;
|
||
using Microsoft.VisualStudio.Shell.Interop;
|
||
using System;
|
||
using System.ComponentModel.Design;
|
||
using System.Diagnostics;
|
||
using System.Diagnostics.CodeAnalysis;
|
||
using System.Runtime.InteropServices;
|
||
using System.Security;
|
||
using System.Threading.Tasks;
|
||
using System.Windows;
|
||
using System.Windows.Forms;
|
||
using System.Windows.Interop;
|
||
|
||
namespace UnrealVS
|
||
{
|
||
delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||
class UbaVisualizer
|
||
{
|
||
const int UbaVisualizerButtonID = 0x1338;
|
||
|
||
public UbaVisualizer()
|
||
{
|
||
// UbaVisualizerButton
|
||
var CommandID = new CommandID(GuidList.UnrealVSCmdSet, UbaVisualizerButtonID);
|
||
var UbaVisualizerButtonCommand = new MenuCommand(new EventHandler(UbaVisualizerButtonHandler), CommandID);
|
||
UnrealVSPackage.Instance.MenuCommandService.AddCommand(UbaVisualizerButtonCommand);
|
||
|
||
UnrealVSPackage.Instance.OnSolutionOpened += () => VisualizerControl?.HandleSolutionChanged();
|
||
UnrealVSPackage.Instance.OnSolutionClosed += () => VisualizerControl?.HandleSolutionChanged();
|
||
UnrealVSPackage.Instance.OnProjectLoaded += (p) => VisualizerControl?.HandleSolutionChanged();
|
||
UnrealVSPackage.Instance.OnProjectUnloading += (p) => VisualizerControl?.HandleSolutionChanged();
|
||
//UnrealVSPackage.Instance.OnDocumentActivated += (d) => VisualizerControl?.HandleDocumentActivated(d);
|
||
}
|
||
|
||
/// Called when 'UbaVisualizer' button is clicked
|
||
void UbaVisualizerButtonHandler(object Sender, EventArgs Args)
|
||
{
|
||
ThreadHelper.ThrowIfNotOnUIThread();
|
||
|
||
ToolWindowPane ToolWindow = UnrealVSPackage.Instance.FindToolWindow(typeof(UbaVisualizerWindow), 0, true);
|
||
if ((null == ToolWindow) || (null == ToolWindow.Frame))
|
||
{
|
||
throw new NotSupportedException(Resources.ToolWindowCreateError);
|
||
}
|
||
IVsWindowFrame ToolWindowFrame = (IVsWindowFrame)ToolWindow.Frame;
|
||
Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(ToolWindowFrame.Show());
|
||
}
|
||
|
||
UbaVisualizerWindowControl VisualizerControl
|
||
{
|
||
get
|
||
{
|
||
ToolWindowPane ToolWindow = UnrealVSPackage.Instance.FindToolWindow(typeof(UbaVisualizerWindow), 0, true);
|
||
if ((null == ToolWindow) || (null == ToolWindow.Frame))
|
||
return null;
|
||
var UbaVisualizer = (UbaVisualizerWindow)ToolWindow;
|
||
return (UbaVisualizerWindowControl)UbaVisualizer.Content;
|
||
}
|
||
}
|
||
}
|
||
|
||
[Guid("b205a03c-566f-4dc3-9e2f-99e2df765f1e")]
|
||
public class UbaVisualizerWindow : ToolWindowPane
|
||
{
|
||
public UbaVisualizerWindow() : base(null)
|
||
{
|
||
this.Caption = "UbaVisualizer";
|
||
|
||
this.Content = new UbaVisualizerWindowControl(this);
|
||
}
|
||
|
||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("", "VSTHRD010")]
|
||
protected override bool PreProcessMessage(ref Message m)
|
||
{
|
||
if (m.Msg == 0x0100)//WM_KEYDOWN)
|
||
{
|
||
if (m.WParam == new IntPtr(0x1B)) // VK_ESCAPE
|
||
{
|
||
//((FileBrowserWindowControl)this.Content).HandleEscape();
|
||
return true;
|
||
}
|
||
else if (m.WParam == new IntPtr(0x70)) // VK_F1
|
||
{
|
||
//((FileBrowserWindowControl)this.Content).HandleF1();
|
||
return true;
|
||
}
|
||
else if (m.WParam == new IntPtr(0x74)) // VK_F5
|
||
{
|
||
//((FileBrowserWindowControl)this.Content).HandleF5();
|
||
return true;
|
||
}
|
||
}
|
||
return base.PreProcessMessage(ref m);
|
||
}
|
||
}
|
||
|
||
[SuppressMessage("Microsoft.Usage", "CA2216:DisposableTypesShouldDeclareFinalizer")]
|
||
internal class UbaVisualizerHost : HwndHost
|
||
{
|
||
string VisualizerExe;
|
||
UbaVisualizerWindow Window;
|
||
IntPtr HwndVisualizer;
|
||
WndProc HwndProc;
|
||
IntPtr HwndStatic;
|
||
|
||
public UbaVisualizerHost(UbaVisualizerWindow window, string visualizerExe)
|
||
{
|
||
Window = window;
|
||
VisualizerExe = visualizerExe;
|
||
ClipToBounds = false;
|
||
}
|
||
|
||
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
|
||
{
|
||
HwndProc = new WndProc(CustomWndProc);
|
||
|
||
NativeMethods.WNDCLASSEX wc = new NativeMethods.WNDCLASSEX();
|
||
wc.cbSize = Marshal.SizeOf(typeof(NativeMethods.WNDCLASSEX));
|
||
wc.style = 0x0001 | 0x0002 | 0x0008; // CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
|
||
wc.hbrBackground = IntPtr.Zero;// (IntPtr)2; //Black background
|
||
wc.cbClsExtra = 0;
|
||
wc.cbWndExtra = 0;
|
||
wc.hInstance = Marshal.GetHINSTANCE(this.GetType().Module);
|
||
wc.hIcon = IntPtr.Zero;
|
||
wc.hCursor = NativeMethods.LoadCursorW(IntPtr.Zero, (IntPtr)32512); // IDC_ARROW
|
||
wc.lpszMenuName = null;
|
||
wc.lpszClassName = "UbaVisualizerWindow";
|
||
wc.lpfnWndProc = Marshal.GetFunctionPointerForDelegate(HwndProc);
|
||
wc.hIconSm = IntPtr.Zero;
|
||
NativeMethods.RegisterClassExW(ref wc);
|
||
|
||
int style = 0x50000000; // WS_CHILD | WS_VISIBLE
|
||
|
||
IntPtr hwndHost = NativeMethods.CreateWindowEx(0, "UbaVisualizerWindow", "", style, 0, 0, 1, 1, hwndParent.Handle, IntPtr.Zero, IntPtr.Zero, 0);
|
||
string hwndStr = hwndHost.ToString("X").ToLower();
|
||
|
||
ProcessStartInfo startInfo = new ProcessStartInfo(VisualizerExe);
|
||
startInfo.UseShellExecute = false;
|
||
startInfo.Arguments = $"-parent={hwndStr}";
|
||
Process.Start(startInfo);
|
||
|
||
return new HandleRef(this, hwndHost);
|
||
}
|
||
|
||
protected override void DestroyWindowCore(HandleRef hwnd)
|
||
{
|
||
NativeMethods.DestroyWindow(hwnd.Handle);
|
||
}
|
||
|
||
(double, double) GetDpiScale()
|
||
{
|
||
PresentationSource source = PresentationSource.FromVisual(this);
|
||
if (source == null)
|
||
{
|
||
return (1.0, 1.0);
|
||
}
|
||
var m = source.CompositionTarget.TransformToDevice;
|
||
return (m.M11, m.M22);
|
||
}
|
||
|
||
protected IntPtr CustomWndProc(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam)
|
||
{
|
||
bool hasVisualizer = false;
|
||
bool shouldResize = msg == 0x0005;
|
||
|
||
if (HwndVisualizer != IntPtr.Zero && NativeMethods.IsWindow(HwndVisualizer))
|
||
{
|
||
hasVisualizer = true;
|
||
|
||
if (msg == 0x0445) // Special UbaVisualizer message for mouse focus
|
||
{
|
||
//NativeMethods.SetWindowPos(hwnd, (IntPtr)(-1), 0, 0, 0, 0, 0x4000 | 0x0010 | 0x0002 | 0x0001);
|
||
//NativeMethods.SetWindowPos(1hwnd, (IntPtr)(-2), 0, 0, 0, 0, 0x4000 | 0x0010 | 0x0002 | 0x0001);
|
||
|
||
//if (!System.Windows.Application.Current.MainWindow.IsActive)
|
||
{
|
||
ThreadHelper.JoinableTaskFactory.Run(() =>
|
||
{
|
||
System.Windows.Application.Current.MainWindow.Topmost = true;
|
||
System.Windows.Application.Current.MainWindow.Topmost = false;
|
||
//System.Windows.Application.Current.MainWindow.Activate();
|
||
//var DTE = UnrealVSPackage.Instance.DTE;
|
||
//DTE.MainWindow.Activate();
|
||
|
||
//IVsWindowFrame Frame = (IVsWindowFrame)Window.Frame;
|
||
//Frame.ShowNoActivate();
|
||
//Focus();
|
||
//NativeMethods.SetFocus(HwndVisualizer);
|
||
return Task.CompletedTask;
|
||
});
|
||
}
|
||
//Focus();
|
||
//NativeMethods.SetActiveWindow(hwnd);
|
||
}
|
||
else if (msg == 0x0014) // WM_ERASEBKGND
|
||
{
|
||
return new IntPtr(1);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (msg == 0x0444) // Special UbaVisualizer message. Called when visualizer process has set this hwnd as parent
|
||
{
|
||
NativeMethods.DestroyWindow(HwndStatic);
|
||
HwndStatic = IntPtr.Zero;
|
||
HwndVisualizer = lParam;
|
||
hasVisualizer = true;
|
||
shouldResize = true;
|
||
}
|
||
else if (msg == 0x02E0) // WM_DPICHANGED
|
||
{
|
||
shouldResize = true;
|
||
}
|
||
else if (HwndVisualizer != IntPtr.Zero)
|
||
{
|
||
HwndVisualizer = IntPtr.Zero;
|
||
int style = 0x50000000; // WS_CHILD | WS_VISIBLE
|
||
string text = $"Waiting for UbaVisualizer process... (parent hwnd: {hwnd.ToString("X").ToLower()})";
|
||
(double scaleWidth, double scaleHeight) = GetDpiScale();
|
||
HwndStatic = NativeMethods.CreateWindowEx(0, "static", text, style, 0, 0, (int)(ActualWidth * scaleWidth), (int)(ActualHeight * scaleHeight), hwnd, IntPtr.Zero, IntPtr.Zero, 0);
|
||
}
|
||
}
|
||
|
||
IntPtr res = NativeMethods.DefWindowProc(hwnd, msg, wParam, lParam);
|
||
|
||
if (shouldResize)
|
||
{
|
||
uint flags = 0;
|
||
IntPtr HwndToResize = IntPtr.Zero;
|
||
|
||
if (hasVisualizer)
|
||
{
|
||
flags = 0x0040 | 0x4000; // SWP_SHOWWINDOW | SWP_ASYNCWINDOWPOS
|
||
HwndToResize = HwndVisualizer;
|
||
}
|
||
else if (HwndStatic != IntPtr.Zero)
|
||
{
|
||
flags = 0x0040;
|
||
HwndToResize = HwndStatic;
|
||
}
|
||
|
||
if (flags != 0)
|
||
{
|
||
(double scaleWidth, double scaleHeight) = GetDpiScale();
|
||
NativeMethods.SetWindowPos(HwndToResize, IntPtr.Zero, 0, 0, (int)(ActualWidth * scaleWidth), (int)(ActualHeight* scaleHeight), flags);
|
||
}
|
||
}
|
||
return res;
|
||
}
|
||
}
|
||
}
|