windows-nt/Source/XPSP1/NT/printscan/fax/samples/broadcast/broadcst.cpp

599 lines
13 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
simple.c
Abstract:
This module implements a command line broadcast fax utility
--*/
#include <windows.h>
#include <winbase.h>
#include <stdio.h>
#include <stdlib.h>
#include <winfax.h>
#include <tchar.h>
#include <assert.h>
#include <shellapi.h>
#include <initguid.h>
#include "adoint.h"
#include "adoid.h"
//
// simple debug macro, get's compiled out in release version
//
#ifdef DBG
TCHAR szDebugBuffer[128];
#define DEBUG(parm1,parm2)\
{\
wsprintf(szDebugBuffer, TEXT(parm1), parm2);\
OutputDebugString(szDebugBuffer);\
}
#else
#define DEBUG(parm1,parm2)
#endif
//
// prototypes
//
VOID CALLBACK GetRecipientDataFromDb(ADORecordset*, DWORD, LPTSTR, LPTSTR );
BOOL CALLBACK FaxRecipientCallbackFunction( HANDLE FaxHandle,
DWORD RecipientNumber,
LPVOID Context,
PFAX_JOB_PARAMW JobParams,
PFAX_COVERPAGE_INFOW CoverpageInfo
);
ADORecordset* InitializeDb(VOID);
//
// globals
//
ADORecordset* pRecordSet;
HANDLE hEvent;
void GiveUsage(
LPTSTR AppName
)
/*++
Routine Description:
prints out usage
Arguments:
AppName - string representing name of app
Return Value:
none.
--*/
{
_tprintf( TEXT("Usage : %s /d <full path to doc> /n --send a fax to each user in database query\n"),AppName);
_tprintf( TEXT("Usage : %s /? -- this message\n"),AppName);
}
int _cdecl
main(
int argc,
char *argvA[]
)
/*++
Routine Description:
Entry point to the setup program
Arguments:
argc - Number of args.
argvA - the commandline arguments.
Return Value:
--*/
{
LPTSTR *argv;
int argcount = 0;
TCHAR Document[MAX_PATH] = {0};
HANDLE hFax;
DWORD FaxJobId;
BOOL bTerminate = FALSE;
HANDLE hPort;
//
// do commandline stuff
//
#ifdef UNICODE
argv = CommandLineToArgvW( GetCommandLine(), &argc );
#else
argv = argvA;
#endif
DEBUG ("Number of arguments = %d\n",argc);
for (argcount=0;argcount<argc;argcount++) {
DEBUG ("Arg %d:",argcount);
DEBUG (" %s\n",argv[argcount]);
}
// check for commandline switches
for (argcount=0; argcount<argc; argcount++) {
if ((argv[argcount][0] == L'/') || (argv[argcount][0] == L'-')) {
switch (towlower(argv[argcount][1])) {
case 'd':
lstrcpy(Document, argv[argcount+1]);
break;
case '?':
GiveUsage(argv[0]);
return 0;
default:
break;
}
}
}
if (!Document[0]) {
_tprintf( TEXT("Missing args.\n") );
GiveUsage(argv[0]);
return -1;
}
CoInitialize(NULL);
hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
//
// initialize the db
//
pRecordSet = InitializeDb();
if (!pRecordSet) {
_tprintf( TEXT("Problems initializing Database, check data source\n") );
CoUninitialize();
return -1;
}
//
// connect to fax service
//
if (!FaxConnectFaxServer(NULL,&hFax)) {
_tprintf( TEXT("FaxConnectFaxServer failed, ec = %d\n"),GetLastError() );
CoUninitialize();
return -1;
}
assert (hFax != NULL);
//
// start the broadcast
//
if (!FaxSendDocumentForBroadcast( hFax,
Document,
&FaxJobId,
FaxRecipientCallbackFunction,
pRecordSet ) ) {
_tprintf( TEXT("FaxSendDocumentforBroadcast failed, ec = %d \n"), GetLastError() );
FaxClose( hFax );
pRecordSet->Release();
CoUninitialize();
return -1;
}
//
// wait for the callback to complete
//
WaitForSingleObject( hEvent, INFINITE );
_tprintf( TEXT("Queued document %s to all recipients\n"), Document);
//
// cleanup
//
FaxClose( hFax );
pRecordSet->Release();
CoUninitialize();
return 1;
}
LPTSTR
StringDup(
LPTSTR Source
)
/*++
Routine Description:
allocates memory off of the stack for a string and copies string to that memory
Arguments:
Source - string to be copied
Return Value:
NULL for error, else a copy of the string, alloced off of the stack
--*/
{
LPTSTR dest;
if (!Source) {
return NULL;
}
dest = (LPTSTR) HeapAlloc( GetProcessHeap(),0, (lstrlen(Source) +1)*sizeof(TCHAR) );
if (!dest) {
return NULL;
}
lstrcpy( dest, Source );
return dest;
}
BOOL CALLBACK
FaxRecipientCallbackFunction(
HANDLE FaxHandle,
DWORD RecipientNumber,
LPVOID Context,
PFAX_JOB_PARAMW JobParams,
PFAX_COVERPAGE_INFOW CoverpageInfo OPTIONAL
)
/*++
Routine Description:
main faxback callback function
Arguments:
FaxHandle - handle to fax service
RecipientNumber - number of times this function has been called
Context - context info (in our case, a ADORecordset pointer)
JobParams - pointer to a FAX_JOB_PARAM structure to receive our information
CoverpageInfo - pointer to a FAX_COVERPAGE_INFO structure to receive our information
Return Value:
TRUE -- use the data we set, FALSE, done sending data back to fax service.
--*/
{
TCHAR RecipientNameBuf[128];
TCHAR RecipientNumberBuf[64];
//
// only send 3 faxes total (arbitrary number)
//
if (RecipientNumber > 3) {
SetEvent( hEvent );
return FALSE;
}
GetRecipientDataFromDb( (ADORecordset*)Context,
RecipientNumber,
RecipientNameBuf,
RecipientNumberBuf
);
CoverpageInfo = NULL;
JobParams->RecipientNumber = StringDup(RecipientNumberBuf);
JobParams->RecipientName = StringDup(RecipientNameBuf);
return TRUE;
}
ADORecordset*
InitializeDb(
VOID
)
/*++
Routine Description:
initailizes our database connection which holds the recipient data (uses ADO)
Arguments:
none.
Return Value:
NULL on error, else an initialized ADORecordSet object (ready to call GetRows).
--*/
{
ADORecordset* prs = NULL;
ADOConnection* pc = NULL;
IDispatch *pdisp = NULL;
HRESULT hr;
VARIANT vSource, vCommand;
BSTR ConnectionString=NULL,
UserID=NULL,
Password=NULL,
SqlStmt=NULL;
//
// get pointers to the required interfaces
//
hr = CoCreateInstance( CLSID_CADOConnection,
NULL,
CLSCTX_INPROC_SERVER,
IID_IADOConnection,
(void **)&pc );
if (FAILED(hr)) {
_tprintf( TEXT("CoCreateInstance failed, ec = %x\n"), hr );
return NULL;
}
hr = CoCreateInstance( CLSID_CADORecordset,
NULL,
CLSCTX_INPROC_SERVER,
IID_IADORecordset,
(void **)&prs );
if (FAILED(hr)) {
_tprintf( TEXT("CoCreateInstance failed, ec = %x\n"), hr );
return NULL;
}
assert(pc != NULL);
assert(prs != NULL);
ConnectionString = SysAllocString( TEXT("nwind_odbc") );
UserID = SysAllocString( TEXT("") );
Password = SysAllocString( TEXT("") );
SqlStmt = SysAllocString( TEXT("SELECT LastName,FirstName FROM Employees") );
hr = pc->QueryInterface(IID_IDispatch,(void**) &pdisp);
if (FAILED(hr)) {
_tprintf( TEXT("couldn't qi, ec = %x\n"), hr);
goto error_exit;
}
//
// open the connection
//
hr = pc->Open( ConnectionString, UserID, Password, -1 ) ;
if (FAILED(hr)) {
_tprintf( TEXT("Open failed, ec = %x\n"), hr );
goto error_exit;
}
hr = prs->put_Source(SqlStmt);
if (FAILED(hr)) {
_tprintf( TEXT("put_Source failed, ec = %x\n"), hr );
goto error_exit;
}
hr = prs->putref_ActiveConnection(pdisp);
if (FAILED(hr)) {
_tprintf( TEXT("putref_ActiveConnection failed, ec = %x\n"), hr );
goto error_exit;
}
vCommand.vt = VT_BSTR;
vCommand.bstrVal = SqlStmt;
vSource.vt = VT_DISPATCH;
//vSource.punkVal = pdisp;
vSource.pdispVal = pdisp;
//vNull.vt = VT_ERROR;
//vNull.scode = DISP_E_PARAMNOTFOUND;
//
// open the recordset
//
hr = prs->Open( vCommand, //VARIANT
vSource, //VARIANT
adOpenForwardOnly, //CursorTypeEnum
adLockReadOnly, //LockTypeEnum
adCmdUnknown // LONG
);
if (FAILED(hr)) {
_tprintf( TEXT("Open failed, ec = %x\n"), hr);
pdisp->Release();
goto error_exit;
}
pdisp->Release();
//
// cleanup and return
//
pc->Release();
SysFreeString( ConnectionString );
SysFreeString( UserID );
SysFreeString( Password );
SysFreeString( SqlStmt );
return prs;
error_exit:
//
// cleanup
//
if (ConnectionString) SysFreeString( ConnectionString );
if (UserID) SysFreeString( UserID );
if (Password) SysFreeString( Password );
if (SqlStmt) SysFreeString( SqlStmt );
if (prs) prs->Release();
if (pc) pc->Release();
return NULL;
}
VOID CALLBACK
GetRecipientDataFromDb(
ADORecordset* pRecordSet,
DWORD RowToRetreive,
LPTSTR RecipientNameBuffer,
LPTSTR RecipientNumberBuffer
)
/*++
Routine Description:
retreives the appropriate data from the database (uses ADO)
Arguments:
pRecordSet - ADORecordset pointer for our database
RowToRetreive - which row should we retreive
RecipientNameBuffer - buffer to receive recipient name
RecipientNumberBuffer - buffer to receive recipient number
Return Value:
none. on error the buffers are set to an empty string (NULL)
--*/
{
static long CurrentRow = 0;
static long TotalRows;
VARIANT vBookmark, rgvFields;
static VARIANT cRows;
VARIANT varField, varNewField;
long Index[2];
HRESULT hr;
//
// the first we're called, let's retrieve the data and then just let it stick around after that point.
//
if (CurrentRow == 0) {
//
//Start from the current place
//
vBookmark.vt = VT_ERROR;
vBookmark.scode = DISP_E_PARAMNOTFOUND;
//
// Get all columns
//
rgvFields.vt = VT_ERROR;
rgvFields.scode = DISP_E_PARAMNOTFOUND;
assert(pRecordSet != NULL);
//
// get the rows
//
hr = pRecordSet->GetRows(adGetRowsRest,
vBookmark,
rgvFields,
&cRows );
if (FAILED(hr)) {
_tprintf( TEXT("GetRows failed, ec = %x\n"), hr );
*RecipientNameBuffer = 0;
*RecipientNumberBuffer = 0;
return;
}
//
// find out the number of rows retreived
//
hr = SafeArrayGetUBound(cRows.parray, 2, &TotalRows);
if (FAILED(hr)) {
_tprintf( TEXT("SafeArrayGetUBound failed, ec=%x\n"), hr );
*RecipientNameBuffer = 0;
*RecipientNumberBuffer = 0;
return;
}
_tprintf( TEXT("There are %d rows in our datasource\n"), TotalRows );
}
//
// data is retrieved at this point. now, get the data and stick it into the caller's buffers
//
if ((LONG)RowToRetreive >TotalRows) {
*RecipientNumberBuffer = 0;
*RecipientNameBuffer = 0;
return;
}
//
// column major order for a safearray
//
assert(RowToRetreive == CurrentRow);
Index[1]=CurrentRow;
for (int i = 0; i< 2; i++) {
Index[0]=i;
//
// get element
//
hr = SafeArrayGetElement( cRows.parray, &Index[0], &varField );
if (FAILED(hr)) {
_tprintf( TEXT("SafeArrayGetElement failed, ec=hr\n"), hr );
*RecipientNameBuffer = 0;
*RecipientNumberBuffer = 0;
return;
}
//
// make sure it's a string
//
hr = VariantChangeType(&varNewField, &varField, 0, VT_BSTR);
if (FAILED(hr)) {
_tprintf( TEXT("VariantChangeType failed, ec=hr\n"), hr );
*RecipientNameBuffer = 0;
*RecipientNumberBuffer = 0;
return;
}
//
// copy the data
//
if (i == 0) {
lstrcpy(RecipientNameBuffer, varNewField.bstrVal);
} else {
lstrcpy(RecipientNumberBuffer, varNewField.bstrVal);
}
}
CurrentRow++;
return;
}