Creating Scripting Objects From Visual C++ Application Objects

Applies to TestComplete 14.92, last modified on September 16, 2021
Information in this topic applies to desktop applications only.
Connected and Self-Testing applications are deprecated. These technologies will be removed in one of the future releases of TestComplete.
To create test code that runs from within your tested apps, use TestLeft, a SmartBear functional testing tool for developers.

This topics explains how you can expose objects of Visual C++ applications as scripting objects in TestComplete. The explanation applies to applications created using the following compilers:

  • Visual C++ 7.x (unmanaged code)
  • Visual C++ 2005 (unmanaged code)

The explanation is divided into the following sections:

Preparing Your Visual C++ Project

In order for you to expose objects of Visual C++ applications as TestComplete scripting objects, you need to get access to the TestComplete COM server. The easiest way to manage TestComplete via COM is to transform your application into a Connected Application. Connected Applications are created by linking special modules to your project. These modules provide routines that free you from having to manually write the code needed to connect to TestComplete via COM.

To learn how you can transform your Visual C++ application into a Connected one, see Creating Connected Applications in C++.

Creating Custom Scripting Objects

The next step is to define the object interface and class and write the implementation for the object’s methods and properties. Since the TestComplete architecture, including the object model, is COM-based, the custom scripting object’s class should be also a COM class. If you need to expose the application’s arbitrary non-COM object, you need to create a COM wrapper that provides access to this object. To learn how you can do this, please refer to the Visual C++ documentation.

Let’s add a sample COM object to the project. To do this:

  • Open your project in Microsoft Visual Studio IDE.

  • Right-click the project node in the Solution Explorer and select Add | Add Class from the context menu. This will call the Add Class dialog.

  • In the dialog, select the Visual C++ | ATL category in the tree on the left, then select ATL Simple Object in the templates list and press Open.

    Visual Studio may ask you whether to add ATL support to your project. If so, click Yes to continue.

  • In the resulting ATL Simple Object Wizard, specify the object name in the Short name field (for instance, SampleRuntimeObject). Press Finish.

Visual Studio will create a type library containing definitions of the COM class and its interface and add this type library to the project.

To add methods and properties to the object interface, click the interface node in Visual Studio’s Class View and select Add | New Method or Add | New Property, respectively. In our example, the object’s interface will contain one property, Text, and one method, ShowText:

Project's Class View
Tip: Help strings that you specify for methods and properties will be shown as a method (or a property) description in the Code Completion window.

As you add interface members, Visual Studio automatically generates stub implementations for these members. You only need to replace the stub code with the code that provides the desired functionality. In our example, the Text property will obtain and return a text string, and the ShowText method will display this string in a message box:

Visual C++

/* SampleRuntimeObject.h */

class ATL_NO_VTABLE CSampleRuntimeObject :
...
{
...
private:
    ATL::CComBSTR fText;
}



/* SampleRuntimeObject.cpp */

...

STDMETHODIMP CSampleRuntimeObject::get_Text(BSTR* pVal)
{
    return fText.CopyTo(pVal);
}

STDMETHODIMP CSampleRuntimeObject::put_Text(BSTR newVal)
{
    fText = newVal;
    return S_OK;
}

STDMETHODIMP CSampleRuntimeObject::ShowText(void)
{
    MessageBoxW(NULL, fText, L"", MB_OK);
    return S_OK;
}

Registering and Unregistering the Runtime Object

In order to add a custom object to the TestComplete object model, you need to register it in the TestComplete extensibility manager (see Creating Scripting Objects From Application Objects). In C++ Connected Applications, you can address this manager via the Manager object.

To register a custom scripting object, use the manager’s AddName method. The method call can be inserted in the application’s initialization routine, or in a routine that is called upon a specific event. In our example, we will add a button control to the application form and place the registration code in the button’s OnBnClicked event handler. This way, we will be able to easily re-register our object in subsequent TestComplete sessions while the application is running.

Below is a sample registration code:

Visual C++

/* SampleAppDlg.h */

...
const wchar_t RuntimeObjectName[] = L"SampleRuntimeObject";

// CSampleAppDlg dialog
class CSampleAppDlg : public CDialog
{
public:
    CSampleAppDlg(CWnd* pParent = NULL);
    ~CSampleAppDlg();

...

// Implementation

public:
    afx_msg void OnBnClickedButton1();

private:
    CComPtr<ISampleRuntimeObject>* fRuntimeObject;
    bool fRegistered;
};



/* SampleAppDlg.cpp */

...

#include "SampleRuntimeObject.h"
#include "C:\Program Files\SmartBear\TestComplete 14\Connected Apps\C++\script.h"
using namespace TestComplete;
IMPLEMENT_TESTCOMPLETE_GLOBAL_OBJECTS

...

CSampleAppDlg::CSampleAppDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CSampleAppDlg::IDD, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

    HRESULT hRes = CSampleRuntimeObject::CreateInstance(&fRuntimeObject);
    _ASSERTE(SUCCEEDED(hRes));
    fRegistered = false;
}

void CSampleAppDlg::OnBnClickedButton1()
{
    const wchar_t* ProjectPath = L"TCProject\\CustomRuntimeObject.pjs";
    try {
        wchar_t buffer[MAX_PATH];
        wchar_t* ch = 0;

        // Open a project suite in TestComplete
        TC["Visible"] = true;
        GetFullPathNameW (ProjectPath, MAX_PATH, buffer, &ch);
        TC["Integration"]["OpenProjectSuite"](buffer);

        // Register FRuntimeObject as a scripting object
        Manager["AddName"](RuntimeObjectName, false, (IDispatch*) fRuntimeObject);
        fRegistered = true;
    }
    catch (CInvokeException* e) {
        wchar_t buffer [MAX_PATH];
        swprintf (buffer, L"Could not register %s:\r\n%s", RuntimeObjectName, e->errBuffer);
        MessageBoxW(this->m_hWnd, buffer, L"", MB_OK);
    }
}

To unregister an object, use the RemoveName method of the TestComplete extensibility manager. In our example, we will place the unregistration code in the form’s destructor, so that the object is unregistered when the user exits the application by closing the form. Since the unregistration makes sense only if the object has been previously registered and if TestComplete is running, we check both these conditions before calling the RemoveName method:

Visual C++

/* SampleAppDlg.cpp */

CSampleAppDlg::~CSampleAppDlg()
{
    IUnknown* TC = NULL;
    CLSID ClassID;
    CLSIDFromProgID(TCApplication, &ClassID);
    HRESULT hres = ::GetActiveObject(ClassID, NULL, &TC);
    if ((fRegistered) & (hres == S_OK))
    {
        Manager["RemoveName"](RuntimeObjectName);
    }
    fRuntimeObject = NULL;
}

Finally, you can re-compile your application.

Using the Runtime Object in Scripts

After a custom scripting object is registered in TestComplete, it is displayed in the Code Completion window and can be used in scripts:

The SampleRuntimeObject object

You can get and set the object’s property values, call its methods and check their return values the same way you would with standard scripting objects such as TestedApps, Log and others. The only difference is that the processing is performed by your application rather than by TestComplete.

The code snippet below demonstrates how you can work with the sample scripting object we have created earlier in this topic. This object is available in TestComplete from the time you launch the application and click the button on its main form and until the application is closed. Note that, despite that the object is a part of a Visual C++ application, it can be used in scripts written in any scripting language, not only C++Script:

JavaScript, JScript

function Main()
{
  SampleRuntimeObject.Text = "Hello, world!";
  SampleRuntimeObject.ShowText();
}

Python

def Main():
  SampleRuntimeObject.Text = "Hello, world!"
  SampleRuntimeObject.ShowText()

VBScript

Sub Main
  SampleRuntimeObject.Text = "Hello, world!"
  SampleRuntimeObject.ShowText
End Sub

DelphiScript

procedure Main;
begin
  SampleRuntimeObject.Text := 'Hello, world!';
  SampleRuntimeObject.ShowText;
end;

C++Script, C#Script

function Main()
{
  SampleRuntimeObject["Text"] = "Hello, world!";
  SampleRuntimeObject["ShowText"]();
}

See Also

Creating Scripting Objects From Application Objects
Connected Applications - Overview
Creating Connected Applications in C++

Highlight search results