599 lines
13 KiB
C++
599 lines
13 KiB
C++
|
/*++
|
||
|
|
||
|
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;
|
||
|
|
||
|
|
||
|
}
|