Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

598 lines
13 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. simple.c
  5. Abstract:
  6. This module implements a command line broadcast fax utility
  7. --*/
  8. #include <windows.h>
  9. #include <winbase.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <winfax.h>
  13. #include <tchar.h>
  14. #include <assert.h>
  15. #include <shellapi.h>
  16. #include <initguid.h>
  17. #include "adoint.h"
  18. #include "adoid.h"
  19. //
  20. // simple debug macro, get's compiled out in release version
  21. //
  22. #ifdef DBG
  23. TCHAR szDebugBuffer[128];
  24. #define DEBUG(parm1,parm2)\
  25. {\
  26. wsprintf(szDebugBuffer, TEXT(parm1), parm2);\
  27. OutputDebugString(szDebugBuffer);\
  28. }
  29. #else
  30. #define DEBUG(parm1,parm2)
  31. #endif
  32. //
  33. // prototypes
  34. //
  35. VOID CALLBACK GetRecipientDataFromDb(ADORecordset*, DWORD, LPTSTR, LPTSTR );
  36. BOOL CALLBACK FaxRecipientCallbackFunction( HANDLE FaxHandle,
  37. DWORD RecipientNumber,
  38. LPVOID Context,
  39. PFAX_JOB_PARAMW JobParams,
  40. PFAX_COVERPAGE_INFOW CoverpageInfo
  41. );
  42. ADORecordset* InitializeDb(VOID);
  43. //
  44. // globals
  45. //
  46. ADORecordset* pRecordSet;
  47. HANDLE hEvent;
  48. void GiveUsage(
  49. LPTSTR AppName
  50. )
  51. /*++
  52. Routine Description:
  53. prints out usage
  54. Arguments:
  55. AppName - string representing name of app
  56. Return Value:
  57. none.
  58. --*/
  59. {
  60. _tprintf( TEXT("Usage : %s /d <full path to doc> /n --send a fax to each user in database query\n"),AppName);
  61. _tprintf( TEXT("Usage : %s /? -- this message\n"),AppName);
  62. }
  63. int _cdecl
  64. main(
  65. int argc,
  66. char *argvA[]
  67. )
  68. /*++
  69. Routine Description:
  70. Entry point to the setup program
  71. Arguments:
  72. argc - Number of args.
  73. argvA - the commandline arguments.
  74. Return Value:
  75. --*/
  76. {
  77. LPTSTR *argv;
  78. int argcount = 0;
  79. TCHAR Document[MAX_PATH] = {0};
  80. HANDLE hFax;
  81. DWORD FaxJobId;
  82. BOOL bTerminate = FALSE;
  83. HANDLE hPort;
  84. //
  85. // do commandline stuff
  86. //
  87. #ifdef UNICODE
  88. argv = CommandLineToArgvW( GetCommandLine(), &argc );
  89. #else
  90. argv = argvA;
  91. #endif
  92. DEBUG ("Number of arguments = %d\n",argc);
  93. for (argcount=0;argcount<argc;argcount++) {
  94. DEBUG ("Arg %d:",argcount);
  95. DEBUG (" %s\n",argv[argcount]);
  96. }
  97. // check for commandline switches
  98. for (argcount=0; argcount<argc; argcount++) {
  99. if ((argv[argcount][0] == L'/') || (argv[argcount][0] == L'-')) {
  100. switch (towlower(argv[argcount][1])) {
  101. case 'd':
  102. lstrcpy(Document, argv[argcount+1]);
  103. break;
  104. case '?':
  105. GiveUsage(argv[0]);
  106. return 0;
  107. default:
  108. break;
  109. }
  110. }
  111. }
  112. if (!Document[0]) {
  113. _tprintf( TEXT("Missing args.\n") );
  114. GiveUsage(argv[0]);
  115. return -1;
  116. }
  117. CoInitialize(NULL);
  118. hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
  119. //
  120. // initialize the db
  121. //
  122. pRecordSet = InitializeDb();
  123. if (!pRecordSet) {
  124. _tprintf( TEXT("Problems initializing Database, check data source\n") );
  125. CoUninitialize();
  126. return -1;
  127. }
  128. //
  129. // connect to fax service
  130. //
  131. if (!FaxConnectFaxServer(NULL,&hFax)) {
  132. _tprintf( TEXT("FaxConnectFaxServer failed, ec = %d\n"),GetLastError() );
  133. CoUninitialize();
  134. return -1;
  135. }
  136. assert (hFax != NULL);
  137. //
  138. // start the broadcast
  139. //
  140. if (!FaxSendDocumentForBroadcast( hFax,
  141. Document,
  142. &FaxJobId,
  143. FaxRecipientCallbackFunction,
  144. pRecordSet ) ) {
  145. _tprintf( TEXT("FaxSendDocumentforBroadcast failed, ec = %d \n"), GetLastError() );
  146. FaxClose( hFax );
  147. pRecordSet->Release();
  148. CoUninitialize();
  149. return -1;
  150. }
  151. //
  152. // wait for the callback to complete
  153. //
  154. WaitForSingleObject( hEvent, INFINITE );
  155. _tprintf( TEXT("Queued document %s to all recipients\n"), Document);
  156. //
  157. // cleanup
  158. //
  159. FaxClose( hFax );
  160. pRecordSet->Release();
  161. CoUninitialize();
  162. return 1;
  163. }
  164. LPTSTR
  165. StringDup(
  166. LPTSTR Source
  167. )
  168. /*++
  169. Routine Description:
  170. allocates memory off of the stack for a string and copies string to that memory
  171. Arguments:
  172. Source - string to be copied
  173. Return Value:
  174. NULL for error, else a copy of the string, alloced off of the stack
  175. --*/
  176. {
  177. LPTSTR dest;
  178. if (!Source) {
  179. return NULL;
  180. }
  181. dest = (LPTSTR) HeapAlloc( GetProcessHeap(),0, (lstrlen(Source) +1)*sizeof(TCHAR) );
  182. if (!dest) {
  183. return NULL;
  184. }
  185. lstrcpy( dest, Source );
  186. return dest;
  187. }
  188. BOOL CALLBACK
  189. FaxRecipientCallbackFunction(
  190. HANDLE FaxHandle,
  191. DWORD RecipientNumber,
  192. LPVOID Context,
  193. PFAX_JOB_PARAMW JobParams,
  194. PFAX_COVERPAGE_INFOW CoverpageInfo OPTIONAL
  195. )
  196. /*++
  197. Routine Description:
  198. main faxback callback function
  199. Arguments:
  200. FaxHandle - handle to fax service
  201. RecipientNumber - number of times this function has been called
  202. Context - context info (in our case, a ADORecordset pointer)
  203. JobParams - pointer to a FAX_JOB_PARAM structure to receive our information
  204. CoverpageInfo - pointer to a FAX_COVERPAGE_INFO structure to receive our information
  205. Return Value:
  206. TRUE -- use the data we set, FALSE, done sending data back to fax service.
  207. --*/
  208. {
  209. TCHAR RecipientNameBuf[128];
  210. TCHAR RecipientNumberBuf[64];
  211. //
  212. // only send 3 faxes total (arbitrary number)
  213. //
  214. if (RecipientNumber > 3) {
  215. SetEvent( hEvent );
  216. return FALSE;
  217. }
  218. GetRecipientDataFromDb( (ADORecordset*)Context,
  219. RecipientNumber,
  220. RecipientNameBuf,
  221. RecipientNumberBuf
  222. );
  223. CoverpageInfo = NULL;
  224. JobParams->RecipientNumber = StringDup(RecipientNumberBuf);
  225. JobParams->RecipientName = StringDup(RecipientNameBuf);
  226. return TRUE;
  227. }
  228. ADORecordset*
  229. InitializeDb(
  230. VOID
  231. )
  232. /*++
  233. Routine Description:
  234. initailizes our database connection which holds the recipient data (uses ADO)
  235. Arguments:
  236. none.
  237. Return Value:
  238. NULL on error, else an initialized ADORecordSet object (ready to call GetRows).
  239. --*/
  240. {
  241. ADORecordset* prs = NULL;
  242. ADOConnection* pc = NULL;
  243. IDispatch *pdisp = NULL;
  244. HRESULT hr;
  245. VARIANT vSource, vCommand;
  246. BSTR ConnectionString=NULL,
  247. UserID=NULL,
  248. Password=NULL,
  249. SqlStmt=NULL;
  250. //
  251. // get pointers to the required interfaces
  252. //
  253. hr = CoCreateInstance( CLSID_CADOConnection,
  254. NULL,
  255. CLSCTX_INPROC_SERVER,
  256. IID_IADOConnection,
  257. (void **)&pc );
  258. if (FAILED(hr)) {
  259. _tprintf( TEXT("CoCreateInstance failed, ec = %x\n"), hr );
  260. return NULL;
  261. }
  262. hr = CoCreateInstance( CLSID_CADORecordset,
  263. NULL,
  264. CLSCTX_INPROC_SERVER,
  265. IID_IADORecordset,
  266. (void **)&prs );
  267. if (FAILED(hr)) {
  268. _tprintf( TEXT("CoCreateInstance failed, ec = %x\n"), hr );
  269. return NULL;
  270. }
  271. assert(pc != NULL);
  272. assert(prs != NULL);
  273. ConnectionString = SysAllocString( TEXT("nwind_odbc") );
  274. UserID = SysAllocString( TEXT("") );
  275. Password = SysAllocString( TEXT("") );
  276. SqlStmt = SysAllocString( TEXT("SELECT LastName,FirstName FROM Employees") );
  277. hr = pc->QueryInterface(IID_IDispatch,(void**) &pdisp);
  278. if (FAILED(hr)) {
  279. _tprintf( TEXT("couldn't qi, ec = %x\n"), hr);
  280. goto error_exit;
  281. }
  282. //
  283. // open the connection
  284. //
  285. hr = pc->Open( ConnectionString, UserID, Password, -1 ) ;
  286. if (FAILED(hr)) {
  287. _tprintf( TEXT("Open failed, ec = %x\n"), hr );
  288. goto error_exit;
  289. }
  290. hr = prs->put_Source(SqlStmt);
  291. if (FAILED(hr)) {
  292. _tprintf( TEXT("put_Source failed, ec = %x\n"), hr );
  293. goto error_exit;
  294. }
  295. hr = prs->putref_ActiveConnection(pdisp);
  296. if (FAILED(hr)) {
  297. _tprintf( TEXT("putref_ActiveConnection failed, ec = %x\n"), hr );
  298. goto error_exit;
  299. }
  300. vCommand.vt = VT_BSTR;
  301. vCommand.bstrVal = SqlStmt;
  302. vSource.vt = VT_DISPATCH;
  303. //vSource.punkVal = pdisp;
  304. vSource.pdispVal = pdisp;
  305. //vNull.vt = VT_ERROR;
  306. //vNull.scode = DISP_E_PARAMNOTFOUND;
  307. //
  308. // open the recordset
  309. //
  310. hr = prs->Open( vCommand, //VARIANT
  311. vSource, //VARIANT
  312. adOpenForwardOnly, //CursorTypeEnum
  313. adLockReadOnly, //LockTypeEnum
  314. adCmdUnknown // LONG
  315. );
  316. if (FAILED(hr)) {
  317. _tprintf( TEXT("Open failed, ec = %x\n"), hr);
  318. pdisp->Release();
  319. goto error_exit;
  320. }
  321. pdisp->Release();
  322. //
  323. // cleanup and return
  324. //
  325. pc->Release();
  326. SysFreeString( ConnectionString );
  327. SysFreeString( UserID );
  328. SysFreeString( Password );
  329. SysFreeString( SqlStmt );
  330. return prs;
  331. error_exit:
  332. //
  333. // cleanup
  334. //
  335. if (ConnectionString) SysFreeString( ConnectionString );
  336. if (UserID) SysFreeString( UserID );
  337. if (Password) SysFreeString( Password );
  338. if (SqlStmt) SysFreeString( SqlStmt );
  339. if (prs) prs->Release();
  340. if (pc) pc->Release();
  341. return NULL;
  342. }
  343. VOID CALLBACK
  344. GetRecipientDataFromDb(
  345. ADORecordset* pRecordSet,
  346. DWORD RowToRetreive,
  347. LPTSTR RecipientNameBuffer,
  348. LPTSTR RecipientNumberBuffer
  349. )
  350. /*++
  351. Routine Description:
  352. retreives the appropriate data from the database (uses ADO)
  353. Arguments:
  354. pRecordSet - ADORecordset pointer for our database
  355. RowToRetreive - which row should we retreive
  356. RecipientNameBuffer - buffer to receive recipient name
  357. RecipientNumberBuffer - buffer to receive recipient number
  358. Return Value:
  359. none. on error the buffers are set to an empty string (NULL)
  360. --*/
  361. {
  362. static long CurrentRow = 0;
  363. static long TotalRows;
  364. VARIANT vBookmark, rgvFields;
  365. static VARIANT cRows;
  366. VARIANT varField, varNewField;
  367. long Index[2];
  368. HRESULT hr;
  369. //
  370. // the first we're called, let's retrieve the data and then just let it stick around after that point.
  371. //
  372. if (CurrentRow == 0) {
  373. //
  374. //Start from the current place
  375. //
  376. vBookmark.vt = VT_ERROR;
  377. vBookmark.scode = DISP_E_PARAMNOTFOUND;
  378. //
  379. // Get all columns
  380. //
  381. rgvFields.vt = VT_ERROR;
  382. rgvFields.scode = DISP_E_PARAMNOTFOUND;
  383. assert(pRecordSet != NULL);
  384. //
  385. // get the rows
  386. //
  387. hr = pRecordSet->GetRows(adGetRowsRest,
  388. vBookmark,
  389. rgvFields,
  390. &cRows );
  391. if (FAILED(hr)) {
  392. _tprintf( TEXT("GetRows failed, ec = %x\n"), hr );
  393. *RecipientNameBuffer = 0;
  394. *RecipientNumberBuffer = 0;
  395. return;
  396. }
  397. //
  398. // find out the number of rows retreived
  399. //
  400. hr = SafeArrayGetUBound(cRows.parray, 2, &TotalRows);
  401. if (FAILED(hr)) {
  402. _tprintf( TEXT("SafeArrayGetUBound failed, ec=%x\n"), hr );
  403. *RecipientNameBuffer = 0;
  404. *RecipientNumberBuffer = 0;
  405. return;
  406. }
  407. _tprintf( TEXT("There are %d rows in our datasource\n"), TotalRows );
  408. }
  409. //
  410. // data is retrieved at this point. now, get the data and stick it into the caller's buffers
  411. //
  412. if ((LONG)RowToRetreive >TotalRows) {
  413. *RecipientNumberBuffer = 0;
  414. *RecipientNameBuffer = 0;
  415. return;
  416. }
  417. //
  418. // column major order for a safearray
  419. //
  420. assert(RowToRetreive == CurrentRow);
  421. Index[1]=CurrentRow;
  422. for (int i = 0; i< 2; i++) {
  423. Index[0]=i;
  424. //
  425. // get element
  426. //
  427. hr = SafeArrayGetElement( cRows.parray, &Index[0], &varField );
  428. if (FAILED(hr)) {
  429. _tprintf( TEXT("SafeArrayGetElement failed, ec=hr\n"), hr );
  430. *RecipientNameBuffer = 0;
  431. *RecipientNumberBuffer = 0;
  432. return;
  433. }
  434. //
  435. // make sure it's a string
  436. //
  437. hr = VariantChangeType(&varNewField, &varField, 0, VT_BSTR);
  438. if (FAILED(hr)) {
  439. _tprintf( TEXT("VariantChangeType failed, ec=hr\n"), hr );
  440. *RecipientNameBuffer = 0;
  441. *RecipientNumberBuffer = 0;
  442. return;
  443. }
  444. //
  445. // copy the data
  446. //
  447. if (i == 0) {
  448. lstrcpy(RecipientNameBuffer, varNewField.bstrVal);
  449. } else {
  450. lstrcpy(RecipientNumberBuffer, varNewField.bstrVal);
  451. }
  452. }
  453. CurrentRow++;
  454. return;
  455. }