Move external application to the front of the screen - c#

The application that I'm running needs to call a separate app to do some scanning. I'm calling the other application by starting a new System.Diagnostics.Process. Once I get that process, I call a method to give that application the focus. I've tried two different ways to give that external app the focus, but neither are working. Could someone help?
Here's the code:
using System.Runtime.InteropServices;
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, uint windowStyle);
[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd,
IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
private static void GiveSpecifiedAppTheFocus(int processID)
{
try
{
Process p = Process.GetProcessById(processID);
ShowWindow(p.MainWindowHandle, 1);
SetWindowPos(p.MainWindowHandle, new IntPtr(-1), 0, 0, 0, 0, 3);
//SetForegroundWindow(p.MainWindowHandle);
}
catch
{
throw;
}
}
First scenario uses the ShowWindow and SetWindowPos methods, the other method uses the SetForegroundWindow method. Neither will work...
Am I using the wrong methods, or do I have an error in the code that I'm using? Thanks all!

Use SetWindowPos, but whenever you don't want the window to be the topmost anymore call it again with the second parameter set to -2 (HWND_NOTOPMOST) instead of -1(HWND_TOPMOST)

Try setting your process to the background ie: this.SendToBack(); This is just another solution, partial fix.

Related

MoveWindow API function Error: Form already displayed; can't show modally

I want to resize an external application by using MoveWindow API.
But every time, I get This Error: Form already displayed; can't show modally (Error 400) .
Obviously, I don't care about modal and/or non-modal mode of the window, I just wanna resize it.
MoveWindow() function always returns true, but the error arise from target application.
I'm using Windows 7 Home Editionand .Net 4.0.
Can anyone leads me to the correct direction? or give me a clue?
Here is my code:
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("kernel32.dll")]
static extern uint GetLastError();
static void Main(string[] args)
{
var hWnd = FindWindow(null, "foodsoft_aminpardazco_1388");
if (hWnd != null)
{
bool result = MoveWindow(hWnd, 0, 0, 100, 100, false);
Console.WriteLine(result?"hooraaa!":"error:"+GetLastError());
}
else
Console.WriteLine("eeeee");
Console.ReadKey();
}
The message tells me, you cannot show already displaying form in modal mode, So i wanna resize this window in non-modal mode. Do you have any idea?

How do I set the focus to the Desktop from within my C# application

Winforms App. .Net 3.5.
I need to set the focus from my C# application to the user's desktop (almost like simulating a mouse click on the desktop).
Can someone please show me how to do this with C#? I just want to set focus on the desktop so the focus is no longer on my application but I want to do this from within my application.
Edit: An answer below works by setting the focus to the desktop, but it minimizes all the open windows on the user's desktop.
Is there a way I can maybe set the focus to the next open window on the desktop instead? I just want to get the focus off of my application (without minimizing my application or hiding it). I just want to move focus to somewhere else. Maybe the desktop was not the best choice if it will minimize all the user's open windows/applications.
This should do it for you.
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1 {
class Program {
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "SendMessage", SetLastError = true)]
static extern IntPtr SendMessage(IntPtr hWnd, Int32 Msg, IntPtr wParam, IntPtr lParam);
const int WM_COMMAND = 0x111;
const int MIN_ALL = 419;
const int MIN_ALL_UNDO = 416;
static void Main(string[] args) {
IntPtr lHwnd = FindWindow("Shell_TrayWnd", null);
SendMessage(lHwnd, WM_COMMAND, (IntPtr)MIN_ALL, IntPtr.Zero);
System.Threading.Thread.Sleep(2000);
SendMessage(lHwnd, WM_COMMAND, (IntPtr)MIN_ALL_UNDO, IntPtr.Zero);
}
}
}
Get Next Window
I don't have a code example ready for these two but I'm going to give you the links to both. The first think you need to do is call GetWindow. After doing that you'll want to call SwitchToThisWindow passing in the pointer you received from GetWindow.
You can add this COM object in your project:
Microsoft Shell Controls And Automation
And then just call:
Shell32.ShellClass shell = new Shell32.ShellClass();
shell.MinimizeAll();
This will minimize all the windows and then focus the desktop. Otherwise, if you have your window non-full screen then you can simulate the mouse click using:
//This is a replacement for Cursor.Position in WinForms
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern bool SetCursorPos(int x, int y);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
public const int MOUSEEVENTF_LEFTDOWN = 0x02;
public const int MOUSEEVENTF_LEFTUP = 0x04;
//This simulates a left mouse click
public static void LeftMouseClick(int xpos, int ypos)
{
SetCursorPos(xpos, ypos);
mouse_event(MOUSEEVENTF_LEFTDOWN, xpos, ypos, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, xpos, ypos, 0, 0);
}
You can calculate coordinates by looking at your window startup location plus height/width and select a available space (that will be the desktop indeed).

Capture screenshot Including Semitransparent windows in .NET

I would like a relatively hack-free way to do this, any ideas? For example, the following takes a screenshot that doesn't include the semi-transparent window:
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Shown
Text = "Opaque Window"
Dim win2 As New Form
win2.Opacity = 0.5
win2.Text = "Tranparent Window"
win2.Show()
win2.Top = Top + 50
win2.Left = Left() + 50
Dim bounds As Rectangle = System.Windows.Forms.Screen.GetBounds(Point.Empty)
Using bmp As Bitmap = New Bitmap(bounds.Width, bounds.Height)
Using g As Graphics = Graphics.FromImage(bmp)
g.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size)
End Using
bmp.Save("c:\temp\scn.gif")
End Using
Process.Start(New Diagnostics.ProcessStartInfo("c:\temp\scn.gif") With {.UseShellExecute = True})
End Sub
End Class
Either my google-fu really sucks or this is not as easy as it sounds. I'm pretty sure why this is happening because of the way the video driver would have to separate the memory to make this work, but I don't care why it doesn't work, I just want to do it without...
* print-screen key hacks
* 3rd party software
* SDK functions are OK but I'll upvote every object owned by the user that can show me it in pure framework (Just kidding but it would be nice).
If This is the only way to do it, how to I do that in VB?
1M thanks.
Forms that have the TransparencyKey or Opacity property set are so-called layered windows. They are shown using the "overlay" feature of the video adapter. Which make them being able to have their transparency effects.
Capturing them requires turning on the CopyPixelOperation.CaptureBlt option in the CopyFromScreen overload that accepts the CopyPixelOperation argument.
Unfortunately, this overload has a critical bug that prevents this from working. It doesn't validate the value properly. Still not fixed in .NET 4.0. There is no other good fix but fall back to using P/Invoke to make the screen shot. Here's an example:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsApplication1 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) {
Size sz = Screen.PrimaryScreen.Bounds.Size;
IntPtr hDesk = GetDesktopWindow();
IntPtr hSrce = GetWindowDC(hDesk);
IntPtr hDest = CreateCompatibleDC(hSrce);
IntPtr hBmp = CreateCompatibleBitmap(hSrce, sz.Width, sz.Height);
IntPtr hOldBmp = SelectObject(hDest, hBmp);
bool b = BitBlt(hDest, 0, 0, sz.Width, sz.Height, hSrce, 0, 0, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
Bitmap bmp = Bitmap.FromHbitmap(hBmp);
SelectObject(hDest, hOldBmp);
DeleteObject(hBmp);
DeleteDC(hDest);
ReleaseDC(hDesk, hSrce);
bmp.Save(#"c:\temp\test.png");
bmp.Dispose();
}
// P/Invoke declarations
[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);
}
}
Fwiw, a later Windows version provided a workaround for this bug. Not exactly sure which, I think it was Win7 SP1. The BitBlt() function will now do what you want if you pass only the CopyPixelOperation.CaptureBlt option. But of course that workaround wasn't applied retro-actively to earlier Windows versions so you can't really depend on it.

Capture Highlighted Text from any window using C#

How to read the highlighted/Selected Text from any window using c#.
i tried 2 approaches.
Send "^c" whenever user selects some thing. But in this case my clipboard is flooded with lots of unnecessary data. Sometime it copied passwords also.
so i switched my approach to 2nd method, send message method.
see this sample code
[DllImport("user32.dll")]
static extern int GetFocus();
[DllImport("user32.dll")]
static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(int hWnd, int ProcessId);
[DllImport("user32.dll") ]
static extern int GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern int SendMessage(int hWnd, int Msg, int wParam, StringBuilder lParam);
// second overload of SendMessage
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, uint Msg, out int wParam, out int lParam);
const int WM_SETTEXT = 12;
const int WM_GETTEXT = 13;
private string PerformCopy()
{
try
{
//Wait 5 seconds to give us a chance to give focus to some edit window,
//notepad for example
System.Threading.Thread.Sleep(5000);
StringBuilder builder = new StringBuilder(500);
int foregroundWindowHandle = GetForegroundWindow();
uint remoteThreadId = GetWindowThreadProcessId(foregroundWindowHandle, 0);
uint currentThreadId = GetCurrentThreadId();
//AttachTrheadInput is needed so we can get the handle of a focused window in another app
AttachThreadInput(remoteThreadId, currentThreadId, true);
//Get the handle of a focused window
int focused = GetFocus();
//Now detach since we got the focused handle
AttachThreadInput(remoteThreadId, currentThreadId, false);
//Get the text from the active window into the stringbuilder
SendMessage(focused, WM_GETTEXT, builder.Capacity, builder);
return builder.ToString();
}
catch (System.Exception oException)
{
throw oException;
}
}
this code working fine in Notepad. But if i try to capture from another applications like Mozilla firefox, or Visual Studio IDE, it's not returning the text.
Can anybody please help me, where i am doing wrong? First of all, i have chosen the right approach?
That's because both Firefox and Visual Studio don't use the built-in Win32 controls for displaying/editing text.
It is not possible in general to be able to get the value of "any" selected text, because of the fact that programs can re-implement their own version of the Win32 controls any way they see fit, and your program cannot possibly expect to work with all of them.
However, you can use the UI Automation APIs which will allow you to interact with the majority of 3rd-party controls (at least, all the good ones - such as Visual Studio and Firefox - will likely work with the UI Automation APIs since it's a requirement for accessibility)

Launching another application from C#

I'm trying to launch another application from C# application, is there a way to display this application inside the mainform of my application?
Thanks,
You can start other applications using Process.Start(...):
Process.Start(#"C:\Path\OtherApp.exe");
To embed the application within your form, check out this CodeProject article that demos a technique for hosting other application's windows within your form.
You can try do this via reparenting. See my post on MSDN where I describe this for WPF: Composite "shell" application.
The technique itself would be the same for WinForms. Have a host area in your app. Change the top-level window's style of the target application to WS_CHILD. Call SetParent(), changing the parent of the target window to your host area.
Note that in Win32, only a top-level window has a menu. Thus, changing to WS_CHILD removes the menu.
You can do it in that way :
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
[DllImport("user32.dll", SetLastError = true)]
static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
[DllImport("user32.dll")]
static extern IntPtr SetActiveWindow(IntPtr hWnd);
private const int GWL_STYLE = (-16);
private const int WS_VISIBLE = 0x10000000;
private const int WS_MAXIMIZE = 0x01000000;
private void Form1_Load(object sender, EventArgs e)
{
this.SuspendLayout();
Process notepad = new Process();
ProcessStartInfo psi = new ProcessStartInfo("notepad.exe");
psi.WindowStyle = ProcessWindowStyle.Normal;
notepad.StartInfo = psi;
notepad.Start();
this.ResumeLayout();
notepad.WaitForInputIdle(3000);
IntPtr old = SetParent(notepad.MainWindowHandle, this.Handle);
SetWindowLong(notepad.MainWindowHandle, GWL_STYLE, WS_VISIBLE + WS_MAXIMIZE);
MoveWindow(notepad.MainWindowHandle, 100, 100, 400, 400, true);
SetActiveWindow(notepad.MainWindowHandle);
SwitchToThisWindow(notepad.MainWindowHandle, true); }
In this way you have Notepad app in your form ;)
In general, it's next to impossible to display any kind of 3rd party application inside of yours.
If target app supports console interface, I would create my own interface for this app that will translate GUI commands to console commands of target app.

Categories