1

I'm trying to create C# Windows Form App that will insert data from CSV file to Sage 50 accounts application. I already created button that automatically sends login and password to application:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApp4
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        [DllImport("user32.dll")]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

        private void button1_Click(object sender, EventArgs e)
        {
            string login = "user";
            string pass = "password";
            IntPtr WindowHandle = FindWindow(null, "Logon");
            if (WindowHandle == IntPtr.Zero)
            {
                MessageBox.Show("Window doesn't exist");
                return;
            }
            SetForegroundWindow(WindowHandle);
            SendKeys.SendWait(login);
            SendKeys.SendWait("{TAB}");
            SendKeys.SendWait(pass);
            SendKeys.SendWait("{ENTER}");
        }

        private void button2_Click(object sender, EventArgs e)
        {
            IntPtr WindowHandle = FindWindow(null, "Sage 50 Accounts Professional");
            SetForegroundWindow(WindowHandle);
        }
    }
}

I tried to use SendMessage function to click button in Sage application and also used Spy++ to get the button ID, but I'm not sure if I used it right:

        private void button2_Click(object sender, EventArgs e)
        {
            IntPtr WindowHandle = FindWindow(null, "Sage 50 Accounts Professional");
            SetForegroundWindow(WindowHandle);

            const int WM_COMMAND = 0x0111;
            const int btn = 0x12EEA720;
            SendMessage(WindowHandle, WM_COMMAND, btn, 0);
        }

What should I do to make it work? How can I send actions to chosen buttons/labels etc.? I searched in MS docs but this seems unclear to me.

I wanted my app to click this button: enter image description here

6
  • 1
    I'm pretty sure Sage has an API. You really should use that instead. Commented Feb 4, 2020 at 11:51
  • You are right, but I wanted to create app like this and use similar code later with other external applications (that don't have API) as I need them to automate my work. This Sage automation will be only a part of whole app. Commented Feb 4, 2020 at 12:15
  • There are tools (the current buzz word is Robotic Process Automation) that do this. I expect it will be a very long, difficult and error prone endeavor for you to write this yourself. Commented Feb 4, 2020 at 12:18
  • Hi Ania, as I see it, you need to activate window of ext app and send key strokes right. Am I missing anything else ? Commented Feb 4, 2020 at 12:22
  • @Clint yes, I need to send key strokes and click buttons but I don't know how to show my app which textbox in external app is the target or how to click chosen button. I edited my question to show which button I'm trying to click from my C# app. Commented Feb 4, 2020 at 12:33

1 Answer 1

1

What should I do to make it work? How can I send actions to chosen buttons/labels etc.? I searched in MS docs but this seems unclear to me.

In your program you're sending message WM_COMMAND to "Sage 50 Accounts Professional window" and not a particular UI control such as a button or label

From MSDN

SendMessage() Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message.

Key primary steps involved in going about spy++ automation

  • Identify your application type, for investigating 64 bit apps you would launch spyxx_amd64.exe else you can use the regular spyxx.exe

  • First Investigate the Window Handler of the UI control, you wish to send message to. I have used spy++ to investigate button control and result in image below InvestigateButtonControl

  • Identifying the value of lpszWindow which is the 4th parameter for FindWindowEx() This can be tricky, there are times when you might not know the real button name, Some times it can be prefixed with an &ButtonText and other times it might not something different. In the above example, value of lpszWindow for Button is ButtonText

  • Identifying the value of 2nd Param of SendMessage() which is uInt32 Msg that can be found from Spy++. As per image below its value is 0x0201

  • Identifying the value of 1st Param which is Window hwnd Handle of the control whom you want to send the message to

Solution :


  • Console App that interacts with the WinForm Apps, (See Inline Comments in the code)

using Microsoft.Management.Infrastructure;
using System;
using System.Linq.Expressions;
using System.Runtime.InteropServices;

namespace Test
{

    public class Program 
    {
        [DllImport("user32.dll")]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);


        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static extern IntPtr SendMessage(IntPtr hWnd,UInt32 Msg, IntPtr wParam,IntPtr lParam);


        [DllImport("user32.dll")]
        static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

        [DllImport("user32.dll")]
        static extern IntPtr PostMessage(IntPtr hwndParent, int msg, int wParam, IntPtr lParam);

        const int BM_CLICK = 0x00F5; //message for Click which is a constant
        const int WM_LBUTTONDOWN = 0x0201; //message for Mouse down
        const int WM_LBUTTONUP = 0x0202; // message for Mouse up

        static void Main(string[] args)
        {


           //Get the MainWindow handle of the Form1 APp
            var windowHandle = FindWindow(null, "Form1");

            //Using the Handle To Bring it to foreground
            SetForegroundWindow(windowHandle);
            System.Threading.Thread.Sleep(2000);



           //Getting the ButtonHandle for the different UI Controls, last param is the ControlText displayed on the UI 
            var btnHandle = FindWindowEx(windowHandle, IntPtr.Zero, null, "ButtonText");
            var txtHandle = FindWindowEx(windowHandle, IntPtr.Zero, null, "TextBoxText");
            var lblHandle = FindWindowEx(windowHandle, IntPtr.Zero, null, "LabelText");
            var chkBoxHandle = FindWindowEx(windowHandle, IntPtr.Zero, null, "CheckBoxText");

            System.Threading.Thread.Sleep(2000);



            //With the UI handle identified, we are passing the message(2nd param) that we have identified from Spy++
            SendMessage(btnHandle, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
            System.Threading.Thread.Sleep(2000);

            SendMessage(chkBoxHandle, WM_LBUTTONDOWN, IntPtr.Zero, IntPtr.Zero);//ButtonDown
            SendMessage(chkBoxHandle, WM_LBUTTONUP, IntPtr.Zero, IntPtr.Zero);//ButtonUp
            System.Threading.Thread.Sleep(2000);

            SendMessage(txtHandle, WM_LBUTTONDOWN, IntPtr.Zero, IntPtr.Zero);
            SendMessage(txtHandle, WM_LBUTTONUP, IntPtr.Zero, IntPtr.Zero);

            System.Threading.Thread.Sleep(2000);

            SendMessage(lblHandle, WM_LBUTTONDOWN, IntPtr.Zero, IntPtr.Zero);
            SendMessage(lblHandle, WM_LBUTTONUP, IntPtr.Zero, IntPtr.Zero);




        }

    }
}



  • Simple Winform App to recreate the problem

WinForm Form1.cs

using System.Drawing;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp2
{
    public partial class Form1 : Form
    {

        public Form1()
        {
            InitializeComponent();

        }


        int counter = 0;
        private void button1_Click(object sender, System.EventArgs e)
        {

            textBox1.Text = counter++.ToString();
        }

        private void label1_Click(object sender, System.EventArgs e)
        {
            MessageBox.Show("You got me !!");
        }

    }
}

WinForm Form1.Designer.CS


using System.Drawing;
namespace WindowsFormsApp2
{
    partial class Form1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }
        #region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.checkBox1 = new System.Windows.Forms.CheckBox();
            this.label1 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(178, 84);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(318, 200);
            this.button1.TabIndex = 1;
            this.button1.Text = "ButtonText";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // textBox1
            // 
            this.textBox1.Location = new System.Drawing.Point(641, 34);
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(100, 22);
            this.textBox1.TabIndex = 2;
            this.textBox1.Text = "TextBoxText";
            // 
            // checkBox1
            // 
            this.checkBox1.AutoSize = true;
            this.checkBox1.Location = new System.Drawing.Point(400, 25);
            this.checkBox1.Name = "checkBox1";
            this.checkBox1.Size = new System.Drawing.Size(119, 21);
            this.checkBox1.TabIndex = 3;
            this.checkBox1.Text = "CheckBoxText";
            this.checkBox1.UseVisualStyleBackColor = true;
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(615, 132);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(46, 17);
            this.label1.TabIndex = 4;
            this.label1.Text = "LabelText";
            this.label1.Click += new System.EventHandler(this.label1_Click);

            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(800, 450);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.checkBox1);
            this.Controls.Add(this.textBox1);
            this.Controls.Add(this.button1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();
        }
        #endregion
        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.TextBox textBox1;
        private System.Windows.Forms.CheckBox checkBox1;
        private System.Windows.Forms.Label label1;
    }
}

Output:

The program would click Button then tick CheckBox then selects TextBox and then clicks Label

enter image description here

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you! I will check your solution and let know if it works
@Clint thanx for your answer.I searched two days for that. But all the solutions are not detailed, half or not working. But your is the best one. I tried and work fine. I am surprised owner of subject did not mark as solution. Also noone gives anypoint. First was is mine. Thanx you so much again.
@Gokhan, I'm glad it helped you and I'm sure you will help someone in the future. Have a nice day!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.