HOWTO: Do 16-Bit Automation in C++ Using VC 1.52

ID: Q194656


The information in this article applies to:


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:

  1. Start Visual C++ 1.52. On the Project menu, click New, and name the new project Auto16.


  2. Use the project type "Windows application (.EXE)." Don't select the MFC option, and click OK.


  3. Add Main.cpp to your project, and click Close.


  4. On the File menu, click New.


  5. 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...");
          } 


  6. On the File menu, click Save, and name the file Main.cpp.


  7. On the Options menu, click Project, then click Linker.


  8. Add ", ole2, ole2disp" (without the quotation marks) to the Libraries edit box, then click OK.


  9. Now click Compiler, on the Project Options dialog box.


  10. 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.


  11. On the Project menu, click Rebuild All, and click Yes when prompted to create a default .def file.


  12. Click Rebuild All again to build the project.


  13. 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.

(c) Microsoft Corporation 1998, All Rights Reserved. Contributions by Joe Crump, Microsoft Corporation

© Microsoft Corporation 1998, 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 

Last Reviewed: July 29, 1999