/*++ Copyright (c) 1998 Microsoft Corporation Module Name: simple.c Abstract: This module implements a command line broadcast fax utility --*/ #include #include #include #include #include #include #include #include #include #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 /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;argcountRelease(); 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; }