How do I know how much time I should wait for PrintDialog after sending Commands to Print(to press "Ctrl+P" button)? - c#

I am writing a Console application using C#, in which I am firing Print jobs through code. My issue is I do not know how much time I should wait for Print Dialog after sending commands to Print. For the time being I am using Thread sleep of 1000 milliseconds.
//Sending Commands to Print(to press "Ctrl+P" button).
SendKeys.SendWait("^(p)");
Thread.Sleep(1000);
//Sending Commands to Print(to press "Enter" Button).
SendKeys.SendWait("{ENTER}");
Can anybody help me to fix this issue please. Any help will be appreciated.
Thanks in advance
Update:
Here is my whole code:
//Launch the file from the location specified in.
White.Core.Application application =White.Core.Application.Launch(#path);
Console.WriteLine("launch is done");
Thread.Sleep(_delayOfPrint);
//Sending Commands to Print(to press "Ctrl+P" button).
SendKeys.SendWait("^(p)");
Thread.Sleep(1000);
//Sending Commands to Print(to press "Enter" Button).
SendKeys.SendWait("{ENTER}");
//Get the current time as the document fired for print job.
_printedTime = DateTime.Now;
Thread.Sleep(1500);
//Closing the window.
SendKeys.SendWait("%{F4}");

I would have a maximum wait of a few seconds, but during that time I would periodically use the Win32 function FindWindowEx to see if the print dialog actually came up, and if so proceed. You can get a process' main window with code like this:
Process[] processes = Process.GetProcessesByName(appName);
foreach (Process p in processes)
{
IntPtr pFoundWindow = p.MainWindowHandle;
}
You can then pass the found main window handle to FindWindowEx to peruse the child windows to check for the print dialog. FindWindowEx has PInvoke signatures like this:
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, IntPtr windowTitle);
Edit 2: Since the OP seems to be demanding I give a perfectly working function, I wrote a general one that does the trick and tested it. Here it is, working general code to wait for any child window. Call as WaitForChildWindow("myApp", "Print", 5000) for this case:
/// <summary>
/// Wait for a child window of an application to appear
/// </summary>
/// <param name="appName">Application name to check (will check all instances)</param>
/// <param name="childWindowName">Name of child window to look for (titlebar)</param>
/// <param name="timeout">Maximum time, in milliseconds, to wait</param>
/// <returns>True if the window was found; false if it wasn't.</returns>
public static bool WaitForChildWindow(string appName, string childWindowName, int timeout)
{
int sleepTime = timeout;
while (sleepTime > 0)
{
Process[] processes = Process.GetProcessesByName(appName);
foreach (Process p in processes)
{
IntPtr pMainWindow = p.MainWindowHandle;
IntPtr pFoundWindow = FindWindowEx(pMainWindow, IntPtr.Zero, null, childWindowName);
if (pFoundWindow != IntPtr.Zero)
return true;
}
Thread.Sleep(100);
sleepTime -= 100;
}
// Timed out!
return false;
}
Edit 3: Here's another way of doing it for merely owned windows that don't have a child relationship:
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hwnd, IntPtr processId);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumThreadWindows(uint dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
[DllImport("user32", SetLastError = true, CharSet = CharSet.Auto)]
private extern static int GetWindowText(IntPtr hWnd, StringBuilder text, int maxCount);
public delegate bool EnumThreadDelegate(IntPtr hwnd, IntPtr lParam);
static bool EnumThreadCallback(IntPtr hWnd, IntPtr lParam)
{
StringBuilder text = new StringBuilder(500);
GetWindowText(hWnd, text, 500);
if (text.ToString() == "Print")
return false;
return true;
}
public static bool FindThreadPrintWindow(uint threadId)
{
return !EnumThreadWindows(threadId, EnumThreadCallback, IntPtr.Zero);
}
public static bool WaitForOwnedPrintWindow(string appName, int timeout)
{
int sleepTime = timeout;
while (sleepTime > 0)
{
Process[] processes = Process.GetProcessesByName(appName);
foreach (Process p in processes)
{
IntPtr pMainWindow = p.MainWindowHandle;
uint threadId = GetWindowThreadProcessId(pMainWindow, IntPtr.Zero);
if (FindThreadPrintWindow(threadId))
return true;
}
Thread.Sleep(100);
sleepTime -= 100;
}
// Timed out!
return false;
}

Related

C# Pinvoke can't find the Hwnd of Controls after List count was 0 at first time

I'm trying to click a Button in another Application (started from my Programm with Process.Start)
The problem: I need to wait until the Loading screen is disappeared and the GUI pop's up...
My idea was to read all (Hwnd)Controls until a specific Control (Button: "Kill Client") from the GUI was found (=GUI Opened).
But this only works if I wait manually for the GUI and press a "Search Control" button.
If I press the "Search Button" if the Loading Screen is aktive I get a Hwnd = 0 (List<'IntPtr> Count is also 0...) and if i press it again if the GUI is opened it is 0 again(List<'IntPtr> Count too...) !!!
Here my Code:
public class WndSearcher
{
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
public static List<IntPtr> GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
GCHandle gch = GCHandle.FromIntPtr(pointer);
List<IntPtr> list = gch.Target as List<IntPtr>;
if (list == null)
{
throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
}
list.Add(handle);
return true;
}
}
My Button:
List<IntPtr> AllControlHandles = WndSearcher.GetChildWindows(selectedCharacter.Botprocess.MainWindowHandle);
IntPtr ControlHandle = AllControlHandles.Find(x => PInvoke.GetWindowTextRaw(x) == "Kill Client" ? true : false);
MessageBox.Show(ControlHandle.ToString());
Part of PInvoke (Class):
const int WM_GETTEXT = 0x000D;
const int WM_GETTEXTLENGTH = 0x000E;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, [Out] StringBuilder lParam);
public static string GetWindowTextRaw(IntPtr hwnd)
{
// Allocate correct string length first
int length = (int)SendMessage(hwnd, WM_GETTEXTLENGTH, IntPtr.Zero, null);
StringBuilder sb = new StringBuilder(length + 1);
SendMessage(hwnd, WM_GETTEXT, (IntPtr)sb.Capacity, sb);
return sb.ToString();
}
Found no solution till now.
So I decided to use AutoHotKey in combination with C#.
In C# I start my AutoHotKey Script and wait until the Script is finished. (Then the external Programm is started completely)
Starting Arguments: 1.Processid 2.NewExternalProgramName
Here my AutoHotKey Script:
counter := 0
Loop, %0% ; For each parameter:
{
param := %A_Index%
if (counter = 0) ; do sth with parameter 1
winwait, ahk_pid %param% ; Not logged in ;wait until the text "Not logged in" can be read (Program started completely)
if (counter = 1) ; do sth with parameter 2
WinSetTitle, %param%
counter += 1
}

Is there a way to remove close button from another process with C#?

I tried it with C++ and it seems like one needs to inject code into another process to disable its main window's close button. But I'm curious, since I actually need it for a .NET program, can I gray out its main window's close button with C#?
PS. Again the process that I'm doing it for is not mine. I can find it as such:
Process[] processes = Process.GetProcessesByName("Notepad");
foreach (Process p in processes)
{
IntPtr pFoundWindow = p.MainWindowHandle;
//Now how to disable it's close button?
}
I think I got it:
[DllImport("user32.dll")]
static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
static extern bool DeleteMenu(IntPtr hMenu, uint uPosition, uint uFlags);
const uint SC_CLOSE = 0xF060;
const uint MF_BYCOMMAND = 0x00000000;
Process[] processes = Process.GetProcessesByName("Notepad");
foreach (Process p in processes)
{
IntPtr pFoundWindow = p.MainWindowHandle;
IntPtr nSysMenu = GetSystemMenu(pFoundWindow, false);
if (nSysMenu != IntPtr.Zero)
{
if (DeleteMenu(nSysMenu, SC_CLOSE, MF_BYCOMMAND))
{
//Done!
}
}
}

Outlook security prompt auto approval

I'm writing a function that will allow users to import their e-mails from outlook in to corporate storage. But I've encounter a problem with security promt, and since we still have clients with office 2003 we can't disable it.
I'm trying to autoclick it with this code:
[DllImport("User32.dll")]
public static extern Int32 FindWindow(String lpClassName, String lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(int hWnd, int msg, int wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
private const int WM_LBUTTONDOWN = 0x201;
private const int WM_LBUTTONUP = 0x0202;
private void button1_Click(object sender, EventArgs e)
{
int hwnd = 0;
IntPtr hwndChild = IntPtr.Zero;
while (true)
{
hwnd = FindWindow(null, "Microsoft Outlook");
if (hwnd != 0)
{
hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "Button", "Yes");
if (hwndChild != IntPtr.Zero)
{
SendMessage((int)hwndChild, WM_LBUTTONDOWN, 0, IntPtr.Zero);
SendMessage((int)hwndChild, WM_LBUTTONUP, 0, IntPtr.Zero);
}
else
{
//...
}
}
else
{
//...
}
hwnd = 0;
}
}
But when I'm trying to use that code I've encounter unexpected problem. Security promt will only disappear only when I'm actually performing mouse click no matter where even on some empty screen space. What is wrong with it?
Try out MAPILab's Advanced Security for Outlook. Should also work for Outlook 2003. This will let you give a permanent access for your application - a pop-up window will show only the first time you access Outlook items:
http://www.mapilab.com/outlook/security/
Advanced Security will have to be installed on each machine you run your app. It's free for noncommercial and commercial use.
You should try using SendKeys and send Y for Yes.

Send String to Command Prompt (cmd) from Windows Form Application using SendMessage

I read many answers on how to send command to cmd prompt.
But I do not want to use StreamWriter or similar stuff to input and get output.
I want to use SendMessage to send my string or say command to the cmd prompt window.
Can anyone please help on this?
Just to give detail about my application.
1. My application is a WinForm App.
2. It has 4-5 buttons.
3. Button1 opens the command prompt window while Button5 closes or exits the command prompt window.
4. Button 2,3,4 are command buttons. When user clicks the Button2 command 1 is send to command prompt window. Similar when button 3 and 4 are clicked command 2 and command 3 are sent to the same command prompt window.
Let me know if anybody has a written code which sends string to command prompt.
Thanks and regards,
Rahul
I have got the solution.
I used PostMessage().
Cmd has handle and you can send string to it.
I was just struggling to find the right way to get the handle.
Now I got one.
/*
* Created by SharpDevelop.
* User: Rahul
* Date: 5/12/2011
* Time: 1:49 AM
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
namespace GetChildWindows
{
/// <summary>
/// Description of MainForm.
/// </summary>
public partial class MainForm : Form
{
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
IntPtr hWnd = FindWindow(null, "Untitled - Notepad");
[DllImport("user32.dll", SetLastError = true)]
static extern bool PostMessage(IntPtr hWnd, [MarshalAs(UnmanagedType.U4)] uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
static extern bool PostMessage(IntPtr hWnd, [MarshalAs(UnmanagedType.U4)] uint Msg, int wParam, int lParam);
const int WM_KEYDOWN = 0x0100;
const int WM_KEYUP = 0x0101;
const int WM_CHAR = 0x0102;
public static IntPtr cmdHwnd = IntPtr.Zero;
public MainForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
//
// TODO: Add constructor code after the InitializeComponent() call.
//
foreach (IntPtr child in GetChildWindows(FindWindow(null, "WinPlusConsole")))
{
StringBuilder sb = new StringBuilder(100);
GetClassName(child, sb, sb.Capacity);
if (sb.ToString() == "ConsoleWindowClass")
{
// uint wparam = 0 << 29 | 0;
// string msg = "Hello";
// int i = 0;
// for (i = 0 ; i < msg.Length ; i++)
// {
// //PostMessage(child, WM_KEYDOWN, (IntPtr)Keys.Enter, (IntPtr)wparam);
// PostMessage(child, WM_CHAR, (int)msg[i], 0);
// }
// PostMessage(child, WM_KEYDOWN, (IntPtr)Keys.Enter, (IntPtr)wparam);
cmdHwnd = child;
}
}
}
/// <summary>
/// Returns a list of child windows
/// </summary>
/// <param name="parent">Parent of the windows to return</param>
/// <returns>List of child windows</returns>
public static List<IntPtr> GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}
/// <summary>
/// Callback method to be used when enumerating windows.
/// </summary>
/// <param name="handle">Handle of the next window</param>
/// <param name="pointer">Pointer to a GCHandle that holds a reference to the list to fill</param>
/// <returns>True to continue the enumeration, false to bail</returns>
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
GCHandle gch = GCHandle.FromIntPtr(pointer);
List<IntPtr> list = gch.Target as List<IntPtr>;
if (list == null)
{
throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
}
list.Add(handle);
// You can modify this to check to see if you want to cancel the operation, then return a null here
return true;
}
/// <summary>
/// Delegate for the EnumChildWindows method
/// </summary>
/// <param name="hWnd">Window handle</param>
/// <param name="parameter">Caller-defined variable; we use it for a pointer to our list</param>
/// <returns>True to continue enumerating, false to bail.</returns>
public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
void BtnHelloClick(object sender, EventArgs e)
{
uint wparam = 0 << 29 | 0;
string msg = "Hello";
int i = 0;
for (i = 0 ; i < msg.Length ; i++)
{
//PostMessage(child, WM_KEYDOWN, (IntPtr)Keys.Enter, (IntPtr)wparam);
PostMessage(cmdHwnd, WM_CHAR, (int)msg[i], 0);
}
PostMessage(cmdHwnd, WM_KEYDOWN, (IntPtr)Keys.Enter, (IntPtr)wparam);
}
void BtnCommandClick(object sender, EventArgs e)
{
uint wparam = 0 << 29 | 0;
string msg = textBox1.Text;
int i = 0;
for (i = 0 ; i < msg.Length ; i++)
{
//PostMessage(child, WM_KEYDOWN, (IntPtr)Keys.Enter, (IntPtr)wparam);
PostMessage(cmdHwnd, WM_CHAR, (int)msg[i], 0);
}
PostMessage(cmdHwnd, WM_KEYDOWN, (IntPtr)Keys.Enter, (IntPtr)wparam);
}
void TextBox1TextChanged(object sender, EventArgs e)
{
}
}
}
Thanks to all!
It seems unlikely that you would be able to achieve this with SendMessage(). SendMessage() requires a window handle and I don't believe that cmd.exe has a suitable window handle to receive the message.
My advice is to look for a method that solves your problem rather than deciding what solution you want and trying to make it fit the problem.
its not clear from your explenation (at least not to me) why you cant use streamWriter in the scenario you describe. can you elaborate on that?
i'd check out the Process class for talking to other programs via stdin/out

How to get ScrollBar positions of Notepad inside my form

I have a problem with getting scrollbar positions. Is it possible to get the scrollbar position of another process for example Notepad. I wrote small app where i tested and always get 0 0 as a position of scrollbars.
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int GetScrollPos(IntPtr hWnd, int nBar);
[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")]
static extern IntPtr SetActiveWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
private void Form1_Load(object sender, EventArgs e)
{
this.SuspendLayout();
Process notepad = new Process();
ProcessStartInfo psi = new ProcessStartInfo(#"c:\list1.txt");
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);
}
I have button which send PGDN event to notepad and it works great but after pgdn event position of scrollbar also is 0 0
private void PGDN_Click(object sender, EventArgs e)
{
Process[] procs = Process.GetProcessesByName("Notepad");
IntPtr hwnd = procs[0].MainWindowHandle;
SetActiveWindow(hwnd);
SwitchToThisWindow(hwnd, true);
Thread.Sleep(2000);
SendKeys.SendWait("{PGDN}");
Thread.Sleep(2000);
label1.Text = "OK";
label1.Text = "";
label1.Text = HScrollPos().ToString() + " " + VScrollPos().ToString(); }
Here are the HScrollPos and VScrollPos functions :
public int VScrollPos()
{
Process[] procs = Process.GetProcessesByName("Notepad");
IntPtr hwnd = procs[0].MainWindowHandle;
if (procs.Length != 0)
{
return GetScrollPos(hwnd , SB_VERT);
}
else
{
MessageBox.Show("Notepad Nor Running");
return 0;
}
}
public int HScrollPos()
{
Process[] procs = Process.GetProcessesByName("Notepad");
IntPtr hwnd = procs[0].MainWindowHandle;
if (procs.Length != 0)
{
return GetScrollPos(hwnd , SB_HORZ);
}
else
{
MessageBox.Show("Notepad Nor Running");
return 0;
}
}
Maybe there is another way to get Scrollbar Position of another process/window in windows? Please Help. Thx for granted.
And here is the Working Code based on Answer. Thx
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
private void button4_Click(object sender, EventArgs e)
{
string lpszParentClass = "Notepad";
string lpszParentWindow = "Untitled - Notepad";
string lpszClass = "Edit";
IntPtr ParenthWnd = new IntPtr(0);
IntPtr hWnd = new IntPtr(0);
ParenthWnd = FindWindow(lpszParentClass, lpszParentWindow);
if (ParenthWnd.Equals(IntPtr.Zero))
MessageBox.Show("Notepad Not Running");
else
{
hWnd = FindWindowEx(ParenthWnd, hWnd, lpszClass, "");
if (hWnd.Equals(IntPtr.Zero))
MessageBox.Show("Notepad doesn't have an edit component ?");
else
{
MessageBox.Show("Notepad Window: " + ParenthWnd.ToString());
MessageBox.Show("Edit Control: " + hWnd.ToString());
}
}
SetActiveWindow(ParenthWnd);
label5.Text = GetScrollPos(hWnd, SB_VERT) + " " + GetScrollPos(hWnd, SB_HORZ);
}
I suspect the problem is that you are using the main window handle, you should be using the handle of the Edit control, which is a child of the main window.
Using the main window hwnd you can enumrate the child windows to find the hWnd of the edit control and then use that hWnd in your call to get the scroll bar position.
SendKeys is working because it is sending the key stroke to the window that has input focus which in this case is the Edit control.
Here is an answer to a question I provided sometime back that will help with the interop for EnumChildWindows if you need, there is a lot more there but it might help.

Categories