Adding Scripting Support to an Application

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

Introduction

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
    public:
    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__;
    #endif

    ///////////////////////////////////////////////////////////////
    // IScriptControl operations

    void IScriptControl::SetLanguage(LPCTSTR lpszNewValue)
    {
    static BYTE parms[] =
    VTS_BSTR;
    InvokeHelper(0x5dc, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL,
    parms, lpszNewValue);
    }

    void IScriptControl::SetSitehWnd(HWND hWnd)
    {
    static BYTE parms[] =
    VTS_I4;
    InvokeHelper(0x5de, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL,
    parms, hWnd);
    }

    LPDISPATCH IScriptControl::GetError()
    {
    LPDISPATCH result;
    InvokeHelper(0x5e3, DISPATCH_PROPERTYGET, VT_DISPATCH,
    (void*)&result, NULL);
    return result;
    }

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

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

  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 !defined(AFX_SCRIPTCONTROLMACRODISPATCH_H__FB55B5AF_00E5_
    47F5_B176_214B2C7BF19A__INCLUDED_)
    #define AFX_SCRIPTCONTROLMACRODISPATCH_H__FB55B5AF_00E5_47F5_
    B176_214B2C7BF19A__INCLUDED_

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

    ///////////////////////////////////////////////////////////////
    // CScriptControlMacroDispatch command target

    class CScriptControlMacroDispatch : public CCmdTarget
    {
    DECLARE_DYNCREATE(CScriptControlMacroDispatch)

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

    // Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CScriptControlMacroDispatch)
    //}}AFX_VIRTUAL

    // Implementation

    protected:
    //friend class CScriptControlMacroView;
    // Generated message map functions
    //{{AFX_MSG(CScriptControlMacroDispatch)
    // NOTE – the ClassWizard will add and remove member
    // functions here.
    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()
    // Generated OLE dispatch map functions
    //{{AFX_DISPATCH(CScriptControlMacroDispatch)

    afx_msg void Test1();
    afx_msg void Test2();
    //}}AFX_DISPATCH
    DECLARE_DISPATCH_MAP()
    DECLARE_INTERFACE_MAP()
    };

    extern const IID IID_IScriptControlMacroDispatch;

    ///////////////////////////////////////////////////////////////

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

    #endif // !defined(AFX_SCRIPTCONTROLMACRODISPATCH_H__FB55B5AF_
    // 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__;
    #endif

    ///////////////////////////////////////////////////////////////
    // CScriptControlMacroDispatch

    IMPLEMENT_DYNCREATE(CScriptControlMacroDispatch, CCmdTarget)

    CScriptControlMacroDispatch::CScriptControlMacroDispatch()
    {
    EnableAutomation();
    }

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

    END_MESSAGE_MAP()

    BEGIN_DISPATCH_MAP(CScriptControlMacroDispatch, CCmdTarget)
    //{{AFX_DISPATCH_MAP(CScriptControlMacroDispatch)
    DISP_FUNCTION(CScriptControlMacroDispatch, “Test1”, Test1,
    VT_EMPTY, VTS_NONE)
    DISP_FUNCTION(CScriptControlMacroDispatch, “Test2”, Test2,
    VT_EMPTY, VTS_NONE)
    //}}AFX_DISPATCH_MAP
    END_DISPATCH_MAP()

    // 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)
    INTERFACE_PART(CScriptControlMacroDispatch,
    IID_IScriptControlMacroDispatch, Dispatch)
    END_INTERFACE_MAP()

    ///////////////////////////////////////////////////////////////
    // 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(
    CScriptControlMacroDispatch)->
    m_lpszClassName + _T(“\” class”),
    MB_ICONASTERISK);
    }

  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”)
    #endif
    // 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

    #endif

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



References

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

Downloads



Download demo project – 10 Kb


Download source – 29 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read