windows-nt/Source/XPSP1/NT/printscan/fax/exchange/xport/faxdoc.cpp
2020-09-26 16:20:57 +08:00

1825 lines
48 KiB
C++

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
faxdoc.cpp
Abstract:
This module contains all code necessary to print an
exchange message as a fax document.
Author:
Wesley Witt (wesw) 13-Aug-1996
--*/
#include "faxxp.h"
#pragma hdrstop
#ifdef WIN95
#include "mfx.h"
#include "fwprov.h"
#endif
PVOID
CXPLogon::MyGetPrinter(
LPSTR PrinterName,
DWORD Level
)
/*++
Routine Description:
Gets the printer data for a specifi printer
Arguments:
PrinterName - Name of the desired printer
Return Value:
Pointer to a printer info structure or NULL for failure.
--*/
{
PVOID PrinterInfo = NULL;
HANDLE hPrinter;
DWORD Bytes;
PRINTER_DEFAULTS PrinterDefaults;
PrinterDefaults.pDatatype = NULL;
PrinterDefaults.pDevMode = NULL;
PrinterDefaults.DesiredAccess = PRINTER_ACCESS_USE;
if (!OpenPrinter( PrinterName, &hPrinter, &PrinterDefaults )) {
goto exit;
}
if ((!GetPrinter( hPrinter, Level, NULL, 0, &Bytes )) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
goto exit;
}
PrinterInfo = (LPPRINTER_INFO_2) MemAlloc( Bytes );
if (!PrinterInfo) {
goto exit;
}
if (!GetPrinter( hPrinter, Level, (LPBYTE) PrinterInfo, Bytes, &Bytes )) {
goto exit;
}
exit:
ClosePrinter( hPrinter );
return PrinterInfo;
}
VOID
CXPLogon::PrintRichText(
HWND hWndRichEdit,
HDC hDC,
PFAXXP_CONFIG FaxConfig
)
/*++
Routine Description:
Prints the rich text contained in a rich text
window into a DC.
Arguments:
hWndRichEdit - Window handle for the rich text window
hDC - Printer device context
Return Value:
None.
--*/
{
FORMATRANGE fr;
LONG lTextOut;
LONG lTextAmt;
RECT rcTmp;
LPTSTR szText;
LPTSTR szChar;
fr.hdc = hDC;
fr.hdcTarget = hDC;
fr.chrg.cpMin = 0;
fr.chrg.cpMax = -1;
//
// Set page rect to phys page size in twips
//
fr.rcPage.top = 0;
fr.rcPage.left = 0;
fr.rcPage.right = MulDiv(GetDeviceCaps(hDC, PHYSICALWIDTH),
1440,
GetDeviceCaps(hDC, LOGPIXELSX));
fr.rcPage.bottom = MulDiv(GetDeviceCaps(hDC, PHYSICALHEIGHT),
1440,
GetDeviceCaps(hDC, LOGPIXELSY));
//
// Set up 3/4" horizontal and 1" vertical margins, but leave a minimum of 1"
// printable space in each direction. Otherwise, use full page.
//
fr.rc = fr.rcPage; // start with full page
if (fr.rcPage.right > 2*3*1440/4 + 1440) {
fr.rc.right -= (fr.rc.left = 3*1440/4);
}
if (fr.rcPage.bottom > 3*1440) {
fr.rc.bottom -= (fr.rc.top = 1440);
}
//
// save the formatting rectangle
//
rcTmp = fr.rc;
SetMapMode( hDC, MM_TEXT );
lTextOut = 0;
lTextAmt = (LONG)SendMessage( hWndRichEdit, WM_GETTEXTLENGTH, 0, 0 );
szText = (LPTSTR) MemAlloc((lTextAmt + 1) * sizeof(TCHAR));
if (szText) {
SendMessage(hWndRichEdit, WM_GETTEXT, (WPARAM) lTextAmt, (LPARAM) szText);
szChar = szText;
while (lTextOut < lTextAmt) {
if ((*szChar != ' ') && (*szChar != '\t') && (*szChar != '\r') && (*szChar != '\n')) {
break;
}
lTextOut++;
szChar = CharNext(szChar);
}
MemFree(szText);
if (!strlen(szChar)) {
lTextAmt = 0;
}
}
while (lTextOut < lTextAmt) {
// Reset margins
fr.rc = rcTmp;
StartPage(hDC);
lTextOut = (LONG) SendMessage(hWndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
EndPage(hDC);
fr.chrg.cpMin = lTextOut;
fr.chrg.cpMax = -1;
}
//
// flush the cache
//
SendMessage(hWndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
}
extern "C"
DWORD CALLBACK
EditStreamRead(
DWORD_PTR dwCookie,
LPBYTE pbBuff,
LONG cb,
LONG *pcb
)
/*++
Routine Description:
Wrapper function for the IStream read method.
This function is used to read rich text from
an exchange stream.
Arguments:
dwCookie - This pointer for the IStream object
pbBuff - Pointer to the data buffer
cb - Size of the data buffer
pcb - Returned byte count
Return Value:
Return code from IStream::Read
--*/
{
return ((LPSTREAM)dwCookie)->Read( pbBuff, cb, (ULONG*) pcb );
}
BOOL
CXPLogon::PrintText(
HDC hDC,
LPSTREAM lpstmT,
PFAXXP_CONFIG FaxConfig
)
/*++
Routine Description:
Prints a stream of plain text into the printer DC provided.
Note: this code was stolen from notepad.
Arguments:
hDC - Printer DC
lpstmT - Stream pointer for rich text.
FaxConfig - Fax configuration data
Return Value:
TRUE for success.
FALSE for failure.
--*/
{
LPSTR BodyText = NULL;
LPSTR lpLine;
LPSTR pLineEOL;
LPSTR pNextLine;
HRESULT hResult;
HFONT hFont = NULL;
HFONT hPrevFont = NULL;
TEXTMETRIC tm;
BOOL rVal = TRUE;
INT nLinesPerPage;
INT dyTop; // width of top border (pixels)
INT dyBottom; // width of bottom border
INT dxLeft; // width of left border
INT dxRight; // width of right border
INT yPrintChar; // height of a character
INT tabSize; // Size of a tab for print device in device units
INT yCurpos = 0;
INT xCurpos = 0;
INT nPixelsLeft = 0;
INT guess = 0;
SIZE Size; // to see if text will fit in space left
INT nPrintedLines = 0;
BOOL fPageStarted = FALSE;
INT iPageNum = 0;
INT xPrintRes; // printer resolution in x direction
INT yPrintRes; // printer resolution in y direction
INT yPixInch; // pixels/inch
INT xPixInch; // pixels/inch
INT xPixUnit; // pixels/local measurement unit
INT yPixUnit; // pixels/local measurement unit
BOOL fEnglish;
INT Chars;
STATSTG Stats;
INT PrevBkMode = 0;
hResult = lpstmT->Stat( &Stats, 0 );
if (hResult) {
rVal = FALSE;
goto exit;
}
Chars = (INT) Stats.cbSize.QuadPart;
BodyText = (LPSTR) MemAlloc( Chars + 4 );
if (!BodyText) {
rVal = FALSE;
goto exit;
}
hResult = lpstmT->Read( (LPVOID) BodyText, Chars, (LPDWORD) &Chars );
if (hResult) {
rVal = FALSE;
goto exit;
}
lpLine = BodyText;
fEnglish = GetProfileInt( "intl", "iMeasure", 1 );
xPrintRes = GetDeviceCaps( hDC, HORZRES );
yPrintRes = GetDeviceCaps( hDC, VERTRES );
xPixInch = GetDeviceCaps( hDC, LOGPIXELSX );
yPixInch = GetDeviceCaps( hDC, LOGPIXELSY );
//
// compute x and y pixels per local measurement unit
//
if (fEnglish) {
xPixUnit= xPixInch;
yPixUnit= yPixInch;
} else {
xPixUnit= CMToInches( xPixInch );
yPixUnit= CMToInches( yPixInch );
}
SetMapMode( hDC, MM_TEXT );
hFont = CreateFontIndirect( &FaxConfig->FontStruct );
hPrevFont = (HFONT) SelectObject( hDC, hFont );
SetBkMode( hDC, TRANSPARENT );
if (!GetTextMetrics( hDC, &tm )) {
rVal = FALSE;
goto exit;
}
yPrintChar = tm.tmHeight + tm.tmExternalLeading;
tabSize = tm.tmAveCharWidth * 8;
//
// compute margins in pixels
//
dxLeft = LEFT_MARGIN * xPixUnit;
dxRight = RIGHT_MARGIN * xPixUnit;
dyTop = TOP_MARGIN * yPixUnit;
dyBottom = BOTTOM_MARGIN * yPixUnit;
//
// Number of lines on a page with margins
//
nLinesPerPage = ((yPrintRes - dyTop - dyBottom) / yPrintChar);
while (*lpLine) {
if (*lpLine == '\r') {
lpLine += 2;
yCurpos += yPrintChar;
nPrintedLines++;
xCurpos= 0;
continue;
}
pLineEOL = lpLine;
while (*pLineEOL && *pLineEOL != '\r') pLineEOL++;
do {
if ((nPrintedLines == 0) && (!fPageStarted)) {
StartPage( hDC );
fPageStarted = TRUE;
yCurpos = 0;
xCurpos = 0;
}
if (*lpLine == '\t') {
//
// round up to the next tab stop
// if the current position is on the tabstop, goto next one
//
xCurpos = ((xCurpos + tabSize) / tabSize ) * tabSize;
lpLine++;
} else {
//
// find end of line or tab
//
pNextLine = lpLine;
while ((pNextLine != pLineEOL) && *pNextLine != '\t') pNextLine++;
//
// find out how many characters will fit on line
//
Chars = (INT)(pNextLine - lpLine);
nPixelsLeft = xPrintRes - dxRight - dxLeft - xCurpos;
GetTextExtentExPoint( hDC, lpLine, Chars, nPixelsLeft, &guess, NULL, &Size );
if (guess) {
//
// at least one character fits - print
//
TextOut( hDC, dxLeft+xCurpos, yCurpos+dyTop, lpLine, guess );
xCurpos += Size.cx; // account for printing
lpLine += guess; // printed characters
} else {
//
// no characters fit what's left
// no characters will fit in space left
// if none ever will, just print one
// character to keep progressing through
// input file.
//
if (xCurpos == 0) {
if( lpLine != pNextLine ) {
//
// print something if not null line
// could use exttextout here to clip
//
TextOut( hDC, dxLeft+xCurpos, yCurpos+dyTop, lpLine, 1 );
lpLine++;
}
} else {
//
// perhaps the next line will get it
//
xCurpos = xPrintRes; // force to next line
}
}
//
// move printhead in y-direction
//
if ((xCurpos >= (xPrintRes - dxRight - dxLeft) ) || (lpLine == pLineEOL)) {
yCurpos += yPrintChar;
nPrintedLines++;
xCurpos = 0;
}
if (nPrintedLines >= nLinesPerPage) {
EndPage( hDC );
fPageStarted = FALSE;
nPrintedLines = 0;
xCurpos = 0;
yCurpos = 0;
iPageNum++;
}
}
} while( lpLine != pLineEOL );
if (*lpLine == '\r') {
lpLine += 1;
}
if (*lpLine == '\n') {
lpLine += 1;
}
}
if (fPageStarted) {
EndPage( hDC );
}
exit:
MemFree( BodyText );
if (hPrevFont) {
SelectObject( hDC, hPrevFont );
DeleteObject( hFont );
}
if (PrevBkMode) {
SetBkMode( hDC, PrevBkMode );
}
return rVal;
}
DWORD
CXPLogon::PrintAttachment(
LPSTR FaxPrinterName,
LPSTR DocName
)
/*++
Routine Description:
Prints a document that is attached to a message
Arguments:
FaxPrinterName - name of the printer to print the attachment on
DocName - name of the attachment document
Return Value:
Print job id or zero for failure.
--*/
{
SHELLEXECUTEINFO sei;
CHAR Args[MAX_PATH];
CHAR TempDir[MAX_PATH];
HANDLE hMap = NULL;
HANDLE hEvent = NULL;
HANDLE hMutex = NULL;
HANDLE hMutexAttach = NULL;
LPDWORD pJobId = NULL;
DWORD JobId = 0;
#ifndef WIN95
SECURITY_ATTRIBUTES memsa,mutsa,synsa,eventsa;
SECURITY_DESCRIPTOR memsd,mutsd,synsd,eventsd;
#endif
//
// serialize access to this function.
// this is necessary because we can't have more than one
// app accessing our shared memory region and mutex
//
hMutexAttach = OpenMutex(MUTEX_ALL_ACCESS,FALSE,FAXRENDER_MUTEX);
if (!hMutexAttach) {
//
// we need to open this mutex with a NULL dacl so that everyone can access this
//
#ifndef WIN95
synsa.nLength = sizeof(SECURITY_ATTRIBUTES);
synsa.bInheritHandle = TRUE;
synsa.lpSecurityDescriptor = &synsd;
if(!InitializeSecurityDescriptor(&synsd, SECURITY_DESCRIPTOR_REVISION)) {
goto exit;
}
if(!SetSecurityDescriptorDacl(&synsd, TRUE, (PACL)NULL, FALSE)) {
goto exit;
}
#endif
hMutexAttach = CreateMutex(
#ifndef WIN95
&synsa,
#else
NULL,
#endif
TRUE,
FAXRENDER_MUTEX
);
if (!hMutexAttach) {
goto exit;
}
} else {
if (WaitForSingleObject( hMutexAttach, 1000* 60 * 5) != WAIT_OBJECT_0) {
//
// something went wrong
//
CloseHandle( hMutexAttach );
goto exit;
}
}
//
// since mapispooler might be running under a different security context,
// we must setup a NULL security descriptor
//
#ifndef WIN95
memsa.nLength = sizeof(SECURITY_ATTRIBUTES);
memsa.bInheritHandle = TRUE;
memsa.lpSecurityDescriptor = &memsd;
if(!InitializeSecurityDescriptor(&memsd, SECURITY_DESCRIPTOR_REVISION)) {
goto exit;
}
if(!SetSecurityDescriptorDacl(&memsd, TRUE, (PACL)NULL, FALSE)) {
goto exit;
}
mutsa.nLength = sizeof(SECURITY_ATTRIBUTES);
mutsa.bInheritHandle = TRUE;
mutsa.lpSecurityDescriptor = &mutsd;
if(!InitializeSecurityDescriptor(&mutsd, SECURITY_DESCRIPTOR_REVISION)) {
goto exit;
}
if(!SetSecurityDescriptorDacl(&mutsd, TRUE, (PACL)NULL, FALSE)) {
goto exit;
}
eventsa.nLength = sizeof(SECURITY_ATTRIBUTES);
eventsa.bInheritHandle = TRUE;
eventsa.lpSecurityDescriptor = &eventsd;
if(!InitializeSecurityDescriptor(&eventsd, SECURITY_DESCRIPTOR_REVISION)) {
goto exit;
}
if(!SetSecurityDescriptorDacl(&eventsd, TRUE, (PACL)NULL, FALSE)) {
goto exit;
}
#endif
//
// create the shared memory region for the print jobid
// the jobid is filled in by the fax printer driver
//
hMap = CreateFileMapping(
INVALID_HANDLE_VALUE,
#ifndef WIN95
&memsa,
#else
NULL,
#endif
PAGE_READWRITE | SEC_COMMIT,
0,
4096,
FAXXP_MEM_NAME
);
if (!hMap) {
goto exit;
}
pJobId = (LPDWORD) MapViewOfFile(
hMap,
FILE_MAP_WRITE,
0,
0,
0
);
if (!pJobId) {
goto exit;
}
*pJobId = (DWORD) 0;
//
// get the temp path name and use it for the
// working dir of the launched app
//
GetTempPath( sizeof(TempDir), TempDir );
//
// set the arguments to the app.
// these arguments are either passed on
// the command line with the /pt switch or
// use as variables for substitution in the
// ddeexec value in the registry.
//
// the values are as follows:
// %1 = file name
// %2 = printer name
// %3 = driver name
// %4 = port name
//
// the first argument does not need to be
// supplied in the args array because it is implied,
// shellexecuteex gets it from the lpFile field.
// arguments 3 & 4 are left blank because they
// are win31 artifacts that are not necessary
// any more. each argument must be enclosed
// in double quotes.
//
wsprintf( Args, "\"%s\" \"\" \"\"", FaxPrinterName );
//
// fill in the SHELLEXECUTEINFO structure
//
sei.cbSize = sizeof(sei);
sei.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_DDEWAIT;
sei.hwnd = NULL;
sei.lpVerb = "printto";
sei.lpFile = DocName;
sei.lpParameters = Args;
sei.lpDirectory = TempDir;
sei.nShow = SW_SHOWMINNOACTIVE;
sei.hInstApp = NULL;
sei.lpIDList = NULL;
sei.lpClass = NULL;
sei.hkeyClass = NULL;
sei.dwHotKey = 0;
sei.hIcon = NULL;
sei.hProcess = NULL;
//
// create the named mutex for the print driver.
// this is initially unclaimed, and is claimed by the first instance
// of the print driver invoked after this. We do this last in order to
// avoid a situation where we catch the incorrect instance of the print driver
// printing
//
hMutex = CreateMutex(
#ifndef WIN95
&mutsa,
#else
NULL,
#endif
FALSE,
FAXXP_MUTEX_NAME
);
if (!hMutex) {
goto exit;
}
//
// create the named event for the print driver.
// this event is signaled when the print driver is finished rendering the document
//
hEvent = CreateEvent(
#ifndef WIN95
&eventsa,
#else
NULL,
#endif
FALSE,
FALSE,
FAXXP_EVENT_NAME
);
if (!hEvent) {
goto exit;
}
//
// launch the app
//
if (!ShellExecuteEx( &sei )) {
goto exit;
}
//
// wait for the print driver to finish rendering the document
//
if (WaitForSingleObject( hEvent, 1000 * 60 * 5 ) != WAIT_OBJECT_0) {
//
// something went wrong...
//
goto exit;
}
//
// wait for the print driver to exit so we can get the document
//
if (WaitForSingleObject( hMutex, 1000 * 60 * 5) != WAIT_OBJECT_0) {
//
// something went wrong
//
goto exit;
}
ReleaseMutex(hMutex);
//
// save the print jobid
//
JobId = *pJobId;
exit:
//
// clean up and leave...
//
if (sei.hProcess) CloseHandle( sei.hProcess );
if (hEvent) CloseHandle( hEvent );
if (hMutex) CloseHandle( hMutex );
if (pJobId) UnmapViewOfFile( pJobId );
if (hMap) CloseHandle( hMap );
if (hMutexAttach) {
ReleaseMutex( hMutexAttach );
CloseHandle( hMutexAttach );
}
return JobId;
}
DWORD
CXPLogon::SendFaxDocument(
LPMESSAGE pMsgObj,
LPSTREAM lpstmT,
BOOL UseRichText,
LPSPropValue pMsgProps,
LPSPropValue pRecipProps
)
/*++
Routine Description:
Prints an exchange message to the fax printer.
Arguments:
lpstmT - Stream pointer for rich text.
pMsgProps - Message properties.
pRecipProps - Recipient properties.
Return Value:
Zero for success, otherwise error code.
--*/
{
PPRINTER_INFO_2 PrinterInfo = NULL;
PRINTER_DEFAULTS PrinterDefaults;
HANDLE hPrinter = NULL;
HDC hDC = NULL;
INT JobId;
DWORD Bytes;
DWORD ec;
DWORD rVal = 0;
HRESULT hResult;
LPSTREAM lpstm = NULL;
EDITSTREAM es = {0};
HWND hWndRichEdit = NULL;
LPPROFSECT pProfileObj = NULL;
ULONG PropCount = 0;
ULONG PropMsgCount = 0;
LPSPropValue pPropsAttachTable = NULL;
LPSPropValue pPropsAttach = NULL;
LPSPropValue pProps = NULL;
LPSPropValue pPropsMsg = NULL;
FAXXP_CONFIG FaxConfig = {0};
DWORD JobIdAttachment = 0;
INT i = 0;
LPMAPITABLE AttachmentTable = NULL;
LPSRowSet pAttachmentRows = NULL;
LPATTACH lpAttach = NULL;
LPSTREAM lpstmA = NULL;
LPSTR AttachFileName = NULL;
CHAR TempPath[MAX_PATH];
CHAR TempFile[MAX_PATH];
CHAR DocFile[MAX_PATH];
HANDLE hFile = INVALID_HANDLE_VALUE;
LPSTR DocType = NULL;
DWORD LastJobId = 0;
PJOB_INFO_1 JobInfo1 = NULL;
JOB_INFO_3 JobInfo3;
LPSTR p;
DWORD Pages = 0;
BOOL DeleteAttachFile;
LPSTR FileName = NULL;
BOOL AllAttachmentsGood = TRUE;
MAPINAMEID NameIds[NUM_FAX_MSG_PROPS];
MAPINAMEID *pNameIds[NUM_FAX_MSG_PROPS] = {&NameIds[0], &NameIds[1], &NameIds[2], &NameIds[3]};
LPSPropTagArray MsgPropTags = NULL;
LARGE_INTEGER BigZero = {0};
CHAR FullName[128];
HKEY hKey;
DWORD RegSize;
DWORD RegType;
DWORD CountPrinters;
#ifdef WIN95
LCID LocaleId;
LPSTR lpszBuffer;
DWORD BytesWrite;
HINSTANCE hinstFwProv;
PFWSPJ pfnStartPrintJob;
#else
USER_INFO UserInfo = {0};
FAX_PRINT_INFOA FaxPrintInfo = {0};
FAX_CONTEXT_INFO ContextInfo = {0};
FAX_COVERPAGE_INFOA FaxCpInfo = {0};
// char szCoverPageName[MAX_PATH];
#endif // WIN95
//
// get the fax config properties
//
hResult = m_pSupObj->OpenProfileSection(
&FaxGuid,
MAPI_MODIFY,
&pProfileObj
);
if (HR_FAILED (hResult)) {
rVal = IDS_CANT_ACCESS_PROFILE;
goto exit;
}
hResult = pProfileObj->GetProps(
(LPSPropTagArray) &sptFaxProps,
0,
&PropCount,
&pProps
);
if (FAILED(hResult)) {
rVal = IDS_CANT_ACCESS_PROFILE;
goto exit;
}
FaxConfig.PrinterName = StringDup( pProps[PROP_FAX_PRINTER_NAME].Value.LPSZ );
FaxConfig.CoverPageName = StringDup( pProps[PROP_COVERPAGE_NAME].Value.LPSZ );
FaxConfig.UseCoverPage = pProps[PROP_USE_COVERPAGE].Value.ul;
FaxConfig.ServerCoverPage = pProps[PROP_SERVER_COVERPAGE].Value.ul;
CopyMemory( &FaxConfig.FontStruct, pProps[PROP_FONT].Value.bin.lpb, pProps[PROP_FONT].Value.bin.cb );
//
// now get the message properties
//
NameIds[MSGPI_FAX_PRINTER_NAME].lpguid = (LPGUID)&PS_PUBLIC_STRINGS;
NameIds[MSGPI_FAX_PRINTER_NAME].ulKind = MNID_STRING;
NameIds[MSGPI_FAX_PRINTER_NAME].Kind.lpwstrName = MSGPS_FAX_PRINTER_NAME;
NameIds[MSGPI_FAX_COVERPAGE_NAME].lpguid = (LPGUID)&PS_PUBLIC_STRINGS;
NameIds[MSGPI_FAX_COVERPAGE_NAME].ulKind = MNID_STRING;
NameIds[MSGPI_FAX_COVERPAGE_NAME].Kind.lpwstrName = MSGPS_FAX_COVERPAGE_NAME;
NameIds[MSGPI_FAX_USE_COVERPAGE].lpguid = (LPGUID)&PS_PUBLIC_STRINGS;
NameIds[MSGPI_FAX_USE_COVERPAGE].ulKind = MNID_STRING;
NameIds[MSGPI_FAX_USE_COVERPAGE].Kind.lpwstrName = MSGPS_FAX_USE_COVERPAGE;
NameIds[MSGPI_FAX_SERVER_COVERPAGE].lpguid = (LPGUID)&PS_PUBLIC_STRINGS;
NameIds[MSGPI_FAX_SERVER_COVERPAGE].ulKind = MNID_STRING;
NameIds[MSGPI_FAX_SERVER_COVERPAGE].Kind.lpwstrName = MSGPS_FAX_SERVER_COVERPAGE;
hResult = pMsgObj->GetIDsFromNames( NUM_FAX_MSG_PROPS, pNameIds, MAPI_CREATE, &MsgPropTags );
if (FAILED(hResult)) {
rVal = IDS_CANT_ACCESS_PROFILE;
goto exit;
}
MsgPropTags->aulPropTag[MSGPI_FAX_PRINTER_NAME] = PROP_TAG( PT_TSTRING, PROP_ID(MsgPropTags->aulPropTag[MSGPI_FAX_PRINTER_NAME]) );
MsgPropTags->aulPropTag[MSGPI_FAX_COVERPAGE_NAME] = PROP_TAG( PT_TSTRING, PROP_ID(MsgPropTags->aulPropTag[MSGPI_FAX_COVERPAGE_NAME]) );
MsgPropTags->aulPropTag[MSGPI_FAX_USE_COVERPAGE] = PROP_TAG( PT_LONG, PROP_ID(MsgPropTags->aulPropTag[MSGPI_FAX_USE_COVERPAGE]) );
MsgPropTags->aulPropTag[MSGPI_FAX_SERVER_COVERPAGE] = PROP_TAG( PT_LONG, PROP_ID(MsgPropTags->aulPropTag[MSGPI_FAX_SERVER_COVERPAGE]) );
hResult = pMsgObj->GetProps( MsgPropTags, 0, &PropMsgCount, &pPropsMsg );
if (FAILED(hResult)) {
rVal = IDS_CANT_ACCESS_PROFILE;
goto exit;
}
if (PROP_TYPE(pPropsMsg[MSGPI_FAX_PRINTER_NAME].ulPropTag) != PT_ERROR) {
MemFree( FaxConfig.PrinterName );
FaxConfig.PrinterName = StringDup( pPropsMsg[MSGPI_FAX_PRINTER_NAME].Value.LPSZ );
}
if (PROP_TYPE(pPropsMsg[MSGPI_FAX_COVERPAGE_NAME].ulPropTag) != PT_ERROR) {
MemFree( FaxConfig.CoverPageName );
FaxConfig.CoverPageName = StringDup( pPropsMsg[MSGPI_FAX_COVERPAGE_NAME].Value.LPSZ );
}
if (PROP_TYPE(pPropsMsg[MSGPI_FAX_USE_COVERPAGE].ulPropTag) != PT_ERROR) {
FaxConfig.UseCoverPage = pPropsMsg[MSGPI_FAX_USE_COVERPAGE].Value.ul;
}
if (PROP_TYPE(pPropsMsg[MSGPI_FAX_SERVER_COVERPAGE].ulPropTag) != PT_ERROR) {
FaxConfig.ServerCoverPage = pPropsMsg[MSGPI_FAX_SERVER_COVERPAGE].Value.ul;
}
//
// open the printer
//
PrinterInfo = (PPRINTER_INFO_2) MyGetPrinter( FaxConfig.PrinterName, 2 );
if (!PrinterInfo) {
PrinterInfo = (PPRINTER_INFO_2) MyEnumPrinters( NULL, 2, &CountPrinters, PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS );
if (PrinterInfo) {
for (i=0; i<(int)CountPrinters; i++) {
if (strcmp( PrinterInfo[i].pDriverName, FAX_DRIVER_NAME ) == 0) {
break;
}
}
} else {
CountPrinters = i = 0;
}
if (i == (int)CountPrinters) {
rVal = IDS_NO_FAX_PRINTER;
goto exit;
}
MemFree( FaxConfig.PrinterName );
FaxConfig.PrinterName = StringDup( PrinterInfo[i].pPrinterName );
MemFree( PrinterInfo );
PrinterInfo = (PPRINTER_INFO_2) MyGetPrinter( FaxConfig.PrinterName, 2 );
if (!PrinterInfo) {
rVal = IDS_CANT_ACCESS_PROFILE;
goto exit;
}
}
PrinterDefaults.pDatatype = NULL;
PrinterDefaults.pDevMode = NULL;
PrinterDefaults.DesiredAccess = PRINTER_ACCESS_USE;
if (!OpenPrinter( FaxConfig.PrinterName, &hPrinter, &PrinterDefaults )) {
rVal = IDS_CANT_ACCESS_PRINTER;
goto exit;
}
//
// get the attachment table, if it is available
//
hResult = pMsgObj->GetAttachmentTable( 0, &AttachmentTable );
if (HR_SUCCEEDED(hResult)) {
hResult = HrAddColumns(
AttachmentTable,
(LPSPropTagArray) &sptAttachTableProps,
gpfnAllocateBuffer,
gpfnFreeBuffer
);
if (HR_SUCCEEDED(hResult)) {
hResult = HrQueryAllRows(
AttachmentTable,
NULL,
NULL,
NULL,
0,
&pAttachmentRows
);
if (FAILED(hResult)) {
pAttachmentRows = NULL;
} else {
if (pAttachmentRows->cRows == 0) {
MemFree( pAttachmentRows );
pAttachmentRows = NULL;
}
}
}
}
if (pAttachmentRows) {
//
// this loop verifies that each document's attachment registration
// supports the printto verb.
//
AllAttachmentsGood = TRUE;
for (i=pAttachmentRows->cRows-1; i>=0; i--) {
pPropsAttachTable = pAttachmentRows->aRow[i].lpProps;
lpAttach = NULL;
pPropsAttach = NULL;
if (pPropsAttachTable[MSG_ATTACH_METHOD].Value.ul == NO_ATTACHMENT) {
goto next_attachment1;
}
//
// open the attachment
//
hResult = pMsgObj->OpenAttach( pPropsAttachTable[MSG_ATTACH_NUM].Value.ul, NULL, MAPI_BEST_ACCESS, &lpAttach );
if (FAILED(hResult)) {
AllAttachmentsGood = FALSE;
goto next_attachment1;
}
//
// get the attachment properties
//
hResult = lpAttach->GetProps(
(LPSPropTagArray) &sptAttachProps,
0,
&PropCount,
&pPropsAttach
);
if (FAILED(hResult)) {
AllAttachmentsGood = FALSE;
goto next_attachment1;
}
//
// try to get the extension if the file.
// this indicates what type of dicument it is.
// if we cannot get the document type then it is
// impossible to print the document.
//
if (DocType) {
MemFree( DocType );
DocType = NULL;
}
if (PROP_TYPE(pPropsAttach[MSG_ATTACH_EXTENSION].ulPropTag) == PT_ERROR) {
if (PROP_TYPE(pPropsAttach[MSG_ATTACH_LFILENAME].ulPropTag) != PT_ERROR) {
p = strchr( pPropsAttach[MSG_ATTACH_LFILENAME].Value.LPSZ, '.' );
if (p) {
DocType = StringDup( p );
}
} else if (PROP_TYPE(pPropsAttach[MSG_ATTACH_FILENAME].ulPropTag) != PT_ERROR) {
p = strchr( pPropsAttach[MSG_ATTACH_FILENAME].Value.LPSZ, '.' );
if (p) {
DocType = StringDup( p );
}
}
} else {
DocType = StringDup( pPropsAttach[MSG_ATTACH_EXTENSION].Value.LPSZ );
}
if (!DocType) {
AllAttachmentsGood = FALSE;
goto next_attachment1;
}
Bytes = sizeof(TempFile);
rVal = RegQueryValue( HKEY_CLASSES_ROOT, DocType, TempFile, (PLONG) &Bytes );
if ((rVal != ERROR_SUCCESS) && (rVal != ERROR_INVALID_DATA)) {
AllAttachmentsGood = FALSE;
goto next_attachment1;
}
wsprintf( TempPath, "%s\\shell\\printto\\command", TempFile );
Bytes = sizeof(TempFile);
rVal = RegQueryValue( HKEY_CLASSES_ROOT, TempPath, TempFile, (PLONG) &Bytes );
if ((rVal != ERROR_SUCCESS) && (rVal != ERROR_INVALID_DATA)) {
AllAttachmentsGood = FALSE;
goto next_attachment1;
}
next_attachment1:
if (lpAttach) {
lpAttach->Release();
}
if (pPropsAttach) {
MemFree( pPropsAttach );
}
}
if (!AllAttachmentsGood) {
rVal = IDS_BAD_ATTACHMENTS;
goto exit;
}
for (i=pAttachmentRows->cRows-1; i>=0; i--) {
pPropsAttachTable = pAttachmentRows->aRow[i].lpProps;
lpAttach = NULL;
pPropsAttach = NULL;
if (pPropsAttachTable[MSG_ATTACH_METHOD].Value.ul == NO_ATTACHMENT) {
goto next_attachment2;
}
//
// open the attachment
//
hResult = pMsgObj->OpenAttach( pPropsAttachTable[MSG_ATTACH_NUM].Value.ul, NULL, MAPI_BEST_ACCESS, &lpAttach );
if (FAILED(hResult)) {
goto next_attachment2;
}
//
// get the attachment properties
//
hResult = lpAttach->GetProps(
(LPSPropTagArray) &sptAttachProps,
0,
&PropCount,
&pPropsAttach
);
if (FAILED(hResult)) {
goto next_attachment2;
}
//
// try to get the extension if the file.
// this indicates what type of dicument it is.
// if we cannot get the document type then it is
// impossible to print the document.
//
if (DocType) {
MemFree( DocType );
DocType = NULL;
}
if (PROP_TYPE(pPropsAttach[MSG_ATTACH_EXTENSION].ulPropTag) == PT_ERROR) {
if (PROP_TYPE(pPropsAttach[MSG_ATTACH_LFILENAME].ulPropTag) != PT_ERROR) {
p = strchr( pPropsAttach[MSG_ATTACH_LFILENAME].Value.LPSZ, '.' );
if (p) {
DocType = StringDup( p );
}
} else if (PROP_TYPE(pPropsAttach[MSG_ATTACH_FILENAME].ulPropTag) != PT_ERROR) {
p = strchr( pPropsAttach[MSG_ATTACH_FILENAME].Value.LPSZ, '.' );
if (p) {
DocType = StringDup( p );
}
}
} else {
DocType = StringDup( pPropsAttach[MSG_ATTACH_EXTENSION].Value.LPSZ );
}
if (!DocType) {
goto next_attachment2;
}
lpstmA = NULL;
AttachFileName = NULL;
DeleteAttachFile = FALSE;
//
// get the attached file name so that we can resolve any links
//
if (PROP_TYPE(pPropsAttach[MSG_ATTACH_PATHNAME].ulPropTag) != PT_ERROR) {
FileName = pPropsAttach[MSG_ATTACH_PATHNAME].Value.LPSZ;
} else {
FileName = NULL;
}
if (_stricmp( DocType, LNK_FILENAME_EXT ) == 0) {
if (!FileName) {
goto next_attachment2;
}
if (ResolveShortcut( FileName, DocFile )) {
p = strchr( DocFile, '.' );
if (p) {
MemFree( DocType );
DocType = StringDup( p );
AttachFileName = StringDup( DocFile );
}
}
} else if (FileName) {
AttachFileName = StringDup( FileName );
}
//
// get the stream object
//
switch( pPropsAttach[MSG_ATTACH_METHOD].Value.ul ) {
case ATTACH_BY_VALUE:
hResult = lpAttach->OpenProperty(
PR_ATTACH_DATA_BIN,
&IID_IStream,
0,
0,
(LPUNKNOWN*) &lpstmA
);
if (FAILED(hResult)) {
goto next_attachment2;
}
break;
case ATTACH_EMBEDDED_MSG:
case ATTACH_OLE:
hResult = lpAttach->OpenProperty(
PR_ATTACH_DATA_OBJ,
&IID_IStreamDocfile,
0,
0,
(LPUNKNOWN*) &lpstmA
);
if (FAILED(hResult)) {
hResult = lpAttach->OpenProperty(
PR_ATTACH_DATA_BIN,
&IID_IStreamDocfile,
0,
0,
(LPUNKNOWN*) &lpstmA
);
if (FAILED(hResult)) {
hResult = lpAttach->OpenProperty(
PR_ATTACH_DATA_OBJ,
&IID_IStorage,
0,
0,
(LPUNKNOWN*) &lpstmA
);
if (FAILED(hResult)) {
goto next_attachment2;
}
}
}
break;
}
if (lpstmA) {
GetTempPath( sizeof(TempPath), TempPath );
GetTempFileName( TempPath, "Fax", 0, TempFile );
hFile = CreateFile(
TempFile,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
0,
NULL
);
if (hFile != INVALID_HANDLE_VALUE) {
#define BLOCK_SIZE (64*1024)
LPBYTE StrmData;
DWORD Bytes;
DWORD BytesWrite;
StrmData = (LPBYTE) MemAlloc( BLOCK_SIZE );
do {
hResult = lpstmA->Read( StrmData, BLOCK_SIZE, &Bytes );
if (FAILED(hResult)) {
break;
}
WriteFile( hFile, StrmData, Bytes, &BytesWrite, NULL );
} while (Bytes == BLOCK_SIZE);
CloseHandle( hFile );
MemFree( StrmData );
if (AttachFileName) {
MemFree( AttachFileName );
}
strcpy( DocFile, TempFile );
p = strchr( DocFile, '.' );
if (p) {
strcpy( p, DocType );
MoveFile( TempFile, DocFile );
AttachFileName = StringDup( DocFile );
} else {
AttachFileName = StringDup( TempFile );
}
DeleteAttachFile = TRUE;
}
lpstmA->Release();
}
if (AttachFileName) {
//
// print the attachment
//
JobIdAttachment = PrintAttachment( FaxConfig.PrinterName, AttachFileName );
if (JobIdAttachment) {
GetJob( hPrinter, JobIdAttachment, 1, NULL, 0, &Bytes );
JobInfo1 = (PJOB_INFO_1) MemAlloc( Bytes );
if (JobInfo1) {
if (GetJob( hPrinter, JobIdAttachment, 1, (LPBYTE) JobInfo1, Bytes, &Bytes )) {
Pages += JobInfo1->TotalPages;
}
MemFree( JobInfo1 );
}
if (LastJobId) {
JobInfo3.JobId = JobIdAttachment;
JobInfo3.NextJobId = LastJobId;
JobInfo3.Reserved = 0;
SetJob( hPrinter, JobIdAttachment, 3, (PBYTE) &JobInfo3, 0 );
SetJob( hPrinter, LastJobId, 0, NULL, JOB_CONTROL_RESUME );
}
LastJobId = JobIdAttachment;
}
if (DeleteAttachFile) {
DeleteFile( AttachFileName );
}
MemFree( AttachFileName );
}
next_attachment2:
if (lpAttach) {
lpAttach->Release();
}
if (pPropsAttach) {
MemFree( pPropsAttach );
}
}
}
FullName[0] = 0;
ec = RegOpenKey(
HKEY_CURRENT_USER,
REGKEY_FAX_USERINFO,
&hKey
);
if (ec == ERROR_SUCCESS) {
RegSize = sizeof(FullName);
ec = RegQueryValueEx(
hKey,
REGVAL_FULLNAME,
0,
&RegType,
(LPBYTE) FullName,
&RegSize
);
RegCloseKey( hKey );
}
#ifdef WIN95
JobId = 0xffffffff;
//
// allocate FaxInfo structure
//
FaxInfo Fax_Info;
memset( &Fax_Info, 0, sizeof(FaxInfo) );
//
// provide recipient-specific information
//
strncpy(
Fax_Info.Recipients[0].szName,
pRecipProps[RECIP_NAME].Value.lpszA,
NAME_LEN + 1
);
strncpy(
Fax_Info.Recipients[0].szFaxNumber,
pRecipProps[RECIP_EMAIL_ADR].Value.lpszA,
PHONE_NUMBER_LEN
);
Fax_Info.nRecipientCount = 1;
//
// provide cover page information
//
Fax_Info.bSendCoverPage = FaxConfig.UseCoverPage;
if (Fax_Info.bSendCoverPage) {
Fax_Info.bLocalCoverPage = ! FaxConfig.ServerCoverPage;
strncpy( Fax_Info.szCoverPage, FaxConfig.CoverPageName, MAX_PATH );
strcat( Fax_Info.szCoverPage, ".cov" );
}
strncpy( Fax_Info.szSubject, pMsgProps[MSG_SUBJECT].Value.lpszA, SUBJECT_LEN+1 );
//
// formatted date/time
//
LocaleId = LOCALE_USER_DEFAULT;
lpszBuffer = Fax_Info.szTimeSent;
Bytes = sizeof( Fax_Info.szTimeSent );
BytesWrite = GetDateFormat( LocaleId, 0x0000, NULL, NULL, lpszBuffer, Bytes );
if (BytesWrite == 0) {
rVal = GetLastError();
goto exit;
} else if (BytesWrite >= Bytes) {
rVal = ERROR_INSUFFICIENT_BUFFER;
goto exit;
} else {
Bytes -= BytesWrite;
}
strcat( lpszBuffer, " " );
Bytes -= 1;
lpszBuffer = &lpszBuffer[strlen(lpszBuffer)];
if (Bytes == 0) {
rVal = ERROR_INSUFFICIENT_BUFFER;
goto exit;
}
BytesWrite = GetTimeFormat( LocaleId, 0x0000, NULL, NULL, lpszBuffer, Bytes );
if (BytesWrite == 0) {
rVal = GetLastError();
goto exit;
} else if ( BytesWrite >= Bytes ) {
rVal = ERROR_INSUFFICIENT_BUFFER;
goto exit;
}
//
// Get system code page and other sender info not grabbed from the INI file
//
Fax_Info.CodePage = GetACP();
//
// provide sender-specific information to fax driver
//
hinstFwProv = LoadLibrary( "fwprov.dll" );
if (!hinstFwProv) {
rVal = GetLastError();
goto exit;
}
//
// initiate fax print job
//
pfnStartPrintJob = (PFWSPJ)GetProcAddress( hinstFwProv, "fwStartPrintJob" );
if (!pfnStartPrintJob) {
rVal = GetLastError();
goto exit;
}
if (!pfnStartPrintJob( FaxConfig.PrinterName, &Fax_Info, (PDWORD)&JobId, &hDC )) {
rVal = GetLastError();
goto exit;
}
//
// done with fwprov.dll
//
if (hinstFwProv) {
FreeLibrary( hinstFwProv );
}
#else
GetUserInfo( FaxConfig.PrinterName, &UserInfo );
TCHAR DocName[64];
LoadString(FaxXphInstance,IDS_FAX_MESSAGE,DocName,sizeof(DocName)/sizeof(TCHAR));
FaxPrintInfo.SizeOfStruct = sizeof(FAX_PRINT_INFOA);
FaxPrintInfo.DocName = DocName;
FaxPrintInfo.RecipientName = pRecipProps[RECIP_NAME].Value.lpszA;
FaxPrintInfo.RecipientNumber = pRecipProps[RECIP_EMAIL_ADR].Value.lpszA;
FaxPrintInfo.SenderName = FullName;
FaxPrintInfo.SenderCompany = UserInfo.Company;
FaxPrintInfo.SenderDept = UserInfo.Dept;
FaxPrintInfo.SenderBillingCode = UserInfo.BillingCode;
JobId = 0xffffffff;
if (PrinterInfo->Attributes & PRINTER_ATTRIBUTE_LOCAL) {
FaxPrintInfo.DrProfileName = m_ProfileName;
FaxPrintInfo.DrEmailAddress = NULL;
} else {
FaxPrintInfo.DrEmailAddress = m_ProfileName;
FaxPrintInfo.DrProfileName = NULL;
}
ContextInfo.SizeOfStruct = sizeof(FAX_CONTEXT_INFOW);
ec = FaxStartPrintJob(
FaxConfig.PrinterName,
&FaxPrintInfo,
(LPDWORD) &JobId,
&ContextInfo
);
if (!ec) {
rVal= GetLastError();
goto exit;
}
if (FaxConfig.UseCoverPage) {
FaxCpInfo.SizeOfStruct = sizeof(FAX_COVERPAGE_INFOA);
FaxCpInfo.CoverPageName = FaxConfig.CoverPageName;
FaxCpInfo.UseServerCoverPage = FaxConfig.ServerCoverPage;
FaxCpInfo.RecName = pRecipProps[RECIP_NAME].Value.lpszA;
FaxCpInfo.RecFaxNumber = pRecipProps[RECIP_EMAIL_ADR].Value.lpszA;
FaxCpInfo.Subject = pMsgProps[MSG_SUBJECT].Value.lpszA;
FaxCpInfo.Note = NULL;
FaxCpInfo.PageCount = Pages + 2;
GetLocalTime( &FaxCpInfo.TimeSent );
ec = FaxPrintCoverPage(
&ContextInfo,
&FaxCpInfo
);
if (!ec) {
rVal= GetLastError();
goto exit;
}
} else if (strlen(pMsgProps[MSG_SUBJECT].Value.lpszA) && !lpstmT) {
//
// HACK: try to use the "basenote.cov" coverpage so that we at least print something.
// if they just entered in a subject but no note, then nothing will be printed
//
TCHAR CoverpageName[MAX_PATH];
ExpandEnvironmentStrings(TEXT("%systemroot%\\system32\\basenote.cov"),CoverpageName,sizeof(CoverpageName));
FaxCpInfo.SizeOfStruct = sizeof(FAX_COVERPAGE_INFOA);
FaxCpInfo.CoverPageName = CoverpageName;
FaxCpInfo.UseServerCoverPage = FALSE;
FaxCpInfo.RecName = pRecipProps[RECIP_NAME].Value.lpszA;
FaxCpInfo.RecFaxNumber = pRecipProps[RECIP_EMAIL_ADR].Value.lpszA;
FaxCpInfo.Subject = pMsgProps[MSG_SUBJECT].Value.lpszA;
FaxCpInfo.Note = NULL;
FaxCpInfo.PageCount = Pages + 2;
GetLocalTime( &FaxCpInfo.TimeSent );
ec = FaxPrintCoverPage(
&ContextInfo,
&FaxCpInfo
);
//
// don't bail if this fails, it's a hack anyway
//
/*if (!ec) {
rVal= GetLastError();
goto exit;
} */
}
#endif
if (lpstmT) {
//
// position the stream to the beginning
//
hResult = lpstmT->Seek( BigZero, STREAM_SEEK_SET, NULL );
if (HR_FAILED (hResult)) {
rVal = IDS_CANT_ACCESS_MSG_DATA;
goto exit;
}
if (UseRichText) {
hResult = WrapCompressedRTFStream( lpstmT, 0, &lpstm );
if (HR_FAILED (hResult)) {
rVal = IDS_CANT_ACCESS_MSG_DATA;
goto exit;
}
//
// print the document
//
hWndRichEdit = CreateWindowEx(
WS_OVERLAPPED,
"RICHEDIT",
"",
ES_MULTILINE,
0, 0,
0, 0,
NULL,
NULL,
FaxXphInstance,
NULL
);
if (!hWndRichEdit) {
ec = GetLastError();
rVal = IDS_CANT_ACCESS_MSG_DATA;
goto exit;
}
es.pfnCallback = EditStreamRead;
es.dwCookie = (DWORD_PTR) lpstm;
SendMessage(
hWndRichEdit,
EM_STREAMIN,
SF_RTF | SFF_SELECTION | SFF_PLAINRTF,
(LPARAM) &es
);
if (es.dwError) {
ec = es.dwError;
rVal = IDS_CANT_ACCESS_MSG_DATA;
goto exit;
}
#ifdef WIN95
PrintRichText( hWndRichEdit, hDC, &FaxConfig );
#else
PrintRichText( hWndRichEdit, ContextInfo.hDC, &FaxConfig );
#endif
} else {
#ifdef WIN95
PrintText( hDC, lpstmT, &FaxConfig );
#else
PrintText( ContextInfo.hDC, lpstmT, &FaxConfig );
#endif
}
}
if (LastJobId) {
//
// chain the main body job to the first
// attachment job and resume the attachment job
//
JobInfo3.JobId = JobId;
JobInfo3.NextJobId = LastJobId;
JobInfo3.Reserved = 0;
if (!SetJob( hPrinter, JobId, 3, (PBYTE) &JobInfo3, 0 )) {
DebugPrint(( "SetJob() failed, ec=%d", GetLastError() ));
}
if (!SetJob( hPrinter, LastJobId, 0, NULL, JOB_CONTROL_RESUME )) {
DebugPrint(( "SetJob() failed, ec=%d", GetLastError() ));
}
//
// update the page count
//
GetJob( hPrinter, JobId, 1, NULL, 0, &Bytes );
JobInfo1 = (PJOB_INFO_1) MemAlloc( Bytes );
if (JobInfo1) {
JobInfo1->TotalPages += Pages;
if (!SetJob( hPrinter, JobId, 1, (PBYTE) JobInfo1, 0 )) {
DebugPrint(( "SetJob() failed, ec=%d", GetLastError() ));
}
MemFree( JobInfo1 );
}
}
rVal = 0;
exit:
if (pProfileObj) {
pProfileObj->Release();
}
if (pProps) {
MemFree( pProps );
}
if (MsgPropTags) {
MemFree( MsgPropTags );
}
if (pPropsMsg) {
MemFree( pPropsMsg );
}
if (pAttachmentRows) {
MemFree( pAttachmentRows );
}
if (AttachmentTable) {
AttachmentTable->Release();
}
if (lpstm) {
lpstm->Release();
}
#ifdef WIN95
if (hDC) {
EndDoc( hDC );
SetJob( hPrinter, JobId, 0, NULL, JOB_CONTROL_RESUME );
DeleteDC( hDC );
}
#else
if (ContextInfo.hDC) {
EndDoc( ContextInfo.hDC );
SetJob( hPrinter, JobId, 0, NULL, JOB_CONTROL_RESUME );
DeleteDC( ContextInfo.hDC );
}
#endif
if (hPrinter) {
ClosePrinter( hPrinter );
}
if (hWndRichEdit) {
DestroyWindow( hWndRichEdit );
}
if (PrinterInfo) {
MemFree( PrinterInfo );
}
if (FaxConfig.PrinterName) {
MemFree( FaxConfig.PrinterName );
}
if (FaxConfig.CoverPageName) {
MemFree( FaxConfig.CoverPageName );
}
if (DocType) {
MemFree( DocType );
}
return rVal;
}