I'm working in Visual Studio 2015, in C # and WPF technology. I need to embed to a window of my program, another window system of a third party system, such as Notepad.
I found the answer here (sorry the site is in spanish) but it only works with Windows Form.
This is my code.
//get the third party window
public static IntPtr getWindow(string titleName)
{
Process[] pros = Process.GetProcesses(".");
foreach (Process p in pros)
if (p.MainWindowTitle.ToUpper().Contains(titleName.ToUpper()))
return p.MainWindowHandle;
return new IntPtr();
}
//Get my own window
IntPtr ptr = new WindowInteropHelper(this).Handle;
//a window embedded within the other
[DllImport("user32.dll")]
public extern static IntPtr SetParent(IntPtr hWnChild, IntPtr hWndNewParent);
As I said, that works for Windows Forms, but in WPF doesn't work.
Here is the code, the problem was that you need to set position, width, height and repaint the child window in the new parent window.
public void CapturarApp()
{
hWndApp = ScreenFuntion.getWindow("Notepad");
if (hWndApp.ToInt32() > 0)
{
ProgramsEncrustForm.MoveWindow(hWndApp,
0, 0,
Int32.Parse(Width.ToString()),
Int32.Parse(Height.ToString()), 1);
ProgramsEncrustForm.SetParent(hWndApp, new WindowInteropHelper(this).Handle);
}
else
{
hWndApp = IntPtr.Zero;
}
this.Show();
}
And here is the method to move and repaint the window
[System.Runtime.InteropServices.DllImport("user32.dll")]
public extern static int MoveWindow(IntPtr hWnd, int x, int y,
int nWidth, int nHeight, int bRepaint);
Related
This question already has answers here:
Opening process and changing window position
(3 answers)
Closed 5 years ago.
The event method below brings up the windows system date time clock window. My label is on the lower right side of my form and the system date time clock window appears on the upper left side of my form. Is there a way to position this date time clock window to be on the lower right side of my form when this event handler is clicked?
private void LabelDateTime_Click(object sender, System.EventArgs e)
{
// bring up the date & time dialog
System.Diagnostics.Process.Start("timedate.cpl");
}
Starting a process using System.Diagnostics.Process.Start() in this manner, is not effective, since the generated Process will exit immediately after the window is created. A .cpl applet is not a standard executable and needs then operating system shell and a launcher to start.
However, a stable process can be created using Rundll32.exe, which will generate some threads to host the applet controls and the GDI+ support.
Reaching the applet Window requires some P/Invoke(ing), though, since rundll is window-less and it doesn't reference the one it helps create, so the Process.MainWindowHandle = 0.
Doc Ref. MSDN EnumThreadWndProc() Callback, EnumThreadWindows(), GetWindowRect(), GetWindowText(), SetWindowPos()
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
ProcessStartInfo psInfo = new ProcessStartInfo() {
UseShellExecute = true,
FileName = "rundll32.exe",
Arguments = "shell32.dll, Control_RunDLL timedate.cpl,,0", //<- 0 = First Tab
WindowStyle = ProcessWindowStyle.Normal
};
Process sysClockProcess = new Process() {
SynchronizingObject = this,
EnableRaisingEvents = true,
StartInfo = psInfo
};
sysClockProcess.Start();
sysClockProcess.WaitForInputIdle();
//Insert the Window title. It's case SENSITIVE
//Window Title in HKEY_CURRENT_USER\Software\Classes\Local Settings\MuiCache\[COD]\[LANG]\
string windowTitle = "Date and Time";
int maxLenght = 256;
SetWindowPosFlags flags = SetWindowPosFlags.NoSize |
SetWindowPosFlags.AsyncWindowPos |
SetWindowPosFlags.ShowWindow;
//The first thread is the Main thread. All Dialog windows' handles are attached here.
//The second thread is for GDI+ Hook Window. Ignore it.
EnumThreadWindows((uint)sysClockProcess.Threads[0].Id, (hWnd, lParam) =>
{
StringBuilder lpString = new StringBuilder(maxLenght);
if (GetWindowText(hWnd, lpString, maxLenght) > 0)
if (lpString.ToString() == windowTitle)
{
GetWindowRect(hWnd, out RECT lpRect);
Size size = new Size(lpRect.Right - lpRect.Left, lpRect.Bottom - lpRect.Top);
//Caculate the position of the Clock Windows relative to the ref. Form Size
SetWindowPos(hWnd, (IntPtr)0, ((this.Width + this.Left) - size.Width),
((this.Height + this.Top) - size.Height), 0, 0, flags);
return false;
}
//Window not found: return true to continue the enumeration
return true;
}, ref windowTitle);
sysClockProcess.Exited += (s, ev) => {
Console.WriteLine($"The process has exited. Code: " +
$"{sysClockProcess.ExitCode} Time: {sysClockProcess.ExitTime}");
sysClockProcess.Dispose();
};
Win32 declarations:
// SetWindowPos() flags
[Flags]
public enum SetWindowPosFlags : uint
{
NoSize = 0x0001,
NoActivate = 0x0010,
ShowWindow = 0x0040,
DeferErase = 0x2000,
AsyncWindowPos = 0x4000
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
//Callback for `EnumThreadWindows()`.
public delegate bool EnumThreadWndProc([In] IntPtr hWnd, [In] IntPtr lParam);
[DllImport("user32.dll")]
static extern bool EnumThreadWindows([In] uint dwThreadId, [In] EnumThreadWndProc lpfn, [In] ref string lParam);
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, [Out] StringBuilder lpString, [In] int nMaxCount);
[DllImport("user32.dll", SetLastError = true)]
static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
[DllImport("user32.dll", SetLastError=true)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
I am runnig an external application from my WPF Project and I am putting external app inside my WPF form with using "user32.dll"
External app has an exit button. I would like to remove or hide that button. Can I do that "using user32.dll" or different approach?
Thank you in advance.
The below code finds the button and hides it. It works gracefully on my system. The code searches for the window title and then find the control. You have to provide the window title and button text. You can update the code as per your need.
Note: Below code will hide all the controls with the matching text specified in the constant TEXT_BUTTON.
const string TEXT_TITLE = "My Specific Window";
const string TEXT_BUTTON = "&HideMeButton";
public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hwnd, int nCmdShow);
const int SW_HIDE = 0;
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
[DllImport("user32.dll", EntryPoint = "GetWindowText", CharSet = CharSet.Auto)]
static extern IntPtr GetWindowCaption(IntPtr hwnd, StringBuilder lpString, int maxCount);
public void HideSpecificButton()
{
//Contains the handle, can be zero if title not found
var handleWindow = WinGetHandle(TEXT_TITLE);
if (GetWindowCaption(handleWindow).Trim() != TEXT_TITLE)
MessageBox.Show("Window is hidden or not running.");
else
GetChildWindows(handleWindow);
}
public IntPtr WinGetHandle(string title)
{
IntPtr hWnd = IntPtr.Zero;
foreach (Process pList in Process.GetProcesses())
{
if (pList.MainWindowTitle.Contains(title))
{
hWnd = pList.MainWindowHandle;
}
}
return hWnd;
}
private string GetWindowCaption(IntPtr hwnd)
{
StringBuilder sb = new StringBuilder(256);
GetWindowCaption(hwnd, sb, 256);
return sb.ToString();
}
public void GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumControls);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
}
private bool EnumControls(IntPtr handle, IntPtr pointer)
{
var controlTitle = GetWindowCaption(handle).Trim();
if (string.Equals(controlTitle, TEXT_BUTTON, StringComparison.CurrentCultureIgnoreCase))
{
//hide the control
ShowWindow(handle, SW_HIDE);
}
return true;
}
"using user32.dll"
No you can't use user32.dll for each app is in their own sandbox so to speak and should be impervious to outside unwanted actions.
(Q: Do you have access to build this external app? A: Y ) ...or different approach?
Since you have access to the code of both apps, have them implement an interprocess named pipe. In the receiving app have it monitor the pipe for a message to turn off the button(s) or change its windows frame style.
See
How to: Use Named Pipes for Network Interprocess Communication
I'm trying to get a window handle from point using p/invoke, where window is a form, and not any child control. I have a simple interface where X and Y are entered by user, and then Find button is used to call win32 and get necessary information. My problem is that window is not necessarily a form, it can also be a control. See below screenshot - at (100,100) happened to be Notepad's text area with "StackOverflow" written in it. As a result, Found window shows "StackOverflow".
Is there any way I can restrict window type to be a Form? Expected result is "Untitled - Notepad" for below test case. Alternatively, is there a way to ask another application's control to provide its form's handle? In short, I need to get form's title from (x,y) point. Button click handler code:
private void btn_Find_Click(object sender, EventArgs e)
{
int xPoint = Convert.ToInt32(txt_WindowX.Text);
int yPoint = Convert.ToInt32(txt_WindowY.Text);
IntPtr hWnd = Win32.GetWindowHandleFromPoint(xPoint, yPoint);
txt_FormTitle.Text = Win32.GetWindowTitle(hWnd);
}
Major portion of Win32 class comes from this answer:
Tergiver's answer to "C# - unable to read another application's caption"
Full Win32 class code is provided below:
public class Win32
{
/// <summary>
///
/// </summary>
/// <param name="hwnd"></param>
/// <remarks>https://stackoverflow.com/questions/4604023/unable-to-read-another-applications-caption</remarks>
public static string GetWindowTitle(IntPtr hwnd)
{
if (hwnd == IntPtr.Zero)
throw new ArgumentNullException("hwnd");
int length = Win32.SendMessageGetTextLength(hwnd, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
if (length > 0 && length < int.MaxValue)
{
length++; // room for EOS terminator
StringBuilder sb = new StringBuilder(length);
Win32.SendMessageGetText(hwnd, WM_GETTEXT, (IntPtr)sb.Capacity, sb);
return sb.ToString();
}
return String.Empty;
}
public static IntPtr GetWindowHandleFromPoint(int x, int y)
{
var point = new Point(x, y);
return Win32.WindowFromPoint(point);
}
const int WM_GETTEXT = 0x000D;
const int WM_GETTEXTLENGTH = 0x000E;
[DllImport("user32.dll")]
private static extern IntPtr WindowFromPoint(Point p);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessageGetTextLength(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessageGetText(IntPtr hWnd, int msg, IntPtr wParam, [Out] StringBuilder lParam);
}
You need to locate the top level window. Start from the window that GetWindowHandleFromPoint yielded. Then call GetParent repeatedly until you find a window with no parent. That window with no parent is the top level window that you are looking for.
There are two monitors and an application (e.g. IE) is currently active/displayed in the 2nd monitor. How do I detect if an application is in 1st or 2nd monitor.
I need to know this - to show a userform just on top of the application irrespective of the monitor in which it is. I know what the WindowText (Title) would be (if that helps).
Right now I just show my form near the system tray but would like to show it on top of the application.
// FORM POSITION
this.StartPosition = FormStartPosition.Manual;
Rectangle workArea = Screen.PrimaryScreen.WorkingArea;
int left = workArea.Width - this.Width;
int top = workArea.Height - this.Height;
this.Location = new Point(left, top);
This was converted from VB but should work.
The Test() method shows how you would use it.
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle);
public static RECT GetWindowLocationByName(string name)
{
IntPtr handle = FindWindow(default(string), name);
RECT result = default(RECT);
GetWindowRect(handle, ref result);
return result;
}
public static void Test()
{
dynamic location = GetWindowLocationByName("Untitled - Notepad");
Screen result = null;
foreach (Screen s in Screen.AllScreens) {
if (s.WorkingArea.IntersectsWith(new Rectangle(location.Left, location.Top, location.Right - location.Left, location.Bottom - location.Top))) {
result = s;
}
}
}
Edit: More Info
Step 1: Get the window handle
Step 2: Get the window rect (location/size)
Step 3: Determin which monitor the window resides on
If your looking at overlyaying one window on top of another you don't actually need to know which monitor it is on just the position and size of the window relative to the desktop. In both windows forms and WPF when you set the window location X / Left is the distance in pixels from the left side of the left most monitor. E.g if you have two montior 1024 pixels wide setting X / Left to 2000 will put the window 86 pixels into right hand monitor.
To get the windows position of another process you could use the library mentioned here
How to get and set the window position of another application in C#
You should then be able to check on which screen the position of the rectangle is
private Screen IsVisibleOnScreen(Rectangle rect)
{
foreach (Screen screen in Screen.AllScreens)
{
if (screen.WorkingArea.IntersectsWith(rect))
{
return Screen;
}
}
return null;
}
I've added a Notify Icon to my app, and quite often I see up to 3 copies of the notify icon in my systray. is there a reason for this?
is there a way to stop it from happening.
Often this persists after my app has closed, untill I mose over to the systray and the systray expands and collapses snd then they all disapear.
Is this while you are debugging your application? if so this is because the messages that remove the icon from the system tray are only sent when the application exits normally, if it terminates because of an exception or because you terminate it from Visual Studio the icon will remain until you mouse over it.
You can kill the icon using the parent Window's Closed event. This works in my WPF app, even when testing in Visual Studio (2010 in my case):
parentWindow.Closing += (object sender, CancelEventArgs e) =>
{
notifyIcon.Visible = false;
notifyIcon.Icon = null;
notifyIcon.Dispose();
};
What I did:
Create a class library that updates the system tray.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace SystrayUtil
{
internal enum MessageEnum
{
WM_MOUSEMOVE = 0x0200,
WM_CLOSE = 0x0010,
}
internal struct RECT
{
internal int Left;
internal int Top;
internal int Right;
internal int Bottom;
internal RECT(int left, int top, int right, int bottom)
{
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
}
public sealed class Systray
{
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, IntPtr lpszWindow);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hWnd, int message, uint wParam, long lParam);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool GetClientRect(IntPtr hWnd, out RECT usrTray);
public static void Cleanup()
{
RECT sysTrayRect = new RECT();
IntPtr sysTrayHandle = FindWindow("Shell_TrayWnd", null);
if (sysTrayHandle != IntPtr.Zero)
{
IntPtr childHandle = FindWindowEx(sysTrayHandle, IntPtr.Zero, "TrayNotifyWnd", IntPtr.Zero);
if (childHandle != IntPtr.Zero)
{
childHandle = FindWindowEx(childHandle, IntPtr.Zero, "SysPager", IntPtr.Zero);
if (childHandle != IntPtr.Zero)
{
childHandle = FindWindowEx(childHandle, IntPtr.Zero, "ToolbarWindow32", IntPtr.Zero);
if (childHandle != IntPtr.Zero)
{
bool systrayWindowFound = GetClientRect(childHandle, out sysTrayRect);
if (systrayWindowFound)
{
for (int x = 0; x < sysTrayRect.Right; x += 5)
{
for (int y = 0; y < sysTrayRect.Bottom; y += 5)
{
SendMessage(childHandle, (int)MessageEnum.WM_MOUSEMOVE, 0, (y << 16) + x);
}
}
}
}
}
}
}
}
}
}
Copy the dll to "%ProgramFiles%\Microsoft Visual Studio x.x\Common7\IDE\PublicAssemblies\SystrayUtil.dll"
Where x.x is the version number of Visual Studio
Record a macro and save it
Edit the macro
Add a reference to the created dll.
Add Imports SystrayUtil to the list of imports at the top of Module EnvironmentEvents.
Remove any unwanted items and add the following code to the EnvironmentEvents module
Public Sub DebuggerEvents_OnEnterDesignMode(ByVal Reason As EnvDTE.dbgEventReason) Handles DebuggerEvents.OnEnterDesignMode
Systray.Cleanup()
MsgBox("Entered design mode!")
End Sub
If it works remove MsgBox("Entered design mode!") because it's annoying to have a message box popping up every time you return from a debugging session.
This should work when you normally close the application:
// in form's constructor
Application.ApplicationExit += new EventHandler(this.OnApplicationExit);
private void OnApplicationExit(object sender, EventArgs e)
{
try
{
if (notifyIcon1!= null)
{
notifyIcon1.Visible = false;
notifyIcon1.Icon = null;
notifyIcon1.Dispose();
notifyIcon1= null;
}
}
catch { }
}
When you stop the App from Visual Studio stop debug button - the process is killed and no dispose events are fired.