Adding Scripting Support to an Application

Environment: VC6 SP5, Windows 98 +, Microsoft Windows Script Control, Windows 2000 SP3


Sometimes, there is no need to implement full scripting support as explained in the articles about ActiveX script hosting. The ScriptControlMacro program shows the easy way to do it using Microsoft Script Control. There are also other interesting techniques in the application:

  • How to allow only one application instance and find its main window using a File Mapping

  • How to use English resources that are built into MFC4x.DLL and prevent the use of MFC4xLOC.DLL

  • How to store and load binary registry data using MFC

  • How to restore the last size and position of the application windows

  • How to use different fonts in the class derived from CEditView

  • How to show recent files list in the menu only when it is not empty

  • Reset newly created (untitled) documents counter

  • How to catch OLE exceptions and show error messages with extended information

Before You Start

This article assumes you’re familiar with COM, ActiveX controls, OLE Automation, and how to use them in MFC. There is a lot of corresponding material about these technologies; so, if you are not, read and come back later.

Basic Steps

  1. Create a new MFC application, including support for ActiveX controls.

  2. Create a dispatch classes from the Script Control type-library using ClassWizard.
  3. ClassWizard will generate header and implementation files for Script Control interfaces:

    // Machine generated IDispatch wrapper class(es) created with
    // ClassWizard

    // IScriptControl wrapper class

    class IScriptControl : public COleDispatchDriver
    // Operations
    void SetLanguage(LPCTSTR lpszNewValue);
    void SetSitehWnd(HWND hWnd);
    LPDISPATCH GetError();
    void AddObject(LPCTSTR Name, LPDISPATCH Object,
    BOOL AddMembers);
    void AddCode(LPCTSTR Code);

    // Machine generated IDispatch wrapper class(es) created with
    // ClassWizard

    #include “stdafx.h”

    #include “MSScriptControl.h”

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

    // IScriptControl operations

    void IScriptControl::SetLanguage(LPCTSTR lpszNewValue)
    static BYTE parms[] =
    parms, lpszNewValue);

    void IScriptControl::SetSitehWnd(HWND hWnd)
    static BYTE parms[] =
    parms, hWnd);

    LPDISPATCH IScriptControl::GetError()
    LPDISPATCH result;
    (void*)&result, NULL);
    return result;

    void IScriptControl::AddObject(LPCTSTR Name, LPDISPATCH Object,
    BOOL AddMembers)
    static BYTE parms[] =
    InvokeHelper(0x9c4, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
    Name, Object, AddMembers);

    void IScriptControl::AddCode(LPCTSTR Code)
    static BYTE parms[] =
    InvokeHelper(0x7d0, DISPATCH_METHOD, VT_EMPTY, NULL, parms,

  4. Remove unneeded IDispatch wrapper methods from classes.

  5. If you want to add your own functions to the scripts, add an Automation class using ClassWizard.

    ClassWizard will generate header and implementation files for the IDispatch interface:

    // ScriptControlMacroDispatch.h : interface of the
    // CScriptControlMacroDispatch class


    #if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000

    // CScriptControlMacroDispatch command target

    class CScriptControlMacroDispatch : public CCmdTarget

    CScriptControlMacroDispatch(); // protected constructor
    // used by dynamic creation

    // Overrides
    // ClassWizard generated virtual function overrides

    // Implementation

    //friend class CScriptControlMacroView;
    // Generated message map functions
    // NOTE – the ClassWizard will add and remove member
    // functions here.

    // Generated OLE dispatch map functions

    afx_msg void Test1();
    afx_msg void Test2();

    extern const IID IID_IScriptControlMacroDispatch;


    // Microsoft Visual C++ will insert additional declarations
    // immediately before the previous line.

    // 00E5_47F5_B176_214B2C7BF19A__INCLUDED_)

    // ScriptControlMacroDispatch.cpp : implementation of the
    // CScriptControlMacroDispatch class

    #include “stdafx.h”
    #include “ScriptControlMacroDispatch.h”

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

    // CScriptControlMacroDispatch

    IMPLEMENT_DYNCREATE(CScriptControlMacroDispatch, CCmdTarget)


    BEGIN_MESSAGE_MAP(CScriptControlMacroDispatch, CCmdTarget)
    // NOTE – the ClassWizard will add and remove mapping
    // macros here.


    BEGIN_DISPATCH_MAP(CScriptControlMacroDispatch, CCmdTarget)
    DISP_FUNCTION(CScriptControlMacroDispatch, “Test1”, Test1,
    DISP_FUNCTION(CScriptControlMacroDispatch, “Test2”, Test2,

    // Note: we add support for IID_IScriptControlMacroDispatch
    // to support typesafe binding from VBA. This IID must match
    // the GUID that is attached to the dispinterface in the
    // .ODL file.

    // {69AA5686-41AF-4CD9-AEAE-9DB88130E7C1}
    const IID IID_IScriptControlMacroDispatch =
    {0x69AA5686, 0x41AF, 0x4CD9, {0xAE, 0xAE, 0x9D, 0xB8, 0x81,
    0x30, 0xE7, 0xC1}};

    BEGIN_INTERFACE_MAP(CScriptControlMacroDispatch, CCmdTarget)
    IID_IScriptControlMacroDispatch, Dispatch)

    // CScriptControlMacroDispatch message handlers

    void CScriptControlMacroDispatch::Test1()
    // TODO: Add your dispatch handler code here

    AfxMessageBox(CString(_T(“\””)) + GetDispatchMap()->
    lpEntries->lpszName + _T(“\” method call of the
    \””) + RUNTIME_CLASS(CScriptControlMacroDispatch)->
    m_lpszClassName + _T(“\” class”), MB_ICONASTERISK);

    void CScriptControlMacroDispatch::Test2()
    // TODO: Add your dispatch handler code here

    AfxMessageBox(CString(_T(“\””)) + GetDispatchMap()->
    lpEntries[1].lpszName + _T(“\” method call
    of the \””) + RUNTIME_CLASS(
    m_lpszClassName + _T(“\” class”),

  6. Several customizations have been done in the generated code:

    1. unnecessary declarations and code have been removed;

    2. the application object variable was made globally accessible: extern CScriptControlMacroApp theApp;

    3. an MFC “hidden” function declaration was added: CString AFXAPI AfxStringFromCLSID( REFCLSID );

    4. the ID of the interface that is mapped to the CScriptControlMacroDispatch class was made globally accessible for using in AfxStringFromCLSID function: extern const IID IID_IScriptControlMacroDispatch;

  7. To easily support Unicode in all MFC applications, the following customization has been done in the AFX.H header file:

    // Win32 libraries

    // Start of customization
    #if !defined(_CONSOLE) && defined(_UNICODE)
    #pragma comment(linker, “/entry:wWinMainCRTStartup”)
    // End of customization

  8. To use the _WIN32_WINDOWS=0x400 preprocessor definition in all MFC applications, the following customization has been done in the AFXV_W32.H header file:

    #ifndef ALL_WARNINGS
    #pragma warning(disable: 4201) // winnt.h uses nameless
    // structs


    // Start of customization
    #ifndef _WIN32_WINDOWS
    // End of customization
    #define _WIN32_WINDOWS 0x0500
    // Start of customization
    // End of customization


Information on the Script Control can be found at the MSDN Scripting Site.

Additional information can be found in the following articles in the Microsoft Knowledge Base:

184739 INFO: Where to Obtain the Script Control

184977 FIX: ScriptControl Reports Invalid Language for VBScript in MFC

165967 PRB: Script Error Occurs When Referencing Non-variant Array

229669 HOWTO: Call Run() Method of the Microsoft Script Control in C++

More information:

Visual Programmer: Add Scripting to Your Apps with Microsoft ScriptControl – MSDN Magazine, June 2000


Download demo project – 10 Kb

Download source – 29 Kb

More by Author

Must Read