Sign in to follow this  
Followers 0
rahoolm

How to create a C Sharp function similar to ControlSend

15 posts in this topic

#1 ·  Posted (edited)

Hello All,

I have created an application in C Sharp which has embedded cmd window inside it.

Now I want to create button which when clicked should send a command to the cmd window.

I have tried StreamWriter but it is not working as expected.

I have created similar thing on Auto It V3.

Can anybody help?

Please let me know if you need any link for source code.

I have the handle of the cmd window and PID of the cmd window.

Regards,

Rahul

Edited by rahoolm

Share this post


Link to post
Share on other sites



How are embedding a command prompt window? That would be the best place to start.

Share this post


Link to post
Share on other sites

How are embedding a command prompt window? That would be the best place to start.

Here is the code in Auto It which I am using taken guidance from thread below

My Code is below:

#include <ButtonConstants.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#include <array.au3>

;~ Opt("GUIOnEventMode", 1) ; Change to OnEvent mode

#region ### START Koda GUI section ### Form=
Global $Form1 = GUICreate("Form1", 700, 500, 200, 20)

Global $btnCommand = GUICtrlCreateButton("Command", 30, 50, 75, 25, $WS_GROUP)
GUISetIcon("D:\102.ico")
GUISetState(@SW_SHOW)
#endregion ### END Koda GUI section ###

;~ GUISetOnEvent($GUI_EVENT_CLOSE, "CLOSEClicked")
;~ GUISetOnEvent($btnCommand, "SENDCommand")

GUIRegisterMsg(0xF, "WM_PAINT")

Global $pid = Run("cmd.exe /T:F0 /k TITLE PLM")
ProcessWait($pid)
; get the handle of the cmd window as i cannot be certain that there will be only one instance of the cmd running with the same window title or class
Global $cmdHandle = _ProcessGetHWnd($pid, 2)
;~ _ArrayDisplay($cmdHandle)
Global $hWndChild = $cmdHandle[1][1]

DllCall("user32.dll", "hwnd", "SetParent", "hwnd", $hWndChild, "hwnd", $Form1)
DllCall("user32.dll", "long", "SetwindowLong", "hwnd", $hWndChild, "int", -20, "long", 0x80000000 + 0x40000000 + 0x40000)
GUISetStyle(BitOR($WS_POPUP, $WS_BORDER), '', $hWndChild)
WinSetState($hWndChild, '', @SW_SHOW)
WinMove($hWndChild, '', 140, 60, 498, 348)

;~ Global $handle = _gethwnd($pid)
;~ ConsoleWrite("Window Handle for " & $pid & " is " & $hWndChild & @CRLF)
;~ ControlSend($hWndChild,"","","Hello")

; inifinite event loop
While 1

    $nMsg = GUIGetMsg()

    Switch $nMsg

        Case $GUI_EVENT_CLOSE
            CLOSEClicked()

        Case $btnCommand
            SENDCommand()

    EndSwitch

    ; sleep for 100 milliseconds (to not hog the cpu)
    Sleep(100)

    ; end of event loop
WEnd

Func CLOSEClicked()
    ; take care of things to do when exiting

    ProcessClose($pid)
    Exit
EndFunc   ;==>CLOSEClicked

Func SENDCommand()

    ConsoleWrite("Window Handle for " & $pid & " is " & $hWndChild & @CRLF)
    ControlSend($hWndChild, "", "", "DIR{ENTER}")

EndFunc   ;==>SENDCommand

Func WM_PAINT($hWnd, $Msg, $wParam, $lParam)
    Sleep(100)
    DllCall("user32.dll", "int", "InvalidateRect", "hwnd", $hWnd, "ptr", 0, "int", 0)
EndFunc   ;==>WM_PAINT

Func _gethwnd($proc)

    $Windowlist = WinList()
    Local $winhandle = 0

    For $i = 1 To $Windowlist[0][0]
        If WinGetProcess($Windowlist[$i][1]) = $proc Then
            $winhandle = $Windowlist[$i][1]
        EndIf
    Next

    ConsoleWrite("Window Handle for " & $proc & " is " & $winhandle & @CRLF)
    Sleep(500)
    Return $winhandle

EndFunc   ;==>_gethwnd

;===============================================================================
;
; Function Name:    _ProcessGetHWnd
; Description:    Returns the HWND(s) owned by the specified process (PID only !).
;
; Parameter(s):  $iPid      - the owner-PID.
;                   $iOption    - Optional : return/search methods :
;                       0 - returns the HWND for the first non-titleless window.
;                       1 - returns the HWND for the first found window (default).
;                       2 - returns all HWNDs for all matches.
;
;                  $sTitle      - Optional : the title to match (see notes).
;                   $iTimeout   - Optional : timeout in msec (see notes)
;
; Return Value(s):  On Success - returns the HWND (see below for method 2).
;                       $array[0][0] - number of HWNDs
;                       $array[x][0] - title
;                       $array[x][1] - HWND
;
;                  On Failure   - returns 0 and sets @error to 1.
;
; Note(s):          When a title is specified it will then only return the HWND to the titles
;                   matching that specific string. If no title is specified it will return as
;                   described by the option used.
;
;                   When using a timeout it's possible to use WinWaitDelay (Opt) to specify how
;                   often it should wait before attempting another time to get the HWND.
;
;
; Author(s):        Helge
;
;===============================================================================
Func _ProcessGetHWnd($iPid, $iOption = 1, $sTitle = "", $iTimeout = 2000)
    Local $aReturn[1][1] = [[0]], $aWin, $hTimer = TimerInit()

    While 1

        ; Get list of windows
        $aWin = WinList($sTitle)

        ; Searches thru all windows
        For $i = 1 To $aWin[0][0]

            ; Found a window owned by the given PID
            If $iPid = WinGetProcess($aWin[$i][1]) Then

                ; Option 0 or 1 used
                If $iOption = 1 OR ($iOption = 0 And $aWin[$i][0] <> "") Then
                    Return $aWin[$i][1]

                    ; Option 2 is used
                ElseIf $iOption = 2 Then
                    ReDim $aReturn[UBound($aReturn) + 1][2]
                    $aReturn[0][0] += 1
                    $aReturn[$aReturn[0][0]][0] = $aWin[$i][0]
                    $aReturn[$aReturn[0][0]][1] = $aWin[$i][1]
                EndIf
            EndIf
        Next

        ; If option 2 is used and there was matches then the list is returned
        If $iOption = 2 And $aReturn[0][0] > 0 Then Return $aReturn

        ; If timed out then give up
        If TimerDiff($hTimer) > $iTimeout Then ExitLoop

        ; Waits before new attempt
        Sleep(Opt("WinWaitDelay"))
    WEnd

    ; No matches
    SetError(1)
    Return 0
EndFunc   ;==>_ProcessGetHWnd

Also providing the C# code in next post.

Share this post


Link to post
Share on other sites

C# code which I am using to attach a cmd window to winform.

Problem I am facing is when I click the command button the string should be passed to the cmd window.

here is the code:

/*
 * Created by SharpDevelop.
 * User: biggy
 * Date: 04/10/2007
 * Time: 10:32
 * 
 * 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.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;
using System.Threading;

namespace picBoxApp
{
    /// <summary>
    /// Description of MainForm.
    /// </summary>
    public partial class MainForm : Form
    {
        public MainForm()
        {
            //
            // The InitializeComponent() call is required for Windows Forms designer support.
            //
            InitializeComponent();
            
            //
            // TODO: Add constructor code after the InitializeComponent() call.
            //
            // Killing a process
            // Process p= Process.GetProcessById(PID);
            // p.Kill();
            
        }
        
        
        void Timer1Tick(object sender, EventArgs e)
        {
            System.IntPtr winParent;
            System.IntPtr x;
            System.IntPtr winHandle = Usr32.FindWin("ConsoleWindowClass", "MY OWN CONSOLE");
            if(winHandle != System.IntPtr.Zero)
            {
                winParent = Usr32.GetParent(winHandle);
                x = Usr32.SetParent(winHandle, this.pictureBox1.Handle);
                x = Usr32.SetWindowPos(winHandle, 1, -5, -32, 649, 445, 0);
                this.timer1.Enabled = false;
            }
        }
        
        void BtnExitClick(object sender, EventArgs e)
        {
            //System.Diagnostics.Process.GetProcessById
            //this.timer1.Enabled = false;
            if (Usr32.MYCMDPID != 0)
            {
                try
                {
                    System.Diagnostics.Process p = System.Diagnostics.Process.GetProcessById(Usr32.MYCMDPID);
                    if (p != null)
                    {
                        p.Kill();
                    }
                }
                catch 
                {
                    MessageBoxEx.Show("Process terminated Abnormally", "Error",200);
                }
            }
            Application.Exit();
            
            
        }
        
        
        void BtnStartClick(object sender, System.EventArgs e)
        {
            //MessageBox.Show("Hello World", "Hello");
            //string cmd1 = "generate business card";
            //MessageBox.Show (cmd1 , "Command One");
            //string pid;
            
            //uint pid;
            //CmdWindow pid = new CmdWindow();
            
            if (Usr32.MYCMDPID != 0)
            {
                try
                {
                    System.Diagnostics.Process p = System.Diagnostics.Process.GetProcessById(Usr32.MYCMDPID);
                    if (p != null)
                    {
                        p.Kill();
                    }
                }
                catch 
                {
                    MessageBoxEx.Show("Starting a new Process!", "Error",2000);
                }
            }
            Process mycmd = new Process();
            mycmd.StartInfo.UseShellExecute = false;
            mycmd.StartInfo.Arguments += " /K TITLE MY OWN CONSOLE";
            mycmd.StartInfo.FileName = "cmd.exe";
            mycmd.StartInfo.CreateNoWindow = false;
            //mycmd.StartInfo.ErrorDialog = false;
            //mycmd.StartInfo.RedirectStandardError = true;
            //mycmd.StartInfo.RedirectStandardInput = true;
            //mycmd.StartInfo.RedirectStandardOutput = true;
            //mycmd.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            // Do not show command prompt window separately
            //psi.CreateNoWindow = true;
            //psi.WindowStyle = ProcessWindowStyle.Hidden;
            //redirect all standard inout to program
            

            mycmd.Start();
            
            this.timer1.Enabled = true;
            //mycmd.Close();
            //MessageBox.Show("Process Id : " + bob.Id + " and Process Name : " + bob.ProcessName , "Process Details");
            //link the streams to standard inout of process
            //StreamWriter inputWriter = mycmd.StandardInput;
            //StreamReader outputReader = mycmd.StandardOutput;
            //StreamReader errorReader = mycmd.StandardError;
            //send command to cmd prompt and wait for command to execute with thread sleep
            //inputWriter.WriteLine("DIR\r\n");

            Usr32.MYCMDPID = mycmd.Id;

            
            
        }
        void BtnCommandClick(object sender, System.EventArgs e)
        {
            
            Process p = Process.GetProcessById(Usr32.MYCMDPID);
            IntPtr pid;
            pid = p.Handle;
            Usr32.SetForegroundWindow(pid);
            //Thread.Sleep(300);
            SendKeys.Send("DIR \r\n");
            
        }
        
        
        
        void ChkBoxConsoleCheckedChanged(object sender, EventArgs e)
        {
            string mytitle;
            if (chkBoxConsole.Checked)
            {
                mytitle = "MY OWN CONSOLE";
                Usr32.AllocConsole();
                System.Console.Title = mytitle;
                    
                
            }
            else
                Usr32.FreeConsole();

        }
        
        void BtnAttachConsoleClick(object sender, EventArgs e)
        {
            this.timer1.Enabled = true;
        }
    }
        public class Usr32
    {
        #region Class Variables
        public  const int SM_CXSCREEN=0;
        public  const int SM_CYSCREEN=1;
        public  static int MYCMDPID=0;
        #endregion    
    
        #region Class Functions
        [DllImport("user32.dll", EntryPoint="FindWindow")]
        public static extern IntPtr FindWin(string lpClassName, string lpWindowName);
    
        [DllImport("user32.dll", EntryPoint="SetParent")]
        public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
    
        [DllImport("user32.dll", EntryPoint="GetParent")]
        public static extern IntPtr GetParent(IntPtr hWnd);
    
        [DllImport("user32.dll", EntryPoint="SetWindowPos")]
        public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter , int x , int Y , int cx , int cy , int wFlags);
    
        [DllImport("user32.dll", EntryPoint="ShowWindow")]
        public static extern IntPtr ShowWindow(IntPtr hWnd, long nCmdShow);
    
        [DllImport("user32.dll", EntryPoint="CloseWindow")]
        public static extern IntPtr CloseWindow(IntPtr hWnd);
    
        [DllImport("user32.dll", EntryPoint="DestroyWindow")]
        public static extern IntPtr DestroyWindow(IntPtr hWnd);
        
        [DllImport("User32.dll")]
        public static extern int SetForegroundWindow(IntPtr hwnd);
        
        [DllImport("user32.dll")]
        public static extern uint SendMessage(IntPtr hWnd, uint nMessage, uint wParam, uint lParam);

        [DllImport("user32.dll")]
        public static extern uint PostMessage(IntPtr hWnd, uint nMessage, uint wParam, uint lParam);
        
        // my lines below are needed to make the console window visible
        
        [DllImport("kernel32.dll")]
        public static extern Boolean AllocConsole();
      
        [DllImport("kernel32.dll")]
        public static extern Boolean FreeConsole();

    
        #endregion
    }
        
        
        
}

Share this post


Link to post
Share on other sites

I'm going to start by saying that inserting another process' window in your own window as a child is a really messy thing to do and you should not be doing it. Ever. It would be much better, and easier, to either emulate the console yourself in your program or pipe commands to a real console window from your text box.

Share this post


Link to post
Share on other sites

I'm going to start by saying that inserting another process' window in your own window as a child is a really messy thing to do and you should not be doing it. Ever. It would be much better, and easier, to either emulate the console yourself in your program or pipe commands to a real console window from your text box.

Could you please explain me by giving an example?

Share this post


Link to post
Share on other sites

Perhaps it would be better if you explained what you wanted in the first place, and we can give you a better way to do it.

Share this post


Link to post
Share on other sites

Perhaps it would be better if you explained what you wanted in the first place, and we can give you a better way to do it.

I want to create an application (windows form application ) with button on it.

When I click a particular button a string should be sent to command prompt window.

Then when I click button2 then other string should be sent to the same command prompt window.

The command prompt should be open all the time. I want that the string should be sent only when button is clicked.

I tried using SendMessage that is user32.dll I am able to send string to Notepad.

But I am not able to send a string to command prompt.

/*
 * Created by SharpDevelop.
 * User: 
 * Date: 5/6/2011
 * Time: 4:06 PM
 * 
 * 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.Runtime.InteropServices;
using System.Diagnostics;


namespace SendToNotepad
{
    /// <summary>
    /// Description of MainForm.
    /// </summary>
    public partial class MainForm : Form
    {
        public MainForm()
        {
            //
            // The InitializeComponent() call is required for Windows Forms designer support.
            //
            InitializeComponent();
            
            //
            // TODO: Add constructor code after the InitializeComponent() call.
            //
        }
        
        
        public const int WM_CLOSE = 16;
        
        
        
        void BtnOpenClick(object sender, EventArgs e)
        {
            int hwnd = -1;
            //Get a handle for the Calculator Application main window
            hwnd = FindWindow(null, "Untitled - Notepad");
            if (hwnd == 0)
            {
                if (MessageBox.Show("Do you want to start it?", "Notepad", MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    Process ntp = Process.Start("notepad.exe");
                    
                }
            }
            else
            {
                MessageBox.Show("Application is running! Handle value is " + hwnd);
            }
        }
        
        
        
        [DllImport("User32.dll")]
        public static extern int FindWindow(string strClassName, string strWindowName);
        
        [DllImport("user32.dll", EntryPoint = "FindWindowEx")]
        public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
        
        [DllImport("User32.dll")]
        public static extern Int32 SendMessage(int hWnd, int Msg, int wParam, int lParam);
        
        [DllImport("User32.dll")]
        public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
        
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int SendMessage(int hWnd, int msg, int wParam, IntPtr lParam);
        

        
        
        void BtnCloseClick(object sender, EventArgs e)
        {
             int hwnd;

            //Get a handle for the Calculator Application main window
            
            hwnd = FindWindow(null, "Untitled - Notepad");

            //send WM_CLOSE system message
            if (hwnd != 0)
                SendMessage(hwnd, WM_CLOSE, 0, IntPtr.Zero);
            else
                MessageBox.Show("Application is not running!");
        }
        
        void BtnSendClick(object sender, EventArgs e)
        {
            int hwnd = 0;
            
            hwnd = FindWindow(null, "Untitled - Notepad");
            //SendMessage((IntPtr)hwnd, 0x000C, 0, textBox1.Text);
            
            if (hwnd != 0)
            {
                IntPtr hwndChild = IntPtr.Zero;
        
                hwndChild = FindWindowEx((IntPtr)hwnd, new IntPtr(0), null, null);
                if(hwndChild != IntPtr.Zero)
                {
                    SendMessage(hwndChild, 0x000C, 0, textBox1.Text);
                }
                else
                {
                    MessageBox.Show("Cannot Get the Child Handle :( !");
                }
                    
        
            }
        
            else
            
                MessageBox.Show("Application is not running!");
            
        }
    }
}

I think I am not able to get the correct value for 2nd option in the SendMessage function SendMessage(hwndChild, 0x000C, 0, textBox1.Text);

0x000C is for WM_SETTEXT

When tried on notepad I observed that when I send this string to Notepad the contents of notepad are replaced.

The strings are not getting appended to the previous string.

Let me know if you can provide me a link for all SendMessage related constants.

Also, if someone can tell how the ControlSend function in AutoIt was written then I think it will be very helpful.

Thanks and Regards,

Rahul

Share this post


Link to post
Share on other sites

Why are you sending text to the command prompt? What do you want the command prompt to do? I'm 90% sure there's a better way to do it.

Share this post


Link to post
Share on other sites

#10 ·  Posted

Why are you sending text to the command prompt? What do you want the command prompt to do? I'm 90% sure there's a better way to do it.

In my org, we have to set certain env using some variable and then only we can work on with other commands.

This is to be done in same command prompt window by entering some commands one by one in a sequence or randomly.

Moreover, I want that the user should be able to see what is going on in the command prompt window.

The strings that will be passed to the command prompt will hard coded in the program. These strings(commands) are very long and difficult to remember.

I do not want to collect the output of the command prompt to any file it should show the progress in the window itself.

Let me know if you can help.

I have successfully created application in Auto It v3 but I want to create it in csharp as autoit application is shown as virus in our org.

Moreover, I have embedded the cmd prompt inside the WinForm so that user cannot interact with close or minimize button :unsure:.

Thanks for your time!

Regards,

Rahul

(Currently I am trying to use PostMessage. I am successfull in sending certain characters to the command prompt but they are appearing in small case)

Share this post


Link to post
Share on other sites

#11 ·  Posted

Just set the environment variables from your C# program. Then run the sub processes and pipe their output to your program's window.

Forget running command prompt.

Share this post


Link to post
Share on other sites

#12 ·  Posted (edited)

Just set the environment variables from your C# program. Then run the sub processes and pipe their output to your program's window.

Forget running command prompt.

Thanks Richard!

I got the solution and I am able to send string to cmd prompt using SendMessage.

I used GetWindow method of Win32 API to find the child window handle of the picture box.

Now I am able to send commands to the command prompt and also I am able to embed the cmd prompt in my GUI using SetParent method.

Now I dont need a function like ControlSend anymore.

I will try your method also. If you can provide a simple C# code.

Thanks !

RAhooLm

Edited by rahoolm

Share this post


Link to post
Share on other sites

I shouldn't have to provide anything. What I described is basically Environment.SetEnvironmentVariable, and then creating new Process objects, letting them run to completion, and reading their output.

Share this post


Link to post
Share on other sites

If you want to see how ControlSend() internally works in Autoit then look at older sources

http://www.autoitscript.com/autoit3/files/archive/autoit/autoit-v3.1.0-src.exe

Share this post


Link to post
Share on other sites

RAhooLm,

Can you post the working code on Mar 16, 2011? I'm work on a program silmilar to yours; open up a Window form with command prompt embedded, and by clicking a button a commands will be send over to the command prompt. In my case. I will type in any command in the textbox and clicking the button will send the textbox text to the command prompt. And keep resending any number of commands I want...just like using a real command prompt.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0