how to append text to mail body in lotus notes (C API) - lotus-notes

I have been trying to append some text to outgoing mail from lotus notes client. For this I have registered EM_MAILSENDNOTE with EM_REG_BEFORE event. On this event, I am appending some rich text to mail body.
But that text appears only in the sent folder mail, not at the receiving end. What I am missing?
code below referred from this document.
void appendRichTextToBody(NOTEHANDLE note_handle, const char *szString1) {
#define PARA_STYLE_ID 1
char itemname[] = "Body";
WORD wBuffLen; /* required buffer length */
BYTE *rt_field; /* allocated rich-text field */
BYTE *buff_ptr; /* position in allocated memory */
CDPABDEFINITION pabdef; /* rich-text paragraph style */
CDPARAGRAPH para; /* rich-text paragraph header */
CDPABREFERENCE ref; /* rich-text style reference */
CDTEXT cdtext; /* rich-text text header */
WORD wString1Len = strlen( szString1 ); /* Length of actual string */
WORD wString1BufLen = wString1Len + (wString1Len%2);
FONTIDFIELDS *pFontID; /* font definitions in text header */
DWORD rt_size; /* size of rich-text field */
STATUS error = NOERROR; /* return code from API calls */
wBuffLen = ODSLength( _CDPABDEFINITION ) +
ODSLength( _CDPARAGRAPH ) + ODSLength( _CDPABREFERENCE) +
ODSLength(_CDTEXT) + wString1BufLen;
rt_field = (BYTE *) malloc ( wBuffLen );
if( rt_field == (BYTE *)NULL ) {
return;
}
//* Keep a pointer to our current position in the buffer. */
buff_ptr = rt_field;
/* Initialize a CDPABDEFINITION structure.We use all defaults, except for centered
justification.*/
memset(&pabdef, 0, sizeof(CDPABDEFINITION));
pabdef.Header.Signature = SIG_CD_PABDEFINITION;
pabdef.Header.Length = ODSLength( _CDPABDEFINITION );
pabdef.PABID = PARA_STYLE_ID;
pabdef.JustifyMode = JUSTIFY_CENTER;
pabdef.LineSpacing = DEFAULT_LINE_SPACING;
pabdef.ParagraphSpacingBefore = DEFAULT_ABOVE_PAR_SPACING;
pabdef.ParagraphSpacingAfter = DEFAULT_BELOW_PAR_SPACING;
pabdef.LeftMargin = DEFAULT_LEFT_MARGIN;
pabdef.RightMargin = DEFAULT_RIGHT_MARGIN;
pabdef.FirstLineLeftMargin = DEFAULT_FIRST_LEFT_MARGIN;
pabdef.Tabs = DEFAULT_TABS;
pabdef.Tab[0] = DEFAULT_TAB_INTERVAL;
pabdef.Flags = 0;
pabdef.TabTypes = TAB_DEFAULT;
pabdef.Flags2 = 0;
/* Call ODSWriteMemory to convert the CDPABDEFINITION structure to
Domino and Notes canonical format and write the converted structure into
the buffer at location buff_ptr. This advances buff_ptr to the
next byte in the buffer after the canonical format strucure.
*/
ODSWriteMemory( &buff_ptr, _CDPABDEFINITION, &pabdef, 1 );
/* Put a paragraph header in the field. */
para.Header.Signature = SIG_CD_PARAGRAPH;
para.Header.Length = (BYTE) ODSLength( _CDPARAGRAPH );
ODSWriteMemory( &buff_ptr, _CDPARAGRAPH, &para, 1 );
/* Add the CDTEXT record to the field. A CDTEXT record consists
of a CDTEXT structure followed by a run of text. Initialize the
CDTEXT structure by filling in the signature and the length.
The CDTEXT structure also contains the font information that
controls how Domino and Notes displays this first run of text.
*/
cdtext.Header.Signature = SIG_CD_TEXT;
cdtext.Header.Length = ODSLength( _CDTEXT ) + wString1Len ;
pFontID = (FONTIDFIELDS *) &(cdtext.FontID);
pFontID->Face = FONT_FACE_SWISS;
pFontID->Attrib = ISBOLD;
pFontID->Color = NOTES_COLOR_BLUE;
pFontID->PointSize = 24;
ODSWriteMemory( &buff_ptr, _CDTEXT, &cdtext, 1 );
/* Write the actual characters of this first text run to the buffer.
Since the run of text may contian embedded null characters, use
memcpy, not strcpy. No need to terminate the run of text with a
null because the Header.Length member of the CDTEXT structure
specifies the length explicitly. Only copy the null if the
number of characters is odd.
*/
memcpy( (char *)buff_ptr, szString1, wString1BufLen );
/* Advance the pointer to the next even-byte boundary */
buff_ptr += wString1BufLen;
rt_size = (DWORD)(buff_ptr - rt_field);
if (error = NSFItemAppend( note_handle, 0, itemname, (WORD) strlen(itemname), TYPE_COMPOSITE, rt_field, rt_size ) ){
Log("%s: NSFItemAppend failed\n", __FUNCTION__);
return;
}
free( rt_field );
}
EDIT1:
Code to register event handler:
error = EMRegister(EM_MAILSENDNOTE, EM_REG_BEFORE,(EMHANDLER)MailSendNoteHandler, gRecursionID, &hMailSendNote);
error = EMRegister(EM_NSFNOTEUPDATEMAILBOX, EM_REG_BEFORE,(EMHANDLER)UpdateMailboxHandler, gRecursionID, &hNSFUpdateMailbox);
Event handler functions:
STATUS MailSendNoteHandler(EMRECORD FAR * pExRecord) {
VARARG_PTR ap = pExRecord->Ap;
DHANDLE hNote = VARARG_GET (ap, DHANDLE);
appendRichTextToBody(hNote, " Test Rich Text EM_MAILSENDNOTE BEFORE ");
return (EM_ERR_CONTINUE);
}
STATUS UpdateMailboxHandler(EMRECORD FAR * pExRecord) {
VARARG_PTR ap = pExRecord->Ap;
DHANDLE hNote = VARARG_GET (ap, DHANDLE);
appendRichTextToBody(hNote, " Test Rich Text EM_NSFNOTEUPDATEMAILBOX BEFORE ");
return (EM_ERR_CONTINUE);
}
I don't see any appended text at recipient side.
But the mail in Sent folders has both appended texts.

I presume that this is a client-side EM. Most mail processing code that I'm familiar with operates on the server-side instead, by hooking EM_NSFNOTEUPDATE in the mail.box file on the server. The trick there, though, is that you must prevent the router from sending the message before you are finished processing it, so the very first thing you do is you change the status of the message to "On Hold" so the router won't even try to mail it. Then you can do your work on it, and finally release it. (Usually on the server side, this involves signalling a separate server task to do the work, as you don't want to keep a main server thread from returning from NSFNoteUpdate while the work is done.)
Even with EM_BEFORE processing, you may need to do something similar on the client side in order to accomplish this. You may at least want to add EM_BEFORE and EM_AFTER event handlers for EM_NSFNOTEUPDATE to monitor the mail.box file to determine whether those events are firing before or after the EM_MAILSENDNOTE event fires.

I know of Domino C API EMs that add signatures via server not client to outbound SMTP emails, so this must be possible.
EM_MAILSENDNOTE can only be used on a client.
BTW If this does not make sense here, its because of the restrictions on this site, I cannot add a comment with more detail, I cannot add a link, I cannot add code !!!

Related

how to get null space in unitywebrequest and base64type

private IEnumerator GetData()
{
WWWForm form = new WWWForm();//php에 보낼 폼을 만듦
form.AddField("data", num);
UnityWebRequest request = new UnityWebRequest();
using (request =
UnityWebRequest.Post("http://localhost/LoadData.php", form))
{
yield return request.SendWebRequest();
if (request.isNetworkError)
{
Debug.Log(request.error);
}
else
{
Debug.Log(request.downloadHandler.text.Length);
Debug.Log(request.downloadHandler.text[request.downloadHandler.text.Length - 1]);
Debug.Log(request.downloadHandler.text[request.downloadHandler.text.Length - 2]);
Debug.Log(request.downloadHandler.text[request.downloadHandler.text.Length - 3]);
string str=request.downloadHandler.text;
str = str.Substring(0, str.Length - 2);
Debug.Log(str[str.Length - 1]);
results = request.downloadHandler.data;
byte[] by = Convert.FromBase64String(request.downloadHandler.text);
Debug.Log(by.Length);
Mesh mesh = MeshSerializer.ReadMesh(by);
transform.GetComponent<MeshFilter>().mesh = mesh;
}
}
I am sending a byte[] as a unity web request, but when sending and receiving, a space is added at the end. so at the beginning, I calculated this space, but it seems to be different for each character length.
The base64 type seems to end with == but I don't know if this is correct
If you use Unity Web Request, can you tell how many null are added?
Or is there a standard for how many null are there?
---added
After sending to Unity Web Request, Post-> Download, the length of the string increased by 2, so at the beginning, only 2 deleted.
As I keep using this method, I don't know what to do because each object has a different null length.
Is it correct to find and compare only the last blank column?
ZWSP: zero width space
https://en.wikipedia.org/wiki/Zero-width_space
ZWSP is created if it is not received in an unusual way.
but don't know that it was data end space add ZWSP

Netsuite: how to add a custom link to the Nav Bar or Header

Is there any way to customize the Nav Bar or the Header to have a custom link?
The use-case is that I have a JIRA issue collector that is driven by javascript. I would like the user to provide feedback from the page they are having issues. However, any solution I can come up with so far takes the user away from the current page.
Example of what I have that takes the user away:
I currently have a Suitelet that is in one of the menus. That Suitelet invokes javascript but even then the user is taken away.
I have a workflow on the case record that calls some Javascript Javascript in one of the UI-based action's conditions is invoked. Similar to #1 but on the case record.
I'm thinking I'm going to need to create and public a chrome extension for my company's domain just to get a pervasive bit of javascript to run for all pages...seems like a sledgehammer.
I hope someone can prove me wrong, but as far as I am aware there is no way to natively inject Javascript or anything into the NetSuite header/navbar - they don't offer customisation to the header/navbar.
I've resorted to creating a Userscript that I load through the Violent Monkey extension for Chrome or Firefox.
Example Userscript Template
// ==UserScript==
// #name NetSuite Mods (Example)
// #namespace Violentmonkey Scripts
// #match *.netsuite.com/*
// #include *.netsuite.com/*
// #grant GM_addStyle
// #version 1.0
// #author Kane Shaw - https://stackoverflow.com/users/4561907/kane-shaw
// #description 6/11/2020, 6:25:20 PM
// ==/UserScript==
// Get access to some commonly used NLAPI functions without having to use "unsafeWindow.nlapi..." in our code
// You can add more of these if you need access to more of the functions contained on the NetSuite page
nlapiSetFieldText = unsafeWindow.nlapiSetFieldText;
nlapiSetFieldValue = unsafeWindow.nlapiSetFieldValue;
nlapiGetFieldText = unsafeWindow.nlapiGetFieldText;
nlapiGetFieldValue = unsafeWindow.nlapiGetFieldValue;
nlapiSearchRecord = unsafeWindow.nlapiSearchRecord;
nlobjSearchFilter = unsafeWindow.nlobjSearchFilter;
nlapiLookupField = unsafeWindow.nlapiLookupField;
nlapiLoadRecord = unsafeWindow.nlapiLoadRecord;
nlapiSubmitRecord = unsafeWindow.nlapiSubmitRecord;
GM_pageTransformations = {};
/**
* The entrypoint for our userscript
*/
function GM_main(jQuery) {
// We want to execute these on every NetSuite page
GM_pageTransformations.header();
GM_pageTransformations.browsertitle();
// Here we build a function name from the path (page being accessed on the NetSuite domain)
var path = location.pathname;
if(path.indexOf('.')>-1) path = path.substr(0,path.indexOf('.'));
path = toCamelCase(path,'/');
// Now we check if a page "GM_pageTransformations" function exists with a matching name
if(GM_pageTransformations[path]) {
console.log('Executing GM_pageTransformations for '+path);
GM_pageTransformations[path]();
} else {
console.log('No GM_pageTransformations for '+path);
}
}
/**
* Changes the header on all pages
*/
GM_pageTransformations['header'] = function() {
// For example, lets make the header background red
GM_addStyle('#ns_header, #ns_header * { background: red !important; }');
}
/**
* Provides useful browser/tab titles for each NetSuite page
*/
GM_pageTransformations['browsertitle'] = function() {
var title = jQuery('.uir-page-title-secondline').text().trim();
var title2 = jQuery('.uir-page-title-firstline').text().trim();
var title3 = jQuery('.ns-dashboard-detail-name').text().trim();
if(title != '') {
document.title = title+(title2 ? ': '+title2 : '')+(title3 ? ': '+title3 : '');
} else if(title2 != '') {
document.title = title2+(title3 ? ': '+title3 : '');
} else if(title3 != '') {
document.title = title3;
}
}
/**
* Changes app center card pages (dashboard pages)
*/
GM_pageTransformations['appCenterCard'] = function() {
// For example, lets make add a new heading text on all Dashboard pages
jQuery('#ns-dashboard-page').prepend('<h1>My New Dashboard Title</h1>');
}
/**
* Convert a given string into camelCase, or CamelCase
* #param {String} string - The input stirng
* #param {String} delimter - The delimiter that seperates the words in the input string (default " ")
* #param {Boolean} capitalizeFirstWord - Wheater or not to capitalize the first word (default false)
*/
function toCamelCase(string, delimiter, capitalizeFirstWord) {
if(!delimiter) delimiter = ' ';
var pieces = string.split(delimiter);
string = '';
for (var i=0; i<pieces.length; i++) {
if(pieces[i].length == 0) continue;
string += pieces[i].charAt(0).toUpperCase() + pieces[i].slice(1);
}
if(!capitalizeFirstWord) string= string.charAt(0).toLowerCase()+string.slice(1);
return string;
}
// ===============
// CREDIT FOR JQUERY INCLUSION CODE: Brock Adams # https://stackoverflow.com/a/12751531/4561907
/**
* Check if we already have a local copy of jQuery, or if we need to fetch it from a 3rd-party server
*/
if (typeof GM_info !== "undefined") {
console.log("Running with local copy of jQuery!");
GM_main(jQuery);
}
else {
console.log ("fetching jQuery from some 3rd-party server.");
add_jQuery(GM_main, "1.9.0");
}
/**
* Add the jQuery into our page for our userscript to use
*/
function add_jQuery(callbackFn, jqVersion) {
var jqVersion = jqVersion || "1.9.0";
var D = document;
var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
var scriptNode = D.createElement ('script');
scriptNode.src = 'https://ajax.googleapis.com/ajax/libs/jquery/'
+ jqVersion
+ '/jquery.min.js'
;
scriptNode.addEventListener ("load", function () {
var scriptNode = D.createElement ("script");
scriptNode.textContent =
'var gm_jQuery = jQuery.noConflict (true);\n'
+ '(' + callbackFn.toString () + ')(gm_jQuery);'
;
targ.appendChild (scriptNode);
}, false);
targ.appendChild (scriptNode);
}
You can copy and paste that code as-is into a new Userscript and it will do the following:
Make Browser tabs/windows have useful titles (shows order numbers, customer names, vendor names etc - not just "Sales Order")
Change the header background to red (as an example)
Add a new heading to the top of all "Dashboard" pages that says "My New Dashboard Title" (as an example)

info command and man pages

Fedora 19 {though I doubt that's relevant]
If I invoke the info command for a topic that doesn't actually have an info node, but does have a man page, info will apparently create a node named (*manpages*)<topic> from the man page -- on the fly.
I can neither find this feature documented anywhere, nor (obviously) a description of how it's done.
Can anyone point me to some documentation about this?
I have checked the GNU standalone info manual and GNU Info manual and found nothing but a small sign that this feature exists. In the description of option --all we can read this:
--all
-a
Find all files matching the given menu-item (a file or node name).
Three usage patterns are supported, as follows.
First, if --all is used together with --where, info prints the names
of all matching files found on standard output (including *manpages*
if relevant) and exits.
So I'm afraid the only documentation is the source code. In info.c you can find the following function. Look for the "Fall back to loading man page." comment.
/* Get the initial Info file, either by following menus from "(dir)Top",
or what the user specifed with values in filename. */
static char *
get_initial_file (char *filename, int *argc, char ***argv, char **error)
{
char *initial_file = 0; /* First file loaded by Info. */
REFERENCE *entry;
/* If there are any more arguments, the initial file is the
dir entry given by the first one. */
if (!filename && (*argv)[0])
{
/* If they say info -O info, we want to show them the invocation node
for standalone info; there's nothing useful in info.texi. */
if (goto_invocation_p && (*argv)[0]
&& mbscasecmp ((*argv)[0], "info") == 0)
(*argv)[0] = "info-stnd";
entry = lookup_dir_entry ((*argv)[0], 0);
if (entry)
{
initial_file = info_find_fullpath (entry->filename, 0);
if (initial_file)
{
(*argv)++; /* Advance past first remaining argument. */
(*argc)--;
/* Store full path, so that we find the already loaded file in
info_find_file, and show the full path if --where is used. */
entry->filename = initial_file;
add_pointer_to_array (info_copy_reference (entry),
ref_index, ref_list, ref_slots, 2);
return initial_file;
}
}
}
/* User used "--file". */
if (filename)
{
initial_file = info_find_fullpath (filename, 0);
if (!initial_file)
{
if (filesys_error_number)
*error = filesys_error_string (filename, filesys_error_number);
}
else
return initial_file;
}
/* File name lookup. */
if (!filename && (*argv)[0])
{
/* Try finding a file with this name, in case
it exists, but wasn't listed in dir. */
initial_file = info_find_fullpath ((*argv)[0], 0);
if (initial_file)
{
(*argv)++; /* Advance past first remaining argument. */
(*argc)--;
return initial_file;
}
else
asprintf (error, _("No menu item `%s' in node `%s'."),
(*argv)[0], "(dir)Top");
}
/* Fall back to loading man page. */
if (filename || (*argv)[0])
{
NODE *man_node;
debug (3, ("falling back to manpage node"));
man_node = get_manpage_node (filename ? filename : (*argv)[0]);
if (man_node)
{
add_pointer_to_array
(info_new_reference (MANPAGE_FILE_BUFFER_NAME,
filename ? filename : (*argv)[0]),
ref_index, ref_list, ref_slots, 2);
initial_file = MANPAGE_FILE_BUFFER_NAME;
return initial_file;
}
}
/* Inexact dir lookup. */
if (!filename && (*argv)[0])
{
entry = lookup_dir_entry ((*argv)[0], 1);
if (entry)
{
(*argv)++; /* Advance past first remaining argument. */
(*argc)--;
/* Clear error message. */
free (*error);
*error = 0;
initial_file = info_find_fullpath (entry->filename, 0);
/* Store full path, so that we find the already loaded file in
info_find_file, and show the full path if --where is used. */
entry->filename = initial_file;
add_pointer_to_array (info_copy_reference (entry),
ref_index, ref_list, ref_slots, 2);
return initial_file;
}
}
/* Otherwise, we want the dir node. The only node to be displayed
or output will be "Top". */
return 0;
}
man.h contains the definition of MANPAGE_FILE_BUFFER_NAME:
#define MANPAGE_FILE_BUFFER_NAME "*manpages*"
info is a command like man, you can test the command : info make but info Read documentation in Info format.

Adding Multiple Views to a VC++ 2010 SDI using MFC

I'm trying to add multiple (actually 3 ) views to an SDI application and give the user choose witch View will be load according to his choice :
IMG
I have followed this tutorial in the official MS documentation .
So, have created three classes : CAdminView CAssistantView CBiblioView and an authentication class associated to a dialog frame .
My Questions are :
1) how to edit this three view classes (Graphically) ?
2) at first time I want to show just the authentication dialog window , how to do that ?
* I tried to change m_pMainWnd->ShowWindow(SW_SHOW);
by m_pMainWnd->ShowWindow(SW_HIDE); but no expected result
3) I expect to load the view according to parameter, this is what I added to the InitInstance funnction :
CView* pActiveView = ((CFrameWnd*) m_pMainWnd)->GetActiveView();
m_BiblioView = (CView*) new CBiblioView;
m_AdminView = (CView*) new CAdminView;
m_AssistantView = (CView*) new CAssistantView;
CDocument* pCurrentDoc = ((CFrameWnd*)m_pMainWnd)->GetActiveDocument();
// Initialize a CCreateContext to point to the active document.
// With this context, the new view is added to the document
// when the view is created in CView::OnCreate().
CCreateContext newContext;
newContext.m_pNewViewClass = NULL;
newContext.m_pNewDocTemplate = NULL;
newContext.m_pLastView = NULL;
newContext.m_pCurrentFrame = NULL;
newContext.m_pCurrentDoc = pCurrentDoc;
// The ID of the initial active view is AFX_IDW_PANE_FIRST.
// Incrementing this value by one for additional views works
// in the standard document/view case but the technique cannot
// be extended for the CSplitterWnd case.
UINT viewID = AFX_IDW_PANE_FIRST + 1;
CRect rect(0, 0, 0, 0); // Gets resized later.
// Create the new view. In this example, the view persists for
// the life of the application. The application automatically
// deletes the view when the application is closed.
m_AdminView->Create(NULL, "Fenetre Administrarteur", WS_CHILD, rect, m_pMainWnd, viewID, &newContext);
m_AssistantView->Create(NULL, "Fenetre Assistant", WS_CHILD, rect, m_pMainWnd, viewID, &newContext);
m_BiblioView->Create(NULL, "Fenetre Bibliothecaire ", WS_CHILD, rect, m_pMainWnd, viewID, &newContext);
// When a document template creates a view, the WM_INITIALUPDATE
// message is sent automatically. However, this code must
// explicitly send the message, as follows.
m_AdminView->SendMessage(WM_INITIALUPDATE, 0, 0);
m_AssistantView->SendMessage(WM_INITIALUPDATE, 0, 0);
m_BiblioView->SendMessage(WM_INITIALUPDATE, 0, 0);
and this is my switch function :
CView* CMiniProjetApp::SwitchView(int Code ) //1 : Admi / 2 : Biblio / 3 : Assistant
{
CView* pActiveView =((CFrameWnd*) m_pMainWnd)->GetActiveView();
CView* pNewView= NULL;
switch(Code){
case 1 : pNewView= m_AdminView; break;
case 2 : pNewView= m_BiblioView; break;
case 3 : pNewView= m_AssistantView; break;
}
// Exchange view window IDs so RecalcLayout() works.
#ifndef _WIN32
UINT temp = ::GetWindowWord(pActiveView->m_hWnd, GWW_ID);
::SetWindowWord(pActiveView->m_hWnd, GWW_ID, ::GetWindowWord(pNewView->m_hWnd, GWW_ID));
::SetWindowWord(pNewView->m_hWnd, GWW_ID, temp);
#else
UINT temp = ::GetWindowLong(pActiveView->m_hWnd, GWL_ID);
::SetWindowLong(pActiveView->m_hWnd, GWL_ID, ::GetWindowLong(pNewView->m_hWnd, GWL_ID));
::SetWindowLong(pNewView->m_hWnd, GWL_ID, temp);
#endif
pActiveView->ShowWindow(SW_HIDE);
pNewView->ShowWindow(SW_SHOW);
((CFrameWnd*) m_pMainWnd)->SetActiveView(pNewView);
((CFrameWnd*) m_pMainWnd)->RecalcLayout();
pNewView->Invalidate();
return pActiveView;
}
Any errors notices ??!!
*please help me !
Thanks .
Showing and Hiding the window is the correct way. But you Need to set the view as active too.
You find the required working codein this MSDN Sample VSSWAP32.
The required code to switch and hide the other views is shown in the article.

Error printing specific page range in microsoft word in c++ app

I have a strange problem where I'm trying to print a specific page range in microsoft word from a console app and I'm seeing strange results and I'm assuming it's something I did incorrectly when specifying a page range.
It appears that after I print a page range and go to get the total number of pages in the word document this number varies after I print specific ranges. Another weird thing is that this works in debug mode but not in release mode.
ex.
Word document consists of 2 pages
Print page 1-1.
Get number of pages returns 2
Print page 2-2
Get number of pages returns 1
Code is below for printing a range of pages:
int CWordComm::PrintAndCloseActiveDocument(int iNumOfCopies, short nTray, int pageNumber)
{
// Convenient values declared as ColeVariants.
COleVariant covTrue((short)TRUE), covFalse((short)FALSE), covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR);
_Document oActiveDoc = m_oWord.GetActiveDocument();
if(!this->m_szOutputFilename.IsEmpty())
{
//If we are outputting the document to a file then we need to print a single page at a time
//This is because of a limitation on the ikonprinter/requisition printer side that can only handle
//one page at a time
// Print out to file
CString szCurrentPage, szPrintRange;
szCurrentPage.Format("%d", pageNumber);
szPrintRange.Format("%d", PRINT_FROM_TO); //PRINT_FROM_TO is #define PRINT_FROM_TO 3
COleVariant printRange(szPrintRange, VT_BSTR);
COleVariant currentPage(szCurrentPage, VT_BSTR);
sprintf(m_szLogMessage, "Printing page %d of requisition", pageNumber);
LogMessage(m_szLogMessage);
oActiveDoc.PrintOut(covFalse, // Background.
covOptional, // Append.
printRange, // Range.
COleVariant(szFileName,VT_BSTR), // OutputFileName.
currentPage, // From.
currentPage, // To.
covOptional, // Item.
COleVariant((long)1), // Copies.
covOptional, // Pages.
covOptional, // PageType.
covTrue, // PrintToFile.
covOptional, // Collate.
covOptional, // ActivePrinterMacGX.
covOptional // ManualDuplexPrint.
);
}
else
{
// Print out to file
oActiveDoc.PrintOut(covFalse, // Background.
covOptional, // Append.
covOptional, // Range.
COleVariant(szFileName,VT_BSTR), // OutputFileName.
covOptional, // From.
covOptional, // To.
covOptional, // Item.
COleVariant((long)1), // Copies.
covOptional, // Pages.
covOptional, // PageType.
covTrue, // PrintToFile.
covOptional, // Collate.
covOptional, // ActivePrinterMacGX.
covOptional // ManualDuplexPrint.
);
}
//Get the number of pages in the word document
iNumPages=GetActiveDocPageCount();
//omitted code
}
Get pages method
int CWordComm::GetActiveDocPageCount()
{
try
{
_Document oActiveDoc;
//Get the Active Document
oActiveDoc = m_oWord.GetActiveDocument();
//Get the BuiltinDocumentProperties collection for the
//document
LPDISPATCH lpdispProps;
lpdispProps = oActiveDoc.GetBuiltInDocumentProperties();
//Get the requested Item from the BuiltinDocumentProperties
//collection
//NOTE: The DISPID of the "Item" property of a
// DocumentProperties object is 0x0
VARIANT vResult;
DISPPARAMS dpItem;
VARIANT vArgs[1];
vArgs[0].vt = VT_BSTR;
//property name for the number of pages in the active document
_bstr_t btVal("Number of pages");
vArgs[0].bstrVal = btVal;
dpItem.cArgs=1;
dpItem.cNamedArgs=0;
dpItem.rgvarg = vArgs;
HRESULT hr = lpdispProps->Invoke(0x0, IID_NULL,
LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET,
&dpItem, &vResult, NULL, NULL);
//Get the Value property of the BuiltinDocumentProperty
//NOTE: The DISPID of the "Value" property of a
// DocumentProperty object is 0x0
DISPPARAMS dpNoArgs = {NULL, NULL, 0, 0};
LPDISPATCH lpdispProp;
lpdispProp = vResult.pdispVal;
hr = lpdispProp->Invoke(0x0, IID_NULL, LOCALE_USER_DEFAULT,
DISPATCH_PROPERTYGET, &dpNoArgs, &vResult,
NULL, NULL);
CString sPropValue = "";
switch (vResult.vt)
{
case VT_BSTR:
sPropValue = vResult.bstrVal;
break;
case VT_I4:
sPropValue.Format("%d",vResult.lVal);
break;
case VT_DATE:
{
COleDateTime dt (vResult);
sPropValue = dt.Format(0, LANG_USER_DEFAULT);
break;
}
default:
// sPropValue = "<Information for the property you selected is not available>";
sPropValue = "-4";
break;
}
//Release the no longer needed IDispatch pointers
lpdispProp->Release();
lpdispProps->Release();
return atoi(sPropValue);
}
catch(...)
{
return FUNC_ERROR;
}
}
Question
Is there something obvious I am doing wrong here? Should I be interfacing with word in a different way if I wish to print a range of pages?
If it works fine in the debug mode, I suggest you write the values ( eg No of pages, page being printed etc ) to a text file. Compare the debug log output , with the release log output. Here's a link which might help you. The link is not in C++, but might guide you in the right way.
It turns out this is some sort of timing issue. I put a 500ms sleep after the printing of the document and before the calculation of the number of pages and I'm now consistently getting the correct results.
When I was debugging it was essentially doing this same thing.

Resources