I'm trying to create WPF GUI application to host an already exist QT GUI application as part of the main UI.
The QT application don't need to deal with mouse/keyboard input.
I've tried approach mentioned in this SO Post. Seems all those approach does not work for a QT application.
I don't know if it's a right thing to do, but that's what I used some times to embedd other apps (found on the internet):
public partial class MainWindow : Window
{
private Process _process;
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32")]
private static extern IntPtr SetParent(IntPtr hWnd, IntPtr hWndParent);
[DllImport("user32")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
private const int SWP_NOZORDER = 0x0004;
private const int SWP_NOACTIVATE = 0x0010;
private const int GWL_STYLE = -16;
private const int WS_CAPTION = 0x00C00000;
private const int WS_THICKFRAME = 0x00040000;
const string patran = "patran";
public MainWindow()
{
InitializeComponent();
Loaded += (s, e) => LaunchChildProcess();
}
private void LaunchChildProcess()
{
_process = Process.Start("/path/to/QtExecutable.exe");
_process.WaitForInputIdle();
var helper = new WindowInteropHelper(this);
SetParent(_process.MainWindowHandle, helper.Handle);
// remove control box
int style = GetWindowLong(_process.MainWindowHandle, GWL_STYLE);
style = style & ~WS_CAPTION & ~WS_THICKFRAME;
SetWindowLong(_process.MainWindowHandle, GWL_STYLE, style);
// resize embedded application & refresh
ResizeEmbeddedApp();
}
private void ResizeEmbeddedApp()
{
if (_process == null)
return;
SetWindowPos(_process.MainWindowHandle, IntPtr.Zero, 0, 0, (int)ActualWidth, (int)ActualHeight, SWP_NOZORDER | SWP_NOACTIVATE);
}
protected override Size MeasureOverride(Size availableSize)
{
Size size = base.MeasureOverride(availableSize);
ResizeEmbeddedApp();
return size;
}
Just modify this skeleton to your necessities.
Let me know if it works.
Yes. It is possible. A very simple way for your quick start without the effort of coding.
Check this link: Hosting EXE Applications in a WPF Window Application (http://www.codeproject.com/Tips/673701/Hosting-EXE-Applications-in-a-WPF-Window-Applicati). Download the project. Find "notepad.exe" in the project and replace it with the file name of your QT application. Just a remind: for the WPF exe to launch the QT application, you may need to take care of the environment variables required by QT.
What it looks like:
Related
As you can see in the image, I want to open a fixed sized WPF window at a location of Launch window (which is WinForms app). How do I make sure that the WPF windows about to open is placed such that it is fully visible on any side of that Launch window. The similar behaviour is there in Windows desktop's right click menu as if you click on extreme edge of the screen, it would open context menu at the left and if you are in middle of the screen, it would open either side.
I have tried few things already and also this SO answer also but still figuring out how to calculate Windows's bounds such that it is within visible area.
WPF determine element is visible on screen
The process would be:
Get the rectangle of the window using its Handle.
Get the handle of the monitor where the window locates.
Get the information (in particular, rectangle of working area) of the monitor using its Handle.
Calculate the direction of available space.
using System;
using System.Runtime.InteropServices;
public enum Direction { None, TopLeft, TopRight, BottomRight, BottomLeft }
public static class WindowHelper
{
public static Direction GetAvailableDirection(IntPtr windowHandle)
{
if (!GetWindowRect(windowHandle, out RECT buffer))
return Direction.None;
System.Drawing.Rectangle windowRect = buffer;
IntPtr monitorHandle = MonitorFromWindow(windowHandle, MONITOR_DEFAULTTO.MONITOR_DEFAULTTONULL);
if (monitorHandle == IntPtr.Zero)
return Direction.None;
MONITORINFO info = new() { cbSize = (uint)Marshal.SizeOf<MONITORINFO>() };
if (!GetMonitorInfo(monitorHandle, ref info))
return Direction.None;
System.Drawing.Rectangle workingAreaRect = info.rcWork;
bool isWindowAlignedTop = (windowRect.Top - workingAreaRect.Top) < (workingAreaRect.Bottom - windowRect.Bottom);
bool isWindowAlignedLeft = (windowRect.Left - workingAreaRect.Left) < (workingAreaRect.Right - windowRect.Right);
return (isWindowAlignedTop, isWindowAlignedLeft) switch
{
(true, true) => Direction.BottomRight,
(true, false) => Direction.BottomLeft,
(false, true) => Direction.TopRight,
(false, false) => Direction.TopLeft
};
}
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(
IntPtr hWnd,
out RECT lpRect);
[DllImport("User32.dll")]
private static extern IntPtr MonitorFromWindow(
IntPtr hwnd,
MONITOR_DEFAULTTO dwFlags);
private enum MONITOR_DEFAULTTO : uint
{
MONITOR_DEFAULTTONULL = 0x00000000,
MONITOR_DEFAULTTOPRIMARY = 0x00000001,
MONITOR_DEFAULTTONEAREST = 0x00000002,
}
[DllImport("User32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetMonitorInfo(
IntPtr hMonitor,
ref MONITORINFO lpmi);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct MONITORINFO
{
public uint cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public static implicit operator System.Drawing.Rectangle(RECT rect)
{
return new System.Drawing.Rectangle(
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top);
}
}
}
Please note that calling app must have an application manifest which includes DPI awareness for correct calculation.
I'm using HwndHost to embed an external application in my WPF window. I noticed on some Windows 7 machines, if an Aero Theme is selected and Desktop Composition is enabled, the external application starts, flickers on the screen for a split second and then it disappears. If I turn off Desktop Composition or use the basic theme, the application is embedded successfully inside the WPF window.
This is the code I use in a class derived from HwndHost:
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("USER32.DLL", SetLastError = true)]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
private const int GWL_STYLE = (-16);
private const int WS_CHILD = 0x40000000;
private const int WS_EX_APPWINDOW = 0x00040000;
[DllImport("user32.dll", SetLastError = true)]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
while (Process.MainWindowHandle == IntPtr.Zero)
{
Process.Refresh();
System.Threading.Thread.Sleep(10);
}
SetLastError(0);
var ret = SetWindowLong(Process.MainWindowHandle, GWL_STYLE, WS_CHILD);
int e1 = Marshal.GetLastWin32Error();
SetParent(Process.MainWindowHandle, hwndParent.Handle);
int e2 = Marshal.GetLastWin32Error();
ShowWindow(Process.MainWindowHandle, 0);
int e3 = Marshal.GetLastWin32Error();
return new HandleRef(this, Process.MainWindowHandle);
}
I don't get any windows errors when the issue occurs. The process starts from another window which injects it to my class. I've checked with task manager and the process runs but it's not visible inside my WPF window. Any thoughts?
Look at the system addin namespace for how you can use the native HWIND_PTR as a control.
You don't need to use all of the library to do the work.
https://learn.microsoft.com/en-us/dotnet/framework/wpf/app-development/wpf-add-ins-overview
Is there an API that would let me do it in http://teststack.github.com/White/?
I can't seem to find it.
Thanks
Pawel
I know this is a very old post. But I though can't hurt to update it. TestStack.White has these functions now:
//Takes a screenshot of the entire desktop, and saves it to disk
Desktop.TakeScreenshot("C:\\white-framework.png", System.Drawing.Imaging.ImageFormat.Png);
//Captures a screenshot of the entire desktop, and returns the bitmap
Bitmap bitmap = Desktop.CaptureScreenshot();
From looking through the code on GitHub, it doesn't appear to have an API for that (perhaps add it as a feature request?).
You can do it fairly simply yourself though using a combination of the Screen class and Graphics.CopyFromScreen. There are some examples of how to capture the screen or the active window in the answers to this question.
The White.Repository project actually records the flow of your test, with screenshots but it is not very well documented and is not released on NuGet yet (it will be soon).
Personally I use this class we have put together from a bunch of sources and forget where I got it originally. This captures modal dialogues and other things that many of the other implementations didn't capture for some reason.
/// <summary>
/// Provides functions to capture the entire screen, or a particular window, and save it to a file.
/// </summary>
public class ScreenCapture
{
[DllImport("gdi32.dll")]
static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, CopyPixelOperation rop);
[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr DeleteDC(IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr DeleteObject(IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
[DllImport("gdi32.dll")]
static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll")]
static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp);
[DllImport("user32.dll")]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr ptr);
public Bitmap CaptureScreenShot()
{
var sz = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Size;
var hDesk = GetDesktopWindow();
var hSrce = GetWindowDC(hDesk);
var hDest = CreateCompatibleDC(hSrce);
var hBmp = CreateCompatibleBitmap(hSrce, sz.Width, sz.Height);
var hOldBmp = SelectObject(hDest, hBmp);
BitBlt(hDest, 0, 0, sz.Width, sz.Height, hSrce, 0, 0, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
var bmp = Image.FromHbitmap(hBmp);
SelectObject(hDest, hOldBmp);
DeleteObject(hBmp);
DeleteDC(hDest);
ReleaseDC(hDesk, hSrce);
return bmp;
}
}
Then to consume
var sc = new ScreenCapture();
var bitmap = sc.CaptureScreenShot();
bitmap.Save(fileName + ".png"), ImageFormat.Png);
Using c# for a wpf application, if in Windows 7 touch is enabled in the control panel, a user by default can 'write' on an InkCanvas with a finger. I want to disable that and force stylus input only.
I'd like to know how to do it more than one way if possible: first by disabling touch on the InkCanvas, second by disabling it for a particular window, and third by disabling it for the entire application. A bonus fourth would be knowing how to turn touch on or off system-wide.
I have tried UnregisterTouchWindow, and I have tried setting Stylus.SetIsTouchFeedbackEnabled to false for the InkCanvas, but neither has worked.
Further digging helped me put together the following as a way to toggle touch on/off system-wide. If anyone knows how to accomplish this in any of the other 3 ways, I'd still appreciate those answers.
The basic steps are to check the current registry status, change it if necessary (and then refresh the system to recognize the change), and make note of the initial state to restore if needed on program exit.
Thanks to the posters at these two links for the education.
public MainWindow(){
InitializeComponent();
RegistryKey regKey = Registry.CurrentUser;
regKey = regKey.OpenSubKey(#"Software\Microsoft\Wisp\Touch", true);
string currKey = regKey.GetValue("TouchGate").ToString();
if (currKey == "1")
{
regKey.SetValue("TouchGate", 0x00000000);
User32Utils.Notify_SettingChange();
UserConfig.TGate = "1";
}
regKey.Close();
...
}
public static class UserConfig {
public static string TGate { get; set; }
...
}
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e){
...
if (UserConfig.TGate == "1")
{
RegistryKey regKey = Registry.CurrentUser;
regKey = regKey.OpenSubKey(#"Software\Microsoft\Wisp\Touch", true);
regKey.SetValue("TouchGate", 0x00000001);
User32Utils.Notify_SettingChange();
regKey.Close();
}
}
//------------------User32Utils.cs
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace (...)
{
internal class User32Utils
{
#region USER32 Options
static IntPtr HWND_BROADCAST = new IntPtr(0xffffL);
static IntPtr WM_SETTINGCHANGE = new IntPtr(0x1a);
#endregion
#region STRUCT
enum SendMessageTimeoutFlags : uint
{
SMTO_NORMAL = 0x0000,
SMTO_BLOCK = 0x0001,
SMTO_ABORTIFHUNG = 0x2,
SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
}
#endregion
#region Interop
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr SendMessageTimeout(IntPtr hWnd,
uint Msg,
UIntPtr wParam,
UIntPtr lParam,
SendMessageTimeoutFlags fuFlags,
uint uTimeout,
out UIntPtr lpdwResult);
#endregion
internal static void Notify_SettingChange()
{
UIntPtr result;
SendMessageTimeout(HWND_BROADCAST, (uint)WM_SETTINGCHANGE,
UIntPtr.Zero, UIntPtr.Zero,
SendMessageTimeoutFlags.SMTO_NORMAL, 1000, out result);
}
}
}
This code works in a windows forms application (it shows the preview) but not in a WPF application.
WMEncoder _encoder;
WMEncDataView _preview;
_encoder = new WMEncoder();
IWMEncSourceGroupCollection SrcGrpColl = _encoder.SourceGroupCollection;
IWMEncSourceGroup2 sourceGroup = (IWMEncSourceGroup2)SrcGrpColl.Add("SG_1");
IWMEncVideoSource2 videoDevice = (IWMEncVideoSource2)sourceGroup.AddSource(WMENC_SOURCE_TYPE.WMENC_VIDEO);
videoDevice.SetInput("Default_Video_Device", "Device", "");
IWMEncAudioSource audioDevice = (IWMEncAudioSource)sourceGroup.AddSource(WMENC_SOURCE_TYPE.WMENC_AUDIO);
audioDevice.SetInput("Default_Audio_Device", "Device", "");
IWMEncProfile2 profile = new WMEncProfile2();
profile.LoadFromFile("Recording.prx");
sourceGroup.set_Profile(profile);
_encoder.PrepareToEncode(true);
_preview = new WMEncDataView();
int lpreviewStream = videoDevice.PreviewCollection.Add(_preview);
_encoder.Start();
_preview.SetViewProperties(lpreviewStream, (int)windowsFormsHost1.Handle);
_preview.StartView(lpreviewStream);
I've tried to use the WindowsFormsHost control to get a handle to pass (as shown in the sample), but still no luck.
I've recently done something similar to integrate an existing DirectShow video component with a new WPF UI. There are a variety of ways to do it, but probably the easiest is to derive a new class from HwndHost. You then simply override a couple of methods, give the window handle to your preview stream and it should all just work. depending on your requirements you may need to handle a couple of Windows messages in your WndProc to handle display changes and redrawing when the video's not playing.
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace SOQuestion
{
public class VideoHost : HwndHost
{
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
IntPtr hwndHost = IntPtr.Zero;
int hostHeight = (int) this.ActualHeight;
int hostWidth = (int) this.ActualWidth;
hwndHost = CreateWindowEx(0, "static", "",
WS_CHILD | WS_VISIBLE,
0, 0,
hostHeight, hostWidth,
hwndParent.Handle,
(IntPtr)HOST_ID,
IntPtr.Zero,
0);
return new HandleRef(this, hwndHost);
}
protected override void DestroyWindowCore(HandleRef hwnd)
{
DestroyWindow(hwnd.Handle);
}
protected override void OnWindowPositionChanged(Rect rcBoundingBox)
{
base.OnWindowPositionChanged(rcBoundingBox);
_preview.SetViewProperties(lpreviewStream, (int)this.Handle);
}
protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
// Handle a couple of windows messages if required - see MSDN for details
return base.WndProc(hwnd, msg, wParam, lParam, ref handled);
}
[DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Auto)]
internal static extern IntPtr CreateWindowEx(int dwExStyle,
string lpszClassName,
string lpszWindowName,
int style,
int x, int y,
int width, int height,
IntPtr hwndParent,
IntPtr hMenu,
IntPtr hInst,
[MarshalAs(UnmanagedType.AsAny)] object pvParam);
[DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Auto)]
internal static extern bool DestroyWindow(IntPtr hwnd);
internal const int WS_CHILD = 0x40000000;
internal const int WS_VISIBLE = 0x10000000;
internal const int HOST_ID = 0x00000002;
}
}