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.
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
(http://support.microsoft.com/kb/183758/EN-US/
)
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.