Test Automation‎ > ‎

Friendly Fundamentals


Frienldy is a magical library!
It breaks through the walls of process!


https://github.com/Codeer-Software/Friendly.Windows

Please also see the API Reference.


The following samples are [ from / here ] downloadable. 


Here is some sample code to show how you can get started with Friendly

This is a perfect ordinary Windows Application that is manipulation target.
(There is no kind of trick.)
using System.Windows.Forms;

namespace
ProductProcess
{
   
public partial class SampleForm : Form
    {
       
int testValue;

       
private void SetTestValue(int value)
        {
            Text = value.ToString();
            testValue = value;
        }

    }
}


This is a test application (using VSTest):
using System;
using System.Diagnostics;
using System.Windows.Forms;
using
Microsoft.VisualStudio.TestTools.UnitTesting;
using Codeer.Friendly;
using Codeer.Friendly.Dynamic;
using
Codeer.Friendly.Windows;

namespace TestProcess
{
    [
TestClass]
   
public class BasicSample
    {
       
WindowsAppFriend _app;

        [
TestInitialize]
       
public void TestInitialize()
        {
            //attach to target process!
 
            _app = new WindowsAppFriend(Process.Start("ProductProcess.exe"));
        }

        [
TestCleanup]
       
public void TestCleanup()
        {
             Process process = 
Process.GetProcessById(_app.ProcessId);
            _app.Dispose();
             process.CloseMainWindow();
        }

        [
TestMethod]
       
public void TestSetValue()
        {
 
           //static method
            dynamic sampleForm = _app.Type<Application>().OpenForms[0];

            //instance method
            sampleForm.SetTestValue(5);

            //instance field
           
int value = sampleForm.testValue;

            //instance property
           
string text = sampleForm.Text;

           
Assert.AreEqual(5, value);
           
Assert.AreEqual("5", text);
        }
    }
}


Set up 

Match the Processor Architecture. (x86 or x64)

The target and test processes must use the same processor architectue.
If you are using VSTest, you can set this by using the Visual Studio menus as shown below.



Permissions

If the target process is running as an administrator, the test process must run as an administrator, too.
You can accomplish this by running Visual Studio as an administrator, as shown below:


Using Statements
using Codeer.Friendly;
using Codeer.Friendly.Dynamic;
using Codeer.Friendly.Windows;



Grammar

Connection to Execution Thread

Attach using WindowsAppFriend.
Operations can be executed on the main window thread:
public WindowsAppFriend(Process process);

Operation can also be executed on a specified window thread:
public WindowsAppFriend(IntPtr windowHandle);


Invoking Static Operations(Any OK
dynamic sampleForm1 = _app.Type<Application>().OpenForms[0];

dynamic sampleForm2 = _app.Type(typeof(Application)).OpenForms[0];

dynamic sampleForm3 = _app.Type().System.Windows.Forms.Application.OpenForms[0];

dynamic sampleForm4 = _app.Type("System.Windows.Forms.Application").OpenForms[0];

dynamic sampleForm5 = _app.Type<Control>().FromHandle(handle);


Invokeing Instance Operations
//method
sampleForm.SetTestValue(5);

//field
int value = sampleForm.testValue;

//property
string text = sampleForm.Text;
Variables are referenced from the target process.
You can access public and private members.


Instantiating New Objects

dynamic listBox1 = _app.Type<ListBox>()();


dynamic listBox2 = _app.Type(typeof(ListBox))();


dynamic listBox3 = _app.Type().System.Windows.Forms.ListBox();


dynamic listBox4 = _app.Type("System.Windows.Forms.ListBox")();


dynamic list = _app.Type<List<int>>()(new int[]{1, 2, 3, 4, 5});



Copy() and Null()
Dictionary<int, string> dic = new Dictionary<int, string>();
dic.Add(1,
"1");

// Object is serialized and a copy will be sent to the target process 
dynamic dicInTarget = _app.Copy(dic);
           
// Null is useful for out arguments
dynamic value = _app.Null();
dicInTarget.TryGetValue(1, value);
Assert.AreEqual("1", (string)value);


Rules for Arguments

You can use serializable objects and reference them in the target process.
If you use serializable objects, they will be serialized and a copy will be sent to the target process. 
// get SampleForm reference from the target process.
dynamic
sampleForm = _app.Type<Application>().OpenForms[0];

 // new instance in target process.
dynamic
listBox = _app.Type<ListBox>()();

// serializable object
listBox.Location =
new Point(10, 10); 

// serializable object
listBox.Items.Add("Item"); 

// reference to target process
sampleForm.Controls.Add(listBox);
 


Return Values
// referenced object exists in target process' memory. 
dynamic reference = sampleForm.Text;

// when you perform a cast, it will be marshaled from the target process.
string text = (string)reference;


Note the Casting Behavior
// OK
string cast = (string)reference;

// OK

string substitution = reference;

// No good. Result is false.
bool isString = reference is string;

// No good. Result is null.
string textAs = reference as string;

// No good. Throws an exception.
string.IsNullOrEmpty(reference);

// OK
string.IsNullOrEmpty((string)reference);


Special Casts

IEnumerable
foreach (dynamic form in _app.Type<Application>().OpenForms)
{
    form.BackColor =
Color.Pink;
}

AppVar
dynamic sampleForm = _app.Type("System.Windows.Forms.Control").FromHandle(_process.MainWindowHandle);

AppVar appVar = sampleForm;
appVar["Text"]("abc");
AppVar is part of the old style interface.
You will need to use AppVar if you use the old interface or if you can't use the .NET framework 4.0.
The old style sample code is pending translation, but the code is in C#.
Please have a look here if you are interested.


Async
Friendly operations are executed synchronously.
But you can use the Async class to execute them asynchronously.
// Async can be specified anywhere among the arguments.
Async async = new Async();
sampleForm.SetTestValue(async, 5);

// You can check whether it has completed.
if (async.IsCompleted)
{
   
//・・・
}

// You can wait for it to complete.
async.WaitForCompletion();

Properties and Fields
sampleForm.Text = "abc";
sampleForm.Text(new Async(), "abc");

// For [], use alias according to .NET framework rules.
dynamic list = _app.Copy(new List<string>(new string[] { "0", "1", "2" }));
list.set_Item(
new Async(), 1, "10");

dynamic
array = _app.Copy(new string[] { "0", "1", "2" });
array.Set(
new Async(), 1, "10");

Return Values
// Invoke getter.
Async async = new Async();

// Text will obtain its value when the operation completes.
dynamic text = sampleForm.Text(async);

// When the operation finishes, the value will be available.
async.WaitForCompletion();
string textValue = (string)text;

Dll injection.
[TestMethod]
public void Test()
{
   
dynamic mainWindow = app.Type<Application>().Current.Windows[0];
   
dynamic button = mainWindow.button;

   
//The code let tasrget process load current assembly.
    WindowsAppExpander.LoadAssembly(app, GetType().Assembly);

   
//You can use class defined in current assembly.
    dynamic observer = app.Type<Observer>()(button);

   
//Check click.
    button.OnClick();
   
Assert.IsTrue((bool)observer.Clicked);
}

class Observer
{
   
internal bool Clicked { get; set; }
   
internal Observer(Button button)
    {
        button.Click +=
delegate { Clicked = true; };
    }
}

Native dll methods.

[TestMethod]
public void TestRect()
{
   
WindowsAppExpander.LoadAssembly(_app, GetType().Assembly);

   
Process process = Process.GetProcessById(_app.ProcessId);
    _app.Type<
BasicSample>().MoveWindow(process.MainWindowHandle, 0, 0, 200, 200, true);

   
dynamic rectInTarget = _app.Type<RECT>()();
    _app.Type<
BasicSample>().GetWindowRect(process.MainWindowHandle, rectInTarget);
   
RECT rect = (RECT)rectInTarget;

   
Assert.AreEqual(0, rect.left);
   
Assert.AreEqual(0, rect.top);
   
Assert.AreEqual(200, rect.right);
   
Assert.AreEqual(200, rect.bottom);
}

[
DllImport("User32.dll")]
static extern bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw);

[DllImport("user32.dll")]
[
return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);

[
Serializable]
[
StructLayout(LayoutKind.Sequential)]
internal struct RECT
{
   
public int left;
   
public int top;
   
public int right;
   
public int bottom;
}




Upper Librarys

They are built on top of Friendly.
They wrap GUI manipulation by Friendly.