HOWTO: Do 16-Bit Automation in C++ Using VC 1.52
ID: Q194656
|
The information in this article applies to:
-
Microsoft Visual C++ for Windows, 16-bit edition, version 1.52
on the following platforms: NT, Win95, Win98
SUMMARY
This article shows you how to build a 16-bit Automation client written in
VC++ 1.52 to automate Microsoft Excel 97 and Microsoft Word 97, without
using MFC.
MORE INFORMATION
Although it is recommended that you port your code to 32-bit, some
implementations require 16-bit code for compatibility and integration with
other 16-bit applications. Please remember that if your code is going to
run on Windows 95, you must install DCOM. Without DCOM installed, 16-bit
automation clients on Windows 95 usually crash. This is also true for 32-
bit code that is called via a thunk from 16-bit.
Follow the steps below to create the example:
- Start Visual C++ 1.52. On the Project menu, click New, and name the
new project Auto16.
- Use the project type "Windows application (.EXE)." Don't select the
MFC option, and click OK.
- Add Main.cpp to your project, and click Close.
- On the File menu, click New.
- Paste the following code into the new file:
#include <windows.h>
#include <ole2.h> // Core OLE support
#include <dispatch.h> // IDispatch/Automation support
#include <olenls.h> // National Language support for LOCALE constants
#include <stdio.h>
#include <stdarg.h> // For variable-argument use in AutoWrap16()
#include <stdlib.h> // For _exit() in AutoWrap16()...
// Helpful functions...
char buf[8192];
void ShowMsg(char *str) {
MessageBox(NULL, str, "Hey", MB_OK);
}
void ShowErr(char *str, HRESULT hr) {
wsprintf(buf, "%s: %ld (%08lx)", str, hr, hr);
MessageBox(NULL, buf, "Error", MB_OK);
}
void PumpMessages(void) {
MSG msg;
while(PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Wrapper function for making generic 16-bit Automation calls...
HRESULT AutoWrap16(int autoType, VARIANT *pvResult,
IDispatch *pDisp, char *ptName, int cArgs...) {
// Begin variable-argument list...
va_list marker;
va_start(marker, cArgs);
if(!pDisp) {
MessageBox(NULL, "NULL IDispatch passed to AutoWrap()",
"Error", 0x10010);
_exit(0);
}
// Variables used...
DISPPARAMS dp = { NULL, NULL, 0, 0 };
DISPID dispidNamed = DISPID_PROPERTYPUT;
DISPID dispID;
HRESULT hr;
char buf[200];
// Get DISPID for name passed...
hr = pDisp->GetIDsOfNames(IID_NULL, &ptName, 1,
LOCALE_USER_DEFAULT, &dispID);
if(FAILED(hr)) {
wsprintf(buf, "IDispatch::GetIDsOfNames(\"%s\") failed w/err"
" 0x%08lx", ptName, hr);
MessageBox(NULL, buf, "AutoWrap()", 0x10010);
_exit(0);
return hr;
}
// Allocate memory for arguments...
VARIANT *pArgs = new VARIANT[cArgs+1];
// Extract arguments...
for(int i=0; i<cArgs; i++) {
pArgs[i] = va_arg(marker, VARIANT);
}
// Build DISPPARAMS
dp.cArgs = cArgs;
dp.rgvarg = pArgs;
// Handle special-case for property-puts!
if(autoType & DISPATCH_PROPERTYPUT) {
dp.cNamedArgs = 1;
dp.rgdispidNamedArgs = &dispidNamed;
}
// Make the call!
hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT,
autoType, &dp, pvResult, NULL, NULL);
if(FAILED(hr)) {
wsprintf(buf, "IDispatch::Invoke(\"%s\"=%08lx) failed w/err"
" 0x%08lx", ptName, dispID, hr);
MessageBox(NULL, buf, "AutoWrap()", 0x10010);
_exit(0);
return hr;
}
// End variable-argument section...
va_end(marker);
delete [] pArgs;
return hr;
}
void ExcelTest(void) {
// Get CLSID for our server...
CLSID clsid;
HRESULT hr = CLSIDFromProgID("Excel.Application", &clsid);
if(FAILED(hr)) {
ShowErr("CLSIDFromProgID() failed", hr);
return;
}
// Start the server...
IUnknown *pUnk = NULL;
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER,
IID_IUnknown, (void **)&pUnk);
if(FAILED(hr)) {
ShowErr("CoCreateInstance() failed", hr);
return;
}
// Query for IDispatch
IDispatch *pDispRoot = NULL;
hr = pUnk->QueryInterface(IID_IDispatch, (void **)&pDispRoot);
if(FAILED(hr)) {
ShowErr("QueryInterface(IID_IDispatch) failed", hr);
pUnk->Release();
return;
}
// Get App IDispatch
IDispatch *pDispApp = NULL;
{
VARIANT result;
VariantInit(&result);
AutoWrap16(DISPATCH_PROPERTYGET, &result, pDispRoot,
"Application", 0);
pDispApp = result.pdispVal;
}
// Set Visible...
{
VARIANT parm;
parm.vt = VT_I4;
parm.lVal = 1;
AutoWrap16(DISPATCH_PROPERTYPUT, 0, pDispApp, "Visible", 1,
parm);
}
// Get Workbooks collection...
IDispatch *pDispBooks = NULL;
{
VARIANT result;
VariantInit(&result);
AutoWrap16(DISPATCH_PROPERTYGET, &result, pDispApp,
"Workbooks", 0);
pDispBooks = result.pdispVal;
}
// Call Workbooks::Add()...
IDispatch *pDispBook = NULL;
{
VARIANT result;
VariantInit(&result);
AutoWrap16(DISPATCH_METHOD|DISPATCH_PROPERTYGET, &result,
pDispBooks, "Add", 0);
pDispBook = result.pdispVal;
}
// Get ActiveSheet...
IDispatch *pDispSheet = NULL;
{
VARIANT result;
VariantInit(&result);
AutoWrap16(DISPATCH_PROPERTYGET, &result, pDispBook,
"ActiveSheet", 0);
pDispSheet = result.pdispVal;
}
// Set a range of values...
for(int i=1; i<10; i++) {
for(int j=1; j<10; j++) {
// Get Range object (i,j)...
IDispatch *pDispRange = NULL;
{
VARIANT result;
VariantInit(&result);
VARIANT p1, p2;
p1.vt = p2.vt = VT_I4;
p1.lVal = i;
p2.lVal = j;
AutoWrap16(DISPATCH_PROPERTYGET, &result, pDispSheet,
"Cells", 2, p2, p1);
pDispRange = result.pdispVal;
}
// Set Range.Value to i*j
{
VARIANT parm;
parm.vt = VT_I4;
parm.lVal = i*j;
AutoWrap16(DISPATCH_PROPERTYPUT, 0, pDispRange, "Value",
1, parm);
}
// Release Range...
pDispRange->Release();
}
}
//ShowMsg("Preparing to clean up...");
// Clean up...
pDispSheet->Release();
pDispBook->Release();
pDispBooks->Release();
pDispApp->Release();
pDispRoot->Release();
pUnk->Release();
}
void WordBasicTest(void) {
// Get CLSID for our server...
CLSID clsid;
HRESULT hr = CLSIDFromProgID("Word.Basic", &clsid);
if(FAILED(hr)) {
ShowErr("CLSIDFromProgID() failed", hr);
return;
}
// Start the server...
IUnknown *pUnk = NULL;
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER,
IID_IUnknown, (void **)&pUnk);
if(FAILED(hr)) {
ShowErr("CoCreateInstance() failed", hr);
return;
}
// Query for IDispatch
IDispatch *pDispRoot = NULL;
hr = pUnk->QueryInterface(IID_IDispatch, (void **)&pDispRoot);
if(FAILED(hr)) {
ShowErr("QueryInterface(IID_IDispatch) failed", hr);
pUnk->Release();
return;
}
// Call AppShow
AutoWrap16(DISPATCH_METHOD, 0, pDispRoot, "AppShow", 0);
// Call FileNew
AutoWrap16(DISPATCH_METHOD, 0, pDispRoot, "FileNew", 0);
// Call Insert "Hello World"
{
VARIANT parm;
parm.vt = VT_BSTR;
parm.bstrVal = SysAllocString("Hello World!");
AutoWrap16(DISPATCH_METHOD, 0, pDispRoot, "Insert", 1, parm);
SysFreeString(parm.bstrVal);
}
// Clean up...
pDispRoot->Release();
pUnk->Release();
}
void WordApplicationTest(void) {
// Get CLSID for our server...
CLSID clsid;
HRESULT hr = CLSIDFromProgID("Word.Application", &clsid);
if(FAILED(hr)) {
ShowErr("CLSIDFromProgID() failed", hr);
return;
}
// Start the server...
IUnknown *pUnk = NULL;
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER,
IID_IUnknown, (void **)&pUnk);
if(FAILED(hr)) {
ShowErr("CoCreateInstance() failed", hr);
return;
}
// Query for IDispatch
IDispatch *pDispRoot = NULL;
hr = pUnk->QueryInterface(IID_IDispatch, (void **)&pDispRoot);
if(FAILED(hr)) {
ShowErr("QueryInterface(IID_IDispatch) failed", hr);
pUnk->Release();
return;
}
// Get App IDispatch
IDispatch *pDispApp = NULL;
{
VARIANT result;
VariantInit(&result);
AutoWrap16(DISPATCH_PROPERTYGET, &result, pDispRoot,
"Application", 0);
pDispApp = result.pdispVal;
}
// Set Visible...
{
VARIANT parm;
parm.vt = VT_I4;
parm.lVal = 1;
AutoWrap16(DISPATCH_PROPERTYPUT, 0, pDispApp, "Visible", 1,
parm);
}
// Get Documents collection...
IDispatch *pDispDocs = NULL;
{
VARIANT result;
VariantInit(&result);
AutoWrap16(DISPATCH_PROPERTYGET, &result, pDispApp,
"Documents", 0);
pDispDocs = result.pdispVal;
}
// Call Documents::Add()...
IDispatch *pDispDoc = NULL;
{
VARIANT result;
VariantInit(&result);
AutoWrap16(DISPATCH_METHOD|DISPATCH_PROPERTYGET, &result,
pDispDocs, "Add", 0);
pDispDoc = result.pdispVal;
}
// Get Selection...
IDispatch *pDispSelection = NULL;
{
VARIANT result;
VariantInit(&result);
AutoWrap16(DISPATCH_PROPERTYGET, &result, pDispApp,
"Selection", 0);
pDispSelection = result.pdispVal;
}
// Call TypeText "Hello World"
{
VARIANT parm;
parm.vt = VT_BSTR;
parm.bstrVal = SysAllocString("Hello World!");
AutoWrap16(DISPATCH_METHOD, 0, pDispSelection, "TypeText", 1,
parm);
SysFreeString(parm.bstrVal);
}
//ShowMsg("Preparing to clean up...");
// Clean up...
pDispSelection->Release();
pDispDoc->Release();
pDispDocs->Release();
pDispApp->Release();
pDispRoot->Release();
pUnk->Release();
}
// Program entry-point...
void main(void) {
// Initialize OLE Libraries...
OleInitialize(NULL);
// Perform the tests...
ShowMsg("Click me to Automate Excel.Application...");
ExcelTest();
ShowMsg("Click me to Automate Word.Application...");
WordApplicationTest();
ShowMsg("Click me to Automate Word.Basic...");
WordBasicTest();
// Uninitialize OLE Libraries...
OleUninitialize();
ShowMsg("Exiting...");
}
- On the File menu, click Save, and name the file Main.cpp.
- On the Options menu, click Project, then click Linker.
- Add ", ole2, ole2disp" (without the quotation marks) to the Libraries
edit box, then click OK.
- Now click Compiler, on the Project Options dialog box.
- Select the "Memory Model" category and change the Model to Large. This
avoids any problems concerning "near" and "far" pointers, because in the
large memory model, all pointers are far pointers. Click OK to close the
Compiler Options dialog box, and click OK again to close the Project
Options dialog box.
- On the Project menu, click Rebuild All, and click Yes when prompted to
create a default .def file.
- Click Rebuild All again to build the project.
- On the Project Menu, click Execute Auto16.exe to run the project.
You should see Microsoft Excel launch, and a series of values written to a
workbook. Then Microsoft Word will launch, and "Hello World" will be
written to a document. Finally, another document in Microsoft Word will
open, and "Hello World" will be written again.
© Microsoft Corporation 1999, All Rights Reserved. Contributions by Joe Crump, Microsoft Corporation
Additional query words:
Keywords : kb16bitonly kbAutomation kbDCOM kbVC152 kbWinOS95 kbWinOS98
Version : WINDOWS:1.52
Platform : WINDOWS
Issue type : kbhowto
|