Microsoft KB Archive/190057

= HOWTO: Use Microsoft Word's CAPI Messaging Interface (CMI) =

Article ID: 190057

Article Last Modified on 2/12/2007

-

APPLIES TO


 * Microsoft Office XP Developer Edition
 * Microsoft Office 2000 Developer Edition
 * Microsoft Visual C++ 6.0 Service Pack 5
 * Microsoft Visual C++ 5.0 Standard Edition
 * Microsoft Word Developer's Kit 95
 * Microsoft Word 97 Standard Edition

-



This article was previously published under Q190057



SUMMARY
As an alternative to automation using COM, you can use the CAPI Messaging Interface (CMI) to control Microsoft Word. CMI uses the same structures and functions as a Microsoft Word DLL add-in (WLL), but allows cross-process and asynchronous calls. To use CMI, you should have a copy of the latest Microsoft Word Developer's Kit (ISBN: 1-55615-880-7), and a 32-bit version of Microsoft Visual C++. This article lists the steps you need to take to build a CMI project, and demonstrates using CMI.



Step By Step Example
 Create a new dialog-based Win32 AppWizard (exe) project. Add a button to your dialog and a handler function for it. Follow steps 2 through 8 in the following Microsoft Knowledge Base article:

183758 WD: Build a Microsoft Word Add-in (WLL) Using Visual C++

  At the top of your dialog boxes .cpp, add the following lines to the end of #include list:
 * 1) include "capilib.h"
 * 2) include "wdcmds.h"
 * 3) include "wdfid.h"

extern "C" { _declspec(dllimport) short WINAPI cmiCommandDispatch(short  CommandID, short DlgOptions, short cArgs, LPWDOPR lpwdoprArgs,   LPWDOPR lpwdoprReturn); }                         In your button handler function, add the following code: // Start Word. STARTUPINFO si;
 * ZeroMemory(&si, sizeof(STARTUPINFO));

si.cb = sizeof(STARTUPINFO); PROCESS_INFORMATION pi;

if(::CreateProcess( NULL, "c:/progra~1/micros~1/office/winword.exe", NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi)) { // Wait for Word to become Idle. WaitForInputIdle(pi.hProcess, 3000); } else { ::MessageBeep(0); ::MessageBox(     NULL, "CreateProcess failed.\nCheck Path for WinWord.Exe.",      "Error", MB_SETFOREGROUND); return; }

// Put your window back in the foreground. SetForegroundWindow;

WCB wcb; // Your local WCB structure. int err; // Error/result.

// Call wdDrawLine - Creates a graphical line object in the document. InitWCB(&wcb, 0, 0, 0); err = cmiCommandDispatch(  wdDrawLine, 0, wcb.cArgs, wcb.wdoprArgs,   (LPWDOPR)&wcb.wdoprReturn); if(err) { ShowCMIError(err); return; }

// Call wdFormatDrawingObject - Changes shape & position of the line. InitWCB(&wcb, 0, 0, 0); AddShortDlgField(&wcb, 40, fidHorizontalPos, INPUT); AddShortDlgField(&wcb, 40, fidVerticalPos, INPUT); AddShortDlgField(&wcb, 180, fidHeight, INPUT); AddShortDlgField(&wcb, 80, fidWidth, INPUT); err = cmiCommandDispatch(  wdFormatDrawingObject, 2, wcb.cArgs, wcb.wdoprArgs,   (LPWDOPR)&wcb.wdoprReturn); if(err) { ShowCMIError(err); return; }

// *** Draw another line with opposite height & width. // Call wdDrawLine - Creates a graphical line object in the document. InitWCB(&wcb, 0, 0, 0); err = cmiCommandDispatch(  wdDrawLine, 0, wcb.cArgs, wcb.wdoprArgs,   (LPWDOPR)&wcb.wdoprReturn); if(err) { ShowCMIError(err); return; }

// Call wdFormatDrawingObject - Changes shape & position of the line. InitWCB(&wcb, 0, 0, 0); AddShortDlgField(&wcb, 40, fidHorizontalPos, INPUT); AddShortDlgField(&wcb, 40, fidVerticalPos, INPUT); AddShortDlgField(&wcb, 80, fidHeight, INPUT); AddShortDlgField(&wcb, 180, fidWidth, INPUT); err = cmiCommandDispatch(  wdFormatDrawingObject, 2, wcb.cArgs, wcb.wdoprArgs,   (LPWDOPR)&wcb.wdoprReturn); if(err) { ShowCMIError(err); return; }

// Sleep for a little bit so user can see what happened.
 * Sleep(1000);


 * MessageBox(NULL, "Cmi-Test: Click me to continue...",

"Notice", MB_SETFOREGROUND);

// Close Word.
 * PostThreadMessage(pi.dwThreadId, WM_QUIT, 0, 0);

// Wait for Word to exit.
 * WaitForSingleObject(pi.hProcess, 2000);

// Release handles to the process (these come from CreateProcess). // Note: The process will not close later, unless these are closed.
 * CloseHandle(pi.hThread);
 * CloseHandle(pi.hProcess);

// Done. SetForegroundWindow;


 * MessageBox(NULL, "Cmi-Test: All Done.", "Notice",

MB_SETFOREGROUND);                         Just before your button handler function, add this function:       void ShowCMIError(int err)      {      static char txt[1024];      char *msg;

// Check for general CMI errors. if((err >= 5001) && (err <= 5034)) { msg = "General CAPI error.\n" "More information: This code is defined and described in              WdError.h\n" "Suggestion: Lookup error in WdError.h, and modify              parameters to cmiCommandDispatch."; }     // Check for Win32Cmi.dll errors. else { switch (err) { case 0: msg = "Success"; break; case -1: msg = "Microsoft Word is invisible, not running, or" " hasn't completly loaded.\n" "Suggestion: Start Microsoft Word before" "executing."; break; case -2: msg = "Win32Cmi.dll's shared memory block not big" " enough.\n" "More Information: The default size is 131072.\n" "Suggestion: Recompile the win32cmi project with" " a bigger buffer."; break; case -3: msg = "Cannot obtain Microsoft Word's thread Id."; break; case -4: msg = "Win32Cmi.dll could not install its                          WH_GETMESSAGE" " hook."; break; case -5: msg = "The current instance of Microsoft Word was not" " the original instance hooked by Win32Cmi.dll.\n" "More Information: Win32Cmi.dll's hooked thread" " id doesn't match the current Microsoft Word\n" " thread id. Win32Cmi.dll is only designed to" " work with a specific instance of Microsoft" " Word.\n" "Suggestion 1: Use CMI with only one Microsoft" " Word instance.\n" "Suggestion 2: Use OLE Automation as an" " alternative to CMI.\n" "Suggestion 3: Modify the Win32Cmi project" " yourself to support multiple instances of" " Microsoft Word."; break; case -6: msg = "Win32Cmi.dll's call to CreateEvent failed."; break; case -7: msg = "Win32Cmi.dll's call to CreateFileMapping" " failed."; break; case -8: msg = "Win32Cmi.dll's call to MapViewOfFile                          failed."; break; case -9: msg = "Timeout (default=60s).                                       Win32Cmi.dll's call to" " WaitForSingleObject did not return " "WAIT_OBJECT_0.\n" "Suggestion 1: Try calling on a separate " " thread.\n" "Suggestion 2: Increase time-out value if " "applicable.\n" "Suggestion 3: Win32Cmi uses hooks for its " "implementation. If you also use hooks, or \n" " synchronization objects, watch out for deadlock" " scenarios."; break; default: msg = "Unknown error.\n" "More Information: Error was not a CMI error.\n" "Suggestion: Check for error and description in " "WdError.h"; break; }     }

sprintf(txt, "CMI Error %d\n%s", err, msg); ::MessageBox(NULL, txt, "CMI Error", MB_SETFOREGROUND); }                        Copy Win32cmi.lib from the Word Developer's Kit disk to your project directory.</li> On the Link tab in the Project Settings dialog box, add Win32cmi.lib to the "Object/Library modules" section.</li> Copy Win32cmi.dll from the Word Developer's Kit disk to your Windows directory.</li> Compile and test.</li></ol>

When your application runs, click your dialog button. You should see Microsoft Word start and the two lines added to the default document.

<div class="references_section">