HOWTO: Use Microsoft Word's CAPI Messaging Interface (CMI)
ID: Q190057
|
The information in this article applies to:
-
Microsoft Office 2000 Developer
-
Microsoft Visual C++, 32-bit Editions, version 5.0
-
Microsoft Word Developer's Kit, version 95
-
Microsoft Word 97 for Windows
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.
MORE INFORMATIONStep 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:
Q183758 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:
#include "capilib.h"
#include "wdcmds.h"
#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.
- On the Link tab in the Project Settings dialog box, add Win32cmi.lib to the "Object/Library modules" section.
- Copy Win32cmi.dll from the Word Developer's Kit disk to your Windows
directory.
- Compile and test.
When your application runs, click your dialog button. You should see
Microsoft Word start and the two lines added to the default document.
REFERENCES
Microsoft Word Developer's Kit: ISBN:1-55615-880-7
For additional information, please see the following
article(s) in the Microsoft Knowledge Base:
Q183165 What Do the cmiCommandDispatch() Errors Mean?
© Microsoft Corporation 1998, All Rights Reserved. Contributions by Joe Crump, Microsoft Corporation
Additional query words:
wdCommandDispatch wordbasic
Keywords : kbnokeyword kbVC500 kbWord kbGrpDSO kbOffice2000
Version : WINDOWS:95,97; winnt:5.0
Platform : WINDOWS winnt
Issue type : kbhowto
|