Click here to Skip to main content
15,893,486 members
Articles / Programming Languages / C++

COM in plain C, Part 6

Rate me:
Please Sign up or sign in to vote.
4.89/5 (32 votes)
22 Jul 2006CPOL25 min read 103.2K   2.4K   102  
How to write an ActiveX Script Host in C.
// Main functions for an example program that executes a VBscript.

#include <windows.h>
#include <stdio.h>
#include <initguid.h>
#include <objbase.h>
#include <activscp.h>
#include <tchar.h>
#include "IActiveScriptSite.h"
#include "extern.h"


// This is our VBscript that we will run. For the sake of simplicity,
// we hard-code it inside of our app. NOTE: It must be UNICODE text
// format (ie, wchar_t, not char) because the script engine's
// ParseScriptText expects that. All this script does is display
// a "Hello world" messagebox
static const wchar_t	VBscript[] = L"MsgBox \"Hello world\"";

// For getting the engine's GUID from registry
static const TCHAR		CLSIDStr[] = _T("CLSID");
static const TCHAR		ScriptEngineStr[] = _T("ScriptEngine");

// Error message box
static const TCHAR		ErrorStr[] = _T("Error");





/********************** display_COM_error() ********************
 * Displays a messagebox for a COM error.
 *
 * msg =		Format string for sprintf().
 * hr =			COM error number.
 *
 * NOTE: Total size of error msg must be < 256 TCHARs.
 */

void display_COM_error(LPCTSTR msg, HRESULT hr)
{
	TCHAR			buffer[256];

	wsprintf(&buffer[0], msg, hr);
	MessageBox(0, &buffer[0], &ErrorStr[0], MB_OK|MB_ICONEXCLAMATION);
}





/************************** runScript() ***********************
 * Runs our VBscript.
 *
 * guid =	GUID of the script engine that runs the script.
 *
 * NOTE: Our VBscript is hard-coded into the static variable
 * named VBscript[].
 *
 * If an error, this displays a message box.
 */

void runScript(GUID *guid)
{
	register HRESULT	hr;
	IActiveScriptParse	*activeScriptParse;
	IActiveScript		*activeScript;

	// Create an instance of the script engine, and get its IActiveScript object
	if ((hr = CoCreateInstance(guid, 0, CLSCTX_ALL, &IID_IActiveScript, (void **)&activeScript)))
		display_COM_error("Can't get engine's IActiveScript: %08X", hr);
	else
	{
		// Get the script engine's IActiveScriptParse object (which we can do from its
		// IActiveScript's QueryInterface since IActiveScriptParse is a
		// sub-object of the IActiveScript)
		if ((hr = activeScript->lpVtbl->QueryInterface(activeScript, &IID_IActiveScriptParse, (void **)&activeScriptParse)))
			display_COM_error("Can't get engine's IActiveScriptParse: %08X", hr);
		else
		{
			// Initialize the engine. This just lets the engine internally
			// initialize some stuff in preparation of us adding scripts to it
			// for the first time
			if ((hr = activeScriptParse->lpVtbl->InitNew(activeScriptParse)))
				display_COM_error("Can't initialize engine : %08X", hr);
			else
			{
				// Give the engine our IActiveScriptSite object. If all goes well,
				// the engine will call its QueryInterface (which will AddRef it)
				if ((hr = activeScript->lpVtbl->SetScriptSite(activeScript, (IActiveScriptSite *)&MyActiveScriptSite)))
					display_COM_error("Can't set our IScriptSite : %08X", hr);
				else
				{
					// Have the script engine parse the script and add it to its internal
					// list of scripts to run
					hr = activeScriptParse->lpVtbl->ParseScriptText(activeScriptParse, &VBscript[0], 0, 0, 0, 0, 0, 0, 0, 0);

					// NOTE: If the script engine has a problem parsing/tokenizing the script, it will
					// have called our IActiveScriptSite's OnScriptError() to display an error msg, so
					// we don't need to do that here
					if (!hr)
					{
						// Set engine's state to CONNECTED. Our above script will run now
						if ((hr = activeScript->lpVtbl->SetScriptState(activeScript, SCRIPTSTATE_CONNECTED)))
							display_COM_error("Engine can't connect events: %08X", hr);
					}
				}
			}

			// Release script engine's IActiveScriptParse
			activeScriptParse->lpVtbl->Release(activeScriptParse);
		}

		// We're supposed to Close() the engine before we Release() the IActiveScript
		activeScript->lpVtbl->Close(activeScript);

		// Release script engine's IActiveScript
		activeScript->lpVtbl->Release(activeScript);
	}
}





/************************ getEngineGuid() ***********************
 * Gets the GUID of the script engine associated with the
 * specified filename extension.
 *
 * extension =	The filename extension.
 * guidBuffer =	Where to return the engine's GUID.
 *
 * RETURNS: S_OK if success, other for error.
 *
 * NOTE: Displays a messagebox for an error.
 */

static HRESULT getEngineGuid(LPCTSTR extension, GUID *guidBuffer)
{
	wchar_t		buffer[100];
	HKEY		hk;
	DWORD		size;
	HKEY		subKey;
	DWORD		type;

	// See if this file extension is associated with an ActiveX script engine
	if (!RegOpenKeyEx(HKEY_CLASSES_ROOT, extension, 0, KEY_QUERY_VALUE|KEY_READ, &hk))
	{
		type = REG_SZ;
		size = sizeof(buffer);
		size = RegQueryValueEx(hk, 0, 0, &type, (LPBYTE)&buffer[0], &size);
		RegCloseKey(hk);
		if (!size)
		{
			// The engine set an association. We got the Language string in buffer[]. Now
			// we can use it to look up the engine's GUID

			// Open HKEY_CLASSES_ROOT\{LanguageName}
again:			size = sizeof(buffer);
			if (!RegOpenKeyEx(HKEY_CLASSES_ROOT, (LPCTSTR)&buffer[0], 0, KEY_QUERY_VALUE|KEY_READ, &hk))
			{
				// Read the GUID (in string format) into buffer[] by querying the value of CLSID
				if (!RegOpenKeyEx(hk, &CLSIDStr[0], 0, KEY_QUERY_VALUE|KEY_READ, &subKey))
				{
					size = RegQueryValueExW(subKey, 0, 0, &type, (LPBYTE)&buffer[0], &size);
					RegCloseKey(subKey);
				}
				else if (extension)
				{
					// If an error, see if we have a "ScriptEngine" key under here that contains
					// the real language name
					if (!RegOpenKeyEx(hk, &ScriptEngineStr[0], 0, KEY_QUERY_VALUE|KEY_READ, &subKey))
					{
						size = RegQueryValueEx(subKey, 0, 0, &type, (LPBYTE)&buffer[0], &size);
						RegCloseKey(subKey);
						if (!size)
						{
							RegCloseKey(hk);
							extension = 0;
							goto again;
						}
					}
				}
			}

			RegCloseKey(hk);

			if (!size)
			{
				// Convert the GUID string to a GUID and put it in caller's guidBuffer
				if ((size = CLSIDFromString(&buffer[0], guidBuffer)))
					display_COM_error("Can't convert engine GUID: %08X", size);
				return(size);
			}
		}
	}

	MessageBox(0, "Can't get engine GUID from registry", &ErrorStr[0], MB_OK|MB_ICONEXCLAMATION);
	return(E_FAIL);
}





/************************** main() **************************
 * Our EXE's entry point. This is called by Windows when our
 * EXE first starts up.
 */

int main(int argc, char **argv)
{
	HRESULT				hr;

	// Initialize COM DLL
	if ((hr = CoInitialize(0)))
		display_COM_error("Failed to initialize COM: %08X", hr);
	else
	{
		GUID	guidBuffer;

		// Initialize MyRealIActiveScriptSite object
		initIActiveScriptSiteObject();

		// Find the script engine to use for files that end with a .VBS extension.
		// NOTE: Microsoft's VBscript engine sets up an association in the
		// registry for this extension.
		if (!getEngineGuid(".vbs", &guidBuffer))
		{
			// Now that we've got the engine GUID, run the script
			runScript(&guidBuffer);
		}

		// Allow our IActiveScriptSite to free any resources
		MyActiveScriptSite.site.lpVtbl->Release((IActiveScriptSite *)&MyActiveScriptSite);
	}

	// Free COM
	CoUninitialize();

	return(0);
}

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
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions