I currently have a Unity3D program being embedded in a WPF page using a WindowsFormsHost. However, whenever scrolling on the page when not focused on the unity application, the exe still captures that input and does what the Unity exe is supppossed to do. Is there some way to distinguish between what is from when Unity is focused and when it's not?
I've tried looking in the process class to see if there was any methods or properties related to focus, to no avail. I've also tried looking in Unity's native classes to check for focus on the application before accepting input, again to no avail.
This is how I'm embedding the application in WPF
[DllImport("User32.dll")]
static extern bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw);
internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam);
[DllImport("user32.dll")]
internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);
[DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
private Process process;
private IntPtr unityHWND = IntPtr.Zero;
private const int WM_ACTIVATE = 0x0006;
private readonly IntPtr WA_ACTIVE = new IntPtr(1);
private readonly IntPtr WA_INACTIVE = new IntPtr(0);
private bool hasStartedOnce = false;
public UnityUserControl()
{
InitializeComponent();
try
{
StartProgram(this.Panel1.Handle.ToInt32());
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
public void StopUnity()
{
process.Kill();
this.Refresh();
}
public Int32 GetPanelHandle()
{
return this.Panel1.Handle.ToInt32();
}
public void StartProgram(Int32 panelHandleInt)
{
try
{
if (File.Exists("SamMaintenanceRackRobot.exe"))
{
process = new Process();
process.StartInfo.FileName = "SamMaintenanceRackRobot.exe";
process.StartInfo.Arguments =
"-parentHWND " + panelHandleInt + " " + Environment.CommandLine;
process.StartInfo.UseShellExecute = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
process.WaitForInputIdle();
Thread.Sleep(1000);
EnumChildWindows(Panel1.Handle, WindowEnum, IntPtr.Zero);
this.Load += UserControl1_Loaded;
hasStartedOnce = true;
}
}
void UserControl1_Loaded(object sender, EventArgs e)
{
Window window = Application.Current.MainWindow;
window.Closing += window_Closing;
}
void window_Closing(object sender, global::System.ComponentModel.CancelEventArgs e)
{
DeactivateUnityWindow();
}
private void ActivateUnityWindow()
{
SendMessage(unityHWND, WM_ACTIVATE, WA_ACTIVE, IntPtr.Zero);
}
private void DeactivateUnityWindow()
{
SendMessage(unityHWND, WM_ACTIVATE, WA_INACTIVE, IntPtr.Zero);
}
private int WindowEnum(IntPtr hwnd, IntPtr lparam)
{
unityHWND = hwnd;
ActivateUnityWindow();
return 0;
}
private void panel1_Resize(object sender, EventArgs e)
{
MoveWindow(unityHWND, 0, 0, Panel1.Width, Panel1.Height, true);
ActivateUnityWindow();
}
Related
I am working on a .net windows forms control where i have used the below code to use modify the caret in my custom textbox
[DllImport("User32.dll")]
static extern bool CreateCaret(IntPtr hWnd, IntPtr hBitmap, int nWidth, int nHeight);
[DllImport("User32.dll")]
static extern bool SetCaretPos(int x, int y);
[DllImport("User32.dll")]
static extern bool DestroyCaret();
[DllImport("User32.dll")]
static extern bool ShowCaret(IntPtr hWnd);
[DllImport("User32.dll")]
static extern bool HideCaret(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool SetCaretBlinkTime(uint uMSeconds);
protected override void OnGotFocus(System.EventArgs e)
{
CreateCaret(this.Handle, IntPtr.Zero, 8, 14);
SetCaretPos(2, 1);
ShowCaret(this.Handle);
SetCaretBlinkTime(100);
base.OnGotFocus(e);
}
protected override void OnLostFocus(EventArgs e)
{
DestroyCaret();
base.OnLostFocus(e);
}
but i found out that once i run the project , the blinking rate of my windows cursor have changed too till i restart my windows xp (sp2) .
any suggestion on why it is happening ?
To draw a caret by your self:
public class CueTextBox : TextBox
{
[DllImport("User32.dll")]
static extern bool GetCaretPos(out POINT pt);
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public UInt32 x;
public UInt32 y;
};
[DllImport("User32.dll")]
static extern bool DestroyCaret();
[DllImport("User32.dll")]
static extern bool HideCaret(IntPtr hWnd);
[DllImport("user32.dll")]
static extern uint GetCaretBlinkTime();
[DllImport("User32.dll")]
static extern bool SetTimer(IntPtr hWnd, IntPtr nIDEvent, uint uElapse, IntPtr lpTimerFunc);
[DllImport("User32.dll")]
static extern bool KillTimer(IntPtr hWnd, IntPtr nIDEvent);
[DllImport("User32.dll")]
static extern bool InvalidateRect(IntPtr hWnd, IntPtr lpRect, Int32 bErase);
private bool show = true;
protected override void OnGotFocus(System.EventArgs e)
{
HideCaret(this.Handle);
IntPtr nIDEvent = new IntPtr(1);
SetTimer(this.Handle, nIDEvent, 100, IntPtr.Zero);
base.OnGotFocus(e);
}
protected override void OnLostFocus(EventArgs e)
{
//SetCaretBlinkTime(530);
IntPtr nIDEvent = new IntPtr(1);
KillTimer(this.Handle, nIDEvent);
show = false;
base.OnLostFocus(e);
}
protected virtual void OnTimer(EventArgs e)
{
InvalidateRect(this.Handle, IntPtr.Zero, 0);
show = !show;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
POINT pt = new POINT();
GetCaretPos(out pt);
SolidBrush brush;
if (show)
{
brush = new SolidBrush(Color.Black);
e.Graphics.FillRectangle(brush, pt.x, pt.y, 8, 14);
}
}
const int WM_PAINT = 0x000F;
const int WM_SETFOCUS = 0x0007;
const int WM_KILLFOCUS = 0x0008;
const int WM_TIMER = 0x0113;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WM_PAINT)
{
this.OnPaint(new PaintEventArgs(Graphics.FromHwnd(m.HWnd), this.ClientRectangle));
}
if (m.Msg == WM_SETFOCUS)
{
this.OnGotFocus(new EventArgs());
}
if (m.Msg == WM_KILLFOCUS)
{
this.OnLostFocus(new EventArgs());
}
if (m.Msg == WM_TIMER)
{
this.OnTimer(new EventArgs());
}
}
}
Use SetTimer to set a "blinktime", and then InvalidateRect in the WM_TIMER to trigger WM_PAINT message, then simulate the blinking caret in OnPaint method.
I have a WPF application which I build as a dll and run from a COM class (MyComClass), developed in c#, as follows.
private void runDlg()
{
m_app = new Application();
m_app.ShutdownMode = ShutdownMode.OnExplicitShutdown;
m_appRunning = true;
m_app.Run();
}
public void Open()
{
if(m_appRunning && !m_windowOpen)
{
m_app.Dispatcher.Invoke(new Action( () => new EwokApp().Show() ) );
Thread.Sleep(cWaitPeriod1000_ms);
m_windowOpen = true;
}
}
I then pass messages from the COM class to the WPF application as follows
[DllImport("User32.dll", EntryPoint = "FindWindow")]
public static extern Int32 FindWindow(String lpClassName, String lpWindowName);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(int hWnd, int Msg, int wParam, int lParam);
public void Start()
{
if(m_windowOpen)
{
int hWnd = FindWindow(null, "MY-WPF-APP");
if(hWnd != 0)
{
m_msgHelper.sendMessage(hWnd, WM_START, 0, 0);
Thread.Sleep(cWaitPeriod2000_ms);
}
}
}
In my WPF application, I create a message handler as follows
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
source.AddHook(new HwndSourceHook(WndProc));
}
private static IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
// address the messages you are receiving using msg, wParam, lParam
if (msg == WM_START)
{
MyApp window = (MyApp)HwndSource.FromHwnd(hWnd).RootVisual;
window.start();
}
return IntPtr.Zero;
}
I can successfully post messages to the application from my COM class by obtaining a handler to the WPF window and passing it into the SendMessage function.
I would also like for the WPF application to obtain a handler to the COM class that created it and post messages to it. Can someone please advise how to do this.
Thanks
I'm using FlashWindowEx() to flash an application window when it needs to attract a user's attention. The window caption and taskbar button flashes continuously until the application receives focus. How can I check whether the application is currently flashing (i.e. has not received focus since it was instructed to flash).
Here are two possible solutions. One uses WH_SHELL, and one uses a NativeWindow. You will have to provide your own extension method (FlashWindow()) to start the flashing.
// base class. Two different forms subclass this form to illustrate two
// different solutions.
public class FormFlash : Form {
protected Label lb = new Label { Text = "Not flashing", Dock = DockStyle.Top };
public FormFlash() {
Controls.Add(lb);
Thread t = new Thread(() => {
Thread.Sleep(3000);
if (Form.ActiveForm == this)
SetForegroundWindow(GetDesktopWindow()); // deactivate the current form by setting the desktop as the foreground window
this.FlashWindow(); // call extension method to flash window
lb.BeginInvoke((Action) delegate {
lb.Text = "Flashing";
});
});
t.IsBackground = true;
t.Start();
}
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern IntPtr GetDesktopWindow();
}
// this solution is a bit simpler. Relies on the programmer knowing when the
// flashing started. Uses a NativeWindow to detect when a WM_ACTIVATEAPP
// message happens, that signals the end of the flashing.
class FormFlashNW : FormFlash {
NW nw = null;
public FormFlashNW() {
}
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
nw = new NW(this.Handle, lb);
}
protected override void OnHandleDestroyed(EventArgs e) {
base.OnHandleDestroyed(e);
nw.ReleaseHandle();
}
class NW : NativeWindow {
Label lb = null;
public NW(IntPtr handle, Label lb) {
AssignHandle(handle);
this.lb = lb;
}
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
const int WM_ACTIVATEAPP = 0x1C;
if (m.Msg == WM_ACTIVATEAPP) {
lb.BeginInvoke((Action) delegate {
lb.Text = "Not flashing";
});
}
}
}
}
// this solution is more complicated. Relies on setting up the hook proc.
// The 'isFlashing' bool fires true and false alternating while the flashing
// is active.
public class FormShellHook : FormFlash {
public FormShellHook() {
FlashWindowExListener.Register(this);
FlashWindowExListener.FlashEvent += FlashExListener_FlashEvent;
}
void FlashExListener_FlashEvent(Form f, bool isFlashing) {
if (f == this) {
lb.Text = DateTime.Now.ToLongTimeString() + " is flashing: " + isFlashing;
}
}
}
public class FlashWindowExListener {
private delegate IntPtr CallShellProc(int nCode, IntPtr wParam, IntPtr lParam);
private static CallShellProc procShell = new CallShellProc(ShellProc);
private static Dictionary<IntPtr,Form> forms = new Dictionary<IntPtr,Form>();
private static IntPtr hHook = IntPtr.Zero;
public static event FlashWindowExEventHandler FlashEvent = delegate {};
public delegate void FlashWindowExEventHandler(Form f, bool isFlashing);
static FlashWindowExListener() {
int processID = GetCurrentThreadId();
// we are interested in listening to WH_SHELL events, mainly the HSHELL_REDRAW event.
hHook = SetWindowsHookEx(WH_SHELL, procShell, IntPtr.Zero, processID);
System.Windows.Forms.Application.ApplicationExit += delegate {
UnhookWindowsHookEx(hHook);
};
}
public static void Register(Form f) {
if (f.IsDisposed)
throw new ArgumentException("Cannot use disposed form.");
if (f.Handle == IntPtr.Zero) {
f.HandleCreated += delegate {
forms[f.Handle] = f;
};
}
else
forms[f.Handle] = f;
f.HandleDestroyed += delegate {
Unregister(f);
};
}
public static void Unregister(Form f) {
forms.Remove(f.Handle);
}
private static IntPtr ShellProc(int nCode, IntPtr wParam, IntPtr lParam) {
if (nCode == HSHELL_REDRAW) {
Form f = null;
// seems OK not having to call f.BeginInvoke
if (forms.TryGetValue(wParam, out f))
FlashEvent(f, (int) lParam == 1);
}
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
private const int WH_SHELL = 10;
private const int HSHELL_REDRAW = 6;
[DllImport("user32.dll")]
private static extern int UnhookWindowsHookEx(IntPtr idHook);
[DllImport("user32.dll")]
private static extern IntPtr SetWindowsHookEx(int idHook, CallShellProc lpfn, IntPtr hInstance, int threadId);
[DllImport("kernel32.dll")]
private static extern int GetCurrentThreadId();
[DllImport("user32.dll")]
private static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam);
}
I Wrote a short program in C# that monitors the clipboard. When a certain string enters the clipboard, a program must be opened with Process.Start (depending on the string). Everything works fine, but sometimes the application is being opened twice. I Don't know why this is happening.
namespace ClipboardMonitor {
public class Form1 : System.Windows.Forms.Form {
[DllImport("User32.dll")]
protected static extern int SetClipboardViewer(int hWndNewViewer);
[DllImport("User32.dll", CharSet=CharSet.Auto)]
public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
const int SW_HIDE = 0;
const int SW_SHOW = 5;
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
IntPtr nextClipboardViewer;
private System.ComponentModel.Container components = null;
public Form1() {
InitializeComponent();
nextClipboardViewer = (IntPtr)SetClipboardViewer((int) this.Handle);
var handle = GetConsoleWindow();
ShowWindow(handle, SW_HIDE);
}
protected override void Dispose( bool disposing ) {
ChangeClipboardChain(this.Handle, nextClipboardViewer);
if( disposing ) {
if (components != null) {
components.Dispose();
}
}
base.Dispose( disposing );
}
private void InitializeComponent() {
this.WindowState = FormWindowState.Minimized;
this.ShowInTaskbar = false;
this.Name = "Form1";
}
[STAThread]
static void Main() {
Application.Run(new Form1());
}
protected override void WndProc(ref System.Windows.Forms.Message m) {
const int WM_DRAWCLIPBOARD = 0x308;
const int WM_CHANGECBCHAIN = 0x030D;
switch(m.Msg) {
case WM_DRAWCLIPBOARD:
DisplayClipboardData();
SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
break;
case WM_CHANGECBCHAIN:
if (m.WParam == nextClipboardViewer)
nextClipboardViewer = m.LParam;
else
SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
break;
default:
base.WndProc(ref m);
break;
}
}
void DisplayClipboardData() {
Thread.Sleep(500);
try {
IDataObject iData = new DataObject();
iData = Clipboard.GetDataObject();
if (iData.GetDataPresent(DataFormats.Text)) {
string path = iData.GetData(DataFormats.Text).ToString();
string[] words = path.Split('_');
if (words[0] == "startLO") {
ProcessStartInfo info = new ProcessStartInfo(words[1]);
Process p = Process.Start(info);
}
} else {
// We doen niets.
}
}
catch(Exception e) {
MessageBox.Show(e.ToString());
}
}
}
One explanation would be that there are multiple clipboard events happening in rapid succession. This is fairly common. You can defend against this with a "settle time" delay. i.e. instead of reacting right away, set a timer or create a thread to handle it "in a little while". As more events come in, keep deferring the settletime. when the settletime finally expires, the timer or thread is allowed to actually run your program.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can I bring my application window to the front?
I am having an issue with SwitchToThisWindow
using System;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace BringToFront
{
public partial class Form1 : Form
{
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(String className, String windowName);
[DllImport("user32.dll", SetLastError = true)]
static extern void SwitchToThisWindow(IntPtr hWnd, bool turnOn);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
IntPtr activeWindowHandle = GetForegroundWindow();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
try
{
if (!checkBox1.Checked)
bringToFront(comboBox1.SelectedItem.ToString());
else
timer1.Enabled = true;
}
catch
{
MessageBox.Show("Please choose a Process Name");
}
}
public static void bringToFront(string title)
{
IntPtr handle = FindWindow(null, title);
if (handle == IntPtr.Zero)
{
return;
}
SwitchToThisWindow(handle, true);
}
private void comboBox1_Click(object sender, EventArgs e)
{
comboBox1.Items.Clear();
Process[] process = Process.GetProcesses();
foreach (Process processes in process)
{
if (!String.IsNullOrEmpty(processes.MainWindowTitle))
comboBox1.Items.Add(processes.MainWindowTitle.ToString());
}
}
private void timer1_Tick(object sender, EventArgs e)
{
Process process = Process.GetCurrentProcess();
string title = process.ProcessName.ToString();
IntPtr handle = FindWindow(null, title);
if (activeWindowHandle != handle)
bringToFront(comboBox1.SelectedItem.ToString());
if (!checkBox1.Checked)
timer1.Enabled = false;
}
}
}
As you can see I'm trying to bring the process that is selected to the front and keep it in the front by doing a timer every 5 seconds and rebringing it to the front. It works perfectly when running the application through Microsoft Visual Studios, but when I run the program as a standalone, it works how every other function like this does and only makes it flash in taskbar instead of bringing it to the front.
Why are the permissions different and is there anyway to fix this?
Via the solution by #ReedCopsy here, I suggest to make the selected handle TopMost after you've switched to that window. Using this solution, no new app can become top over the selected window.
Add the following to your code:
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_SHOWWINDOW = 0x0040;
and change your bringToFront by adding a call to SetWindowPos:
public static void bringToFront(string title)
{
IntPtr handle = FindWindow(null, title);
if (handle == IntPtr.Zero)
{
return;
}
SwitchToThisWindow(handle, true);
// Call this way:
SetWindowPos(handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
}