Click here to Skip to main content
15,892,809 members
Articles / Desktop Programming / WTL

Adding Macro Scripting language support to existing ATL/WTL Application

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
30 Sep 2002CPOL6 min read 152.7K   1.3K   70  
Shows how to use Microsoft Script Hosting inside existing ATL/WTL Application
///////////////////////////////////////////////////////////////////////////////
//	File:		AtlActiveScriptSite.cpp
//	Version:	1.00
//
//	Author:		Ernest Laurentin
//	E-mail:		elaurentin@sympatico.ca
//
//	This class implements an Active Script Hosting for client application
//
//	This code may be used in compiled form in any way you desire. This
//	file may be redistributed unmodified by any means PROVIDING it is
//	not sold for profit without the authors written consent, and
//	providing that this notice and the authors name and all copyright
//	notices remains intact.
//
//	An email letting me know how you are using it would be nice as well.
//
//	This file is provided "as is" with no expressed or implied warranty.
//	The author accepts no liability for any damage/loss of business that
//	this c++ class may cause.
//
//	Version history
///////////////////////////////////////////////////////////////////////////////
#ifndef _SCRIPTHOST_H_
#define _SCRIPTHOST_H_

#ifndef __cplusplus
	#error ATL requires C++ compilation (use a .cpp suffix)
#endif

#ifndef __ATLBASE_H__
	#error atlcom.h requires atlbase.h to be included first
#endif

#include <xstring>
#include <map>
#include <activscp.h>
using namespace std;

class str_lower : public binary_function<wstring, wstring, bool> {
public:
    bool operator()(const wstring& x, const wstring& y) const
	{
		return (x.compare( y.c_str() ) < 0);
	}
};

typedef map<wstring, LPUNKNOWN, str_lower> IUnknownPtrMap;

#define ACTIVS_HOST		L"ScriptHost"
#define SCRIPTITEM_NAMEDITEM		(SCRIPTITEM_ISSOURCE | \
									 SCRIPTITEM_ISVISIBLE | \
									 SCRIPTITEM_GLOBALMEMBERS)

#define LANG_ENGLISH_NEUTRAL	(MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL))
#define LOCALE_SCRIPT_DEFAULT	(MAKELCID(LANG_ENGLISH_NEUTRAL, SORT_DEFAULT))

//////////////////////////////////////////////////////////////////////////////
// IActiveScriptSiteImpl
// "class T" must implement:
//		LPUNKNOWN T::GetObjectPointer(const WCHAR* pstrName)
template <class T>
class ATL_NO_VTABLE IActiveScriptSiteImpl : public IActiveScriptSite
{
protected:
public:
		///////////////////////////////////////////////////////////////////////////
		// O P E R A T I O N S
		///////////////////////////////////////////////////////////////////////////

		STDMETHOD(GetLCID)(LCID* plcid)
		{
			*plcid = LOCALE_SCRIPT_DEFAULT;
			return S_OK;
		}

		STDMETHOD(GetItemInfo)(
			/* [in] */ LPCOLESTR pstrName,
			/* [in] */ DWORD dwReturnMask,
			/* [out] */LPUNKNOWN* ppiunkItem,
			/* [out] */LPTYPEINFO* ppti)
		{
			if (dwReturnMask & SCRIPTINFO_ITYPEINFO)
			{
				if (!ppti)
					return E_INVALIDARG;
				*ppti = NULL;
			}

			if (dwReturnMask & SCRIPTINFO_IUNKNOWN)
			{
				if (!ppiunkItem)
					return E_INVALIDARG;
				*ppiunkItem = NULL;
			}

			// Required LPUNKNOWN T::GetObjectPointer(const WCHAR* pstrName)
			T* pT = static_cast<T*>(this);
			LPUNKNOWN lpUnknown = pT->GetObjectPointer( pstrName );
			ATLASSERT(NULL != lpUnknown);
			if (NULL == lpUnknown)
			{
				ATLTRACE("No IUnknown for item!\n");
				// This shouldn't ever happen, since we're providing the items
				return TYPE_E_ELEMENTNOTFOUND;
			}

			if (dwReturnMask & SCRIPTINFO_ITYPEINFO)
			{
				// Use IProvideClassInfo to get ITypeInfo of coclass!
				IProvideClassInfo *pci = NULL;
				HRESULT hr = lpUnknown->QueryInterface(IID_IProvideClassInfo, (void**)&pci);
				if (SUCCEEDED(hr) && pci)
				{
					hr = pci->GetClassInfo(ppti);
				}

				// Release interface
				if (pci)
					pci->Release();

				if (FAILED(hr))
					return E_FAIL;
			}

			if (dwReturnMask & SCRIPTINFO_IUNKNOWN)
			{
				*ppiunkItem = lpUnknown;
				(*ppiunkItem)->AddRef();    // because returning
			}

			return S_OK;
		}

        STDMETHOD(GetDocVersionString)(BSTR* /*pbstrVersion*/)
		{
			ATLTRACENOTIMPL(_T("IActiveScriptSiteImpl::GetDocVersionString\n"));
		}

        STDMETHOD(OnScriptTerminate)(const VARIANT* /*pvarResult*/, const EXCEPINFO* /*pexcepinfo*/)
		{
			ATLTRACE(_T("IActiveScriptSiteImpl::OnScriptTerminate\n"));
			return S_OK;
		}

        STDMETHOD(OnStateChange)(SCRIPTSTATE ssScriptState)
		{
			ATLTRACE(_T("IActiveScriptSiteImpl::OnStateChange\n"));
			return S_OK;
		}

        STDMETHOD(OnScriptError)(IActiveScriptError* /*pse*/)
		{
			ATLTRACE(_T("IActiveScriptSiteImpl::OnScriptError\n"));
			return S_OK;
		}

        STDMETHOD(OnEnterScript)()
		{
			ATLTRACE(_T("IActiveScriptSiteImpl::OnEnterScript\n"));
			return S_OK;
		}

        STDMETHOD(OnLeaveScript)()
		{
			ATLTRACE(_T("IActiveScriptSiteImpl::OnLeaveScript\n"));
			return S_OK;
		}
};


//////////////////////////////////////////////////////////////////////////////
// IActiveScriptSiteWindowImpl
// "class T" must implement:
//		LPUNKNOWN T::GetBaseWindow()
template <class T>
class ATL_NO_VTABLE IActiveScriptSiteWindowImpl : public IActiveScriptSiteWindow
{
protected:
public:
		///////////////////////////////////////////////////////////////////////////
		// O P E R A T I O N S
		///////////////////////////////////////////////////////////////////////////

		STDMETHOD(EnableModeless)(BOOL /*fEnable*/)
		{
			return S_OK;
		}

		STDMETHOD(GetWindow)(HWND* phWnd)
		{
			T* pT = static_cast<T*>(this);
			*phWnd = pT->GetBaseWindow();
			return S_OK;
		}
};


//////////////////////////////////////////////////////////////////////////////
// IActiveScriptHostImpl
template <class T>
class ATL_NO_VTABLE IActiveScriptHostImpl :
		public IActiveScriptSiteImpl<IActiveScriptHostImpl>,
		public IActiveScriptSiteWindowImpl<IActiveScriptHostImpl>
{
public:
// Constructors
	IActiveScriptHostImpl()
	{
	}
	virtual ~IActiveScriptHostImpl()
	{
		ReleaseObjects();
	}

	void ReleaseObjects()
	{
		if (m_pAxsScript != NULL)
			m_pAxsParse = NULL;

		if (m_pAxsScript != NULL)
		{
			m_pAxsScript->Close();
			m_pAxsScript = NULL;
		}
	}

	///////////////////////////////////////////////////////////////////////////
	// O V E R R I D E
	///////////////////////////////////////////////////////////////////////////

	virtual LPUNKNOWN GetObjectPointer(const WCHAR* pstrName)
	{
		LPUNKNOWN lpUnknown = NULL;
		wstring sNamedItem = pstrName;
		IUnknownPtrMap::iterator iter = m_mapNamedItems.find( sNamedItem );
		if ( iter != m_mapNamedItems.end())
			lpUnknown = (*iter).second;
		return lpUnknown;
	}

	virtual HWND GetBaseWindow()
	{
		return GetDesktopWindow();
	}

	///////////////////////////////////////////////////////////////////////////
	// O P E R A T I O N S
	///////////////////////////////////////////////////////////////////////////

	///////////////////////////////////////////////////////////////////////////
	// CreateEngine
	BOOL CreateEngine(LPCOLESTR pstrProgID)
	{
		// Auto deletion of script engine
		ReleaseObjects();

		CLSID clsid;
		// Search for LanguageID and create instance of script engine
		if (SUCCEEDED(CLSIDFromProgID(pstrProgID, &clsid)))
		{
			// If this happens, the scripting engine is probably not properly registered
			HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_IActiveScript, (void **)&m_pAxsScript);
			// Script Engine must support IActiveScriptParse for us to use it
			if (SUCCEEDED(hr))
				hr = m_pAxsScript->QueryInterface(IID_IActiveScriptParse, (void **)&m_pAxsParse);

			if (SUCCEEDED(hr))
				hr = m_pAxsScript->SetScriptSite( (IActiveScriptSiteImpl<IActiveScriptHostImpl>*) this);

			// InitNew the object:
			if (SUCCEEDED(hr))
				hr = m_pAxsParse->InitNew();

			// Store 'Host' object
			wstring sNamedItem = ACTIVS_HOST;
			T* pT = static_cast<T*>(this);
			m_mapNamedItems.insert(IUnknownPtrMap::value_type(sNamedItem,pT->GetUnknown()) );

			// Add Top-level Global Named Item
			if (SUCCEEDED(hr))
				hr = m_pAxsScript->AddNamedItem(ACTIVS_HOST, SCRIPTITEM_NAMEDITEM);

			if (SUCCEEDED(hr))
				hr = m_pAxsScript->SetScriptState(SCRIPTSTATE_STARTED);

			if (SUCCEEDED(hr))
				return TRUE;

			m_mapNamedItems.erase( sNamedItem );
			ReleaseObjects();
		}
		return FALSE;
	}

	///////////////////////////////////////////////////////////////////////////
	// AddScriptItem
	BOOL AddScriptItem(LPCOLESTR pstrNamedItem, LPUNKNOWN lpUnknown,
		DWORD dwFlags = SCRIPTITEM_NAMEDITEM)
	{
		// Add Top-level Global Named Item
		if (m_pAxsScript != NULL && lpUnknown != NULL)
		{
			HRESULT hr;
			USES_CONVERSION;

			// Insert object into map
			wstring sNamedItem = pstrNamedItem;
			m_mapNamedItems.insert(IUnknownPtrMap::value_type(sNamedItem,lpUnknown) );

			hr = m_pAxsScript->AddNamedItem(pstrNamedItem, dwFlags);
			// Connected to object sink-interface
			if (SUCCEEDED(hr))
				hr = m_pAxsScript->SetScriptState(SCRIPTSTATE_CONNECTED);

			if (SUCCEEDED(hr))
				return TRUE;

			m_mapNamedItems.erase( sNamedItem );
		}
		return FALSE;
	}

	///////////////////////////////////////////////////////////////////////////
	// AddScriptCode
	BOOL AddScriptCode(LPCOLESTR pstrScriptCode)
	{
		if (m_pAxsScript != NULL)
		{
			HRESULT hr;
			EXCEPINFO ei = { 0 };
			hr = m_pAxsParse->ParseScriptText(pstrScriptCode, 0, 0, 0, 0, 0, 
								SCRIPTTEXT_ISPERSISTENT|SCRIPTTEXT_ISVISIBLE,
								0, &ei);
			if (SUCCEEDED(hr))
				return TRUE;
		}
		return FALSE;
	}

	///////////////////////////////////////////////////////////////////////////
	// AddScriptlet
	BOOL AddScriptlet(LPCOLESTR pstrDefaultName, LPCOLESTR pstrCode,
						LPCOLESTR pstrItemName, LPCOLESTR pstrEventName)
	{
		if (m_pAxsParse != NULL)
		{
			HRESULT hr;
			EXCEPINFO ei = { 0 };
			BSTR bstrName = NULL;
			hr = m_pAxsParse->AddScriptlet(pstrDefaultName, 
									pstrCode,
									pstrItemName, // name object
									pstrItemName, // no subobject - same as object
									pstrEventName, // event name - attach to 'pstrItemName'
									 L"", 
									 0, 
									 0,
									 0, 
									 &bstrName,
									 &ei);
			SysFreeString(bstrName);
			if (SUCCEEDED(hr))
				return TRUE;
		}
		return FALSE;
	}

	// Helper functions
	///////////////////////////////////////////////////////////////////////////
	// CreateObjectHelper
	LPDISPATCH CreateObjectHelper(LPCOLESTR bstrProgID)
	{
		LPDISPATCH lpObject = NULL;
		CLSID clsidObject = CLSID_NULL;
		HRESULT hr = CLSIDFromProgID(bstrProgID, &clsidObject );
		if( SUCCEEDED( hr ) )
		{
			// Only Local-Server is safe (run as separated process)
			hr = CoCreateInstance(clsidObject, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void**)&lpObject );
			if (FAILED(hr))
			{
				ATLTRACE("(Script) Failed to create object Return = 0x%x\n", hr);
			}
		}
		else
		{
			ATLTRACE("(Script) Invalid ProgID\n");
		}

		return lpObject;
	}
	
	///////////////////////////////////////////////////////////////////////////
	// ReadTextFileHelper
	WCHAR*	ReadTextFileHelper(LPCOLESTR strFileName)
	{
		WCHAR *pwszResult = NULL;
		char szFileNameA[MAX_PATH];
		if (wcstombs(szFileNameA, strFileName, MAX_PATH) == -1)
			return pwszResult;

		HANDLE hfile = CreateFileA(szFileNameA, GENERIC_READ,
							   FILE_SHARE_READ, 0, OPEN_EXISTING,
							   FILE_ATTRIBUTE_NORMAL, 0);
		if (hfile != INVALID_HANDLE_VALUE)
		{
		 DWORD cch = GetFileSize(hfile, 0);
		 char *psz = (char*)CoTaskMemAlloc(cch + 1);

		 if (psz)
		 {
			 DWORD cb;
			 ReadFile(hfile, psz, cch, &cb, 0);
			 pwszResult = (WCHAR*)CoTaskMemAlloc((cch + 1)*sizeof(WCHAR));
			 if (pwszResult)
				 mbstowcs(pwszResult, psz, cch + 1);
			 pwszResult[cch] = 0;
			 CoTaskMemFree(psz);
		 }
		 CloseHandle(hfile);
		}
		return pwszResult;
	}

	///////////////////////////////////////////////////////////////////////////
	// DestroyDataHelper
	void DestroyDataHelper(LPVOID lpvData)
	{
		CoTaskMemFree(lpvData);
	}

	///////////////////////////////////////////////////////////////////////////
	// InvokeFuncHelper
	HRESULT InvokeFuncHelper(LPCOLESTR lpszName, VARIANT* pvarParams, int nParams, VARIANT* pvarRet = NULL)
	{
		HRESULT hr = E_FAIL;
		DISPID dispid;
		if (m_pAxsScript != NULL)
		{
			LPDISPATCH pDisp = NULL;
			DISPPARAMS dispparams = { pvarParams, NULL, nParams, 0};
			hr = m_pAxsScript->GetScriptDispatch(0, &pDisp);
			if (SUCCEEDED(hr))
				hr = pDisp->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpszName, 1, LOCALE_SCRIPT_DEFAULT, &dispid);
			if (SUCCEEDED(hr))
				hr = pDisp->Invoke(dispid, IID_NULL, LOCALE_SCRIPT_DEFAULT, DISPATCH_METHOD, &dispparams, pvarRet, NULL, NULL);
		}
		return hr;
	}

protected:
	CComPtr<IActiveScriptParse> m_pAxsParse;	// Active Script parse object
	CComPtr<IActiveScript>		m_pAxsScript;	// Active Script
	IUnknownPtrMap				m_mapNamedItems; // Named items map
};

#endif // _SCRIPTHOST_H_

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)
United States United States
Ernest is a multi-discipline software engineer.
Skilled at software design and development for all Windows platforms.
-
MCSD (C#, .NET)
Interests: User Interface, GDI/GDI+, Scripting, Android, iOS, Windows Mobile.
Programming Skills: C/C++, C#, Java (Android), VB and ASP.NET.

I hope you will enjoy my contributions.

Comments and Discussions