Click here to Skip to main content
15,896,316 members
Articles / Programming Languages / C#

Automating Windows Applications

Rate me:
Please Sign up or sign in to vote.
4.95/5 (137 votes)
1 Feb 2003CPOL11 min read 393.1K   12.3K   309  
A Windows application that does not export any program interface, may be converted to automation server with COM object(s) injected into the application process.
// AutomationClientDlg.cpp : implementation file
//

#include "stdafx.h"
#include "AutomationClient.h"
#include "AutomationClientDlg.h"

#include "DefineWindowMessages.h"	// Specific notification Windows message definition


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CAutomationClientDlg dialog

CAutomationClientDlg::CAutomationClientDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CAutomationClientDlg::IDD, pParent),
	  m_pAdvise(NULL), m_spHandler(NULL)
{
	//{{AFX_DATA_INIT(CAutomationClientDlg)
	m_sText = _T("");
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CAutomationClientDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAutomationClientDlg)
	DDX_Text(pDX, IDC_EDIT, m_sText);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAutomationClientDlg, CDialog)
	//{{AFX_MSG_MAP(CAutomationClientDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON_COPY_TEXT, OnButtonCopyText)
	ON_BN_CLICKED(IDC_BUTTON_FIND, OnButtonFind)
	ON_BN_CLICKED(IDC_BUTTON_APPEND_MENU, OnButtonAppendMenu)
	ON_BN_CLICKED(IDC_BUTTON_AUTOMATE, OnButtonAutomate)
	//}}AFX_MSG_MAP

	// Massage sent by sink object
	ON_MESSAGE(WM_SENTENCE_COMPLETED, OnSentenceCompleted) 
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CAutomationClientDlg message handlers

BOOL CAutomationClientDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here
	SetPreAutomationState();
	
	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CAutomationClientDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CAutomationClientDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CAutomationClientDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}



//
// Set dialog controls to "pre-automation" state
//
void CAutomationClientDlg::SetPreAutomationState()
{
	m_spHandler = NULL;
	m_sText.Empty();
	UpdateData( FALSE );
	GetDlgItem(IDC_BUTTON_AUTOMATE)->EnableWindow();
	GetDlgItem(IDC_BUTTON_APPEND_MENU)->EnableWindow( FALSE );
	GetDlgItem(IDC_BUTTON_FIND)->EnableWindow( FALSE );
	GetDlgItem(IDC_BUTTON_COPY_TEXT)->EnableWindow( FALSE );
}


//
// Set dialog controls to "automated" state
//
void CAutomationClientDlg::SetPostAutomationState()
{
	GetDlgItem(IDC_BUTTON_AUTOMATE)->EnableWindow( FALSE );
	GetDlgItem(IDC_BUTTON_APPEND_MENU)->EnableWindow();
	GetDlgItem(IDC_BUTTON_FIND)->EnableWindow();
	GetDlgItem(IDC_BUTTON_COPY_TEXT)->EnableWindow();
}



//////////////////////////////////////////////////////////////////////////////////////
//
// Specific includes and buttons handlers  
//

#include "ObjectROTToken.h"
#include "GetObjectFromROT.h"
#include "Advise.h"


//
#import "..\NotepadHandlerNotification\NotepadHandlerNotification.tlb"
using namespace NOTEPADHANDLERNOTIFICATIONLib;


const char g_szLoader[] =	// Loader application
#ifdef _DEBUG
	_T("LoaderD.exe");
#else
	_T("Loader.exe");
#endif


//
// Execute Loader application to run target application and 
//		inject DLL to target process
//
void ExecuteLoader()
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( &pi, sizeof(pi) );

    // Start the child process. 
    if ( CreateProcess(NULL,               // No module name (use command line). 
                      (LPTSTR)g_szLoader,  // Command line. 
                      NULL,                // Process handle not inheritable. 
                      NULL,                // Thread handle not inheritable. 
                      FALSE,               // Set handle inheritance to FALSE. 
                      0,                   // No creation flags. 
                      NULL,                // Use parent's environment block. 
                      NULL,                // Use parent's starting directory. 
                      &si,                 // Pointer to STARTUPINFO structure.
                      &pi)                 // Pointer to PROCESS_INFORMATION structure.
       ) 
		// Wait until child process exits.
		WaitForSingleObject( pi.hProcess, INFINITE );

    // Close process and thread handles. 
    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );
}

//
// Obtain proxy of the COM object embedded to target process 
//
BOOL CAutomationClientDlg::ObtainObjectFromROT()
{	
	HRESULT hr = E_FAIL;

#ifdef _MONIKER
	hr = GetObjectFromROT<IHandler>( (LPTSTR)g_szROTRegToken, &m_spHandler );
#else
	CLSID clsid;
	CLSIDFromProgID( g_szProgID, &clsid );
	IUnknownPtr spUnk = NULL;
	hr = GetActiveObject( clsid, NULL, &spUnk );

	if ( SUCCEEDED(hr) )
		m_spHandler = (IHandlerPtr)spUnk;
#endif
	
	return (NULL != m_spHandler);
}

//
// Obtain proxy of the COM object embedded to target process 
//    for the first time
//
BOOL CAutomationClientDlg::ObtainObjectFirstTime()
{
	for (int i=0; i<20; i++)
	{
		if ( ObtainObjectFromROT() )
			break;

		Sleep( 100 );
	}

	return (NULL != m_spHandler);
}


//
// Cancel button handler
//
void CAutomationClientDlg::OnCancel() 
{
	DeleteAdvise();

	CoUninitialize();		// uninitialize COM
	
	CDialog::OnCancel();
}

//
// Delete Advise object
//
void CAutomationClientDlg::DeleteAdvise()
{
	if ( NULL != m_pAdvise )
	{
		delete m_pAdvise;
		m_pAdvise = NULL;
	}
}

//
// Copy Text button handler
//
void CAutomationClientDlg::OnButtonCopyText() 
{
	BSTR bsText;
	try
	{
		m_spHandler->GetLastTypedCharSequence( &bsText );
		_bstr_t bstText( bsText, FALSE );

		m_sText = bsText;
		UpdateData( FALSE );
	}
	catch (...)
	{
		SetPreAutomationState();
	}
}


//
// Find button handler
//
void CAutomationClientDlg::OnButtonFind() 
{
	const int IDM_NOTEPAD_FIND = 3;

	try
	{
		m_spHandler->DoMenuCommand( IDM_NOTEPAD_FIND );
	}
	catch (...)
	{
		SetPreAutomationState();
	}
}


//
// Append Menu button handler
//
void CAutomationClientDlg::OnButtonAppendMenu() 
{
	try
	{
		m_spHandler->AppendMenu();
	}
	catch (...)
	{
		SetPreAutomationState();
	}
}


//
// Automate Notepad button handler
//
void CAutomationClientDlg::OnButtonAutomate() 
{
	// Execute Loader application to run target application and 
	//		inject DLL to target process
	ExecuteLoader();

	if ( FAILED(CoInitialize(NULL)) )	// initialize COM
		return;
	
	// Reset m_pAdvise 
	DeleteAdvise();

	if ( NULL == m_pAdvise && 
		 ObtainObjectFirstTime() /* get proxy of the COM object embedded to target process */)
	{
		// Actions to advise sink interface to COM object embedded in target process
		m_pAdvise = new CAdvise( __uuidof(IHandlerEvents) );
		if ( NULL != m_pAdvise )
		{
			// Implement sink interface
			IHandlerEventsPtr spHandlerEvents( __uuidof(Notification) ); 
				
			// Provide sink object with dialog window handle for subsequent notification 
			//		by message posting
			INotificationPtr spNotification = spHandlerEvents;
			spNotification->SetMainWnd( (DWORD)m_hWnd );
			
			// Actually advise sink interface to embedded COM object
			if ( SUCCEEDED(m_pAdvise->AdviseToServer(m_spHandler, spHandlerEvents)) )
				SetPostAutomationState();
		}
	}
}


//
// Notification message handler
//
void CAutomationClientDlg::OnSentenceCompleted(WPARAM wParam, LPARAM lParam) 
{
	if ( 0 != wParam )
	{
		TCHAR* szText = (LPTSTR)wParam;
		m_sText.Format( _T("%s"), szText );
		UpdateData( FALSE );
		delete szText;
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Israel Israel


  • Nov 2010: Code Project Contests - Windows Azure Apps - Winner
  • Feb 2011: Code Project Contests - Windows Azure Apps - Grand Prize Winner



Comments and Discussions