You can add VBA-like scripting capability to your ATL application with
little overhead, using Microsoft ActiveX Scripting technologies. This
article demonstrates how to create a new ATL application, or modify an
existing one, that incorporates support for VBScript.
How to create an automation client project using ATL
Open your dialog in ResourceView, and add an edit-box where the user can type in some VBScript. Right-click on it, choose properties, and make it "Multiline" and "Want return."
Select New ATL Object from the Insert menu. Add a Simple Object named MyObject on the Names tab, and select Support Connection Points on the Attributes tab.
Add a new file called MyScriptSite.h to your application, and paste the following code into it:
Remove any code you have in your OnOK() dialog button handler, and add the following code:
// Instantiate script site & objects...
CMyScriptSite *pMySite = new CMyScriptSite;
CComObject<CMyObject> *pMyObject = new CComObject<CMyObject>;
// Register your type-library
ITypeLib *ptLib = 0;
HRVERIFY(LoadTypeLib(L"AtlClientApp.tlb", &ptLib), "LoadTypeLib");
// Initialize your IActiveScriptSite implementation with your
// object's ITypeInfo...
ptLib->GetTypeInfoOfGuid(CLSID_MyObject, &pMySite->m_pTypeInfo);
ptLib->Release();
// Initialize your IActiveScriptSite implementation with your
// script object's IUnknown interface...
HRVERIFY(pMyObject->QueryInterface(IID_IUnknown,
(void **)&pMySite->m_pUnkScriptObject), "IUnknown initialization");
// Start inproc script engine, VBSCRIPT.DLL
IActiveScript *pAS;
HRVERIFY(CoCreateInstance(CLSID_VBScript, NULL, CLSCTX_INPROC_SERVER,
IID_IActiveScript, (void **)&pAS),
"CoCreateInstance() for CLSID_VBScript");
// Get engine's IActiveScriptParse interface.
IActiveScriptParse *pASP;
HRVERIFY(pAS->QueryInterface(IID_IActiveScriptParse, (void **)&pASP),
"QueryInterface() for IID_IActiveScriptParse");
// Give engine your IActiveScriptSite interface...
HRVERIFY(pAS->SetScriptSite((IActiveScriptSite *)pMySite),
"IActiveScript::SetScriptSite()");
// Give the engine a chance to initialize itself...
HRVERIFY(pASP->InitNew(), "IActiveScriptParse::InitNew()");
// Add a root-level item to the engine's name space...
HRVERIFY(pAS->AddNamedItem(L"MyObject", SCRIPTITEM_ISVISIBLE |
SCRIPTITEM_ISSOURCE), "IActiveScript::AddNamedItem()");
// Get text from edit-box...
HWND editHwnd = GetDlgItem(IDC_EDIT1);
char szBuf[1024];
int len = ::GetWindowText(editHwnd, szBuf, 1024);
szBuf[len] = 0;
// Convert it to a wide-char string...
WCHAR wcBuf[1024];
memset(wcBuf, 0, sizeof(WCHAR)*1024);
MultiByteToWideChar(CP_ACP, 0, szBuf, strlen(szBuf), wcBuf, 1024);
// Parse the code scriptlet...
EXCEPINFO ei;
BSTR pParseText = ::SysAllocString(wcBuf);
HRVERIFY(pASP->ParseScriptText(pParseText, L"MyObject", NULL, NULL, 0,
0, 0L, NULL, &ei), "ParseScriptText");
::SysFreeString(pParseText);
// Set the engine state. This line actually triggers the execution
// of the script.
HRVERIFY(pAS->SetScriptState(SCRIPTSTATE_CONNECTED), "SetScriptState");
// Let user know script-run is finished...
::MessageBox(NULL, "Script finished, click me to fire event...", "",
MB_SETFOREGROUND);
// Fire an event...
pMyObject->Fire_MyEvent();
// Close script and release interfaces...
pAS->Close();
pASP->Release();
pAS->Release();
// Delete site & objects...
delete pMySite;
delete pMyObject;
return 0;
Add the following to the top of the same file (ClientDlg.h), just below the #include for atlhost.h:
Right-click on your IMyObjectEvents interface and add a method named MyEvent. Before going to the next step, you should Choose 'Rebuild All' from the Build menu, even though it won't completely build yet. This is so your project gets updated and ClassView will recognize your event in the next step.
In ClassView, right-click on your CMyObject class, and select Implement Connection Point. Check the IMyObjectEvents interface, and click OK.
Select Rebuild All from the Build menu and run the example. Type in the following example VBScript in the edit box, and click Run: