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


Comments

  • run debug version error

    Posted by Legacy on 06/05/2003 12:00am

    Originally posted by: lanser

    error break this line:
    m_IScriptCtl.CreateDispatch(CLSID_ScriptControl)

    but release version is fine.

    Reply
  • Slick and simple. Good one!

    Posted by Legacy on 02/20/2003 12:00am

    Originally posted by: Vitaly

    Subj.

    Reply
  • titel

    Posted by Legacy on 02/19/2003 12:00am

    Originally posted by: Johann

    cool ! (immeno to tschto ja iskal, spasibo ;-))

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • The explosion in mobile devices and applications has generated a great deal of interest in APIs. Today's businesses are under increased pressure to make it easy to build apps, supply tools to help developers work more quickly, and deploy operational analytics so they can track users, developers, application performance, and more. Apigee Edge provides comprehensive API delivery tools and both operational and business-level analytics in an integrated platform. It is available as on-premise software or through …

  • Best-in-Class organizations execute on a strategy that supports the multi-channel nature of customer requests. These leading organizations do not just open up their service infrastructures to accommodate new channels, but also empower their teams to deliver an effective and consistent experience regardless of the channel selected by the customer. This document will highlight the key business capabilities that support a Best-in-Class customer engagement strategy.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds