1 /*
2 * PROJECT: ReactOS Spooler API
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Functions for managing print jobs
5 * COPYRIGHT: Copyright 2015-2018 Colin Finck (colin@reactos.org)
6 */
7
8 #include "precomp.h"
9 #include <marshalling/jobs.h>
10
11 BOOL WINAPI
AddJobA(HANDLE hPrinter,DWORD Level,PBYTE pData,DWORD cbBuf,PDWORD pcbNeeded)12 AddJobA(HANDLE hPrinter, DWORD Level, PBYTE pData, DWORD cbBuf, PDWORD pcbNeeded)
13 {
14 BOOL ret;
15
16 FIXME("AddJobA(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pData, cbBuf, pcbNeeded);
17
18 if (Level != 1)
19 {
20 ERR("Level = %d, unsupported!\n", Level);
21 SetLastError(ERROR_INVALID_LEVEL);
22 return FALSE;
23 }
24
25 ret = AddJobW(hPrinter, Level, pData, cbBuf, pcbNeeded);
26
27 if (ret)
28 {
29 DWORD dwErrorCode;
30 ADDJOB_INFO_1W *addjobW = (ADDJOB_INFO_1W*)pData;
31 dwErrorCode = UnicodeToAnsiInPlace(addjobW->Path);
32 if (dwErrorCode != ERROR_SUCCESS)
33 {
34 ret = FALSE;
35 }
36 }
37 return ret;
38 }
39
40 BOOL WINAPI
AddJobW(HANDLE hPrinter,DWORD Level,PBYTE pData,DWORD cbBuf,PDWORD pcbNeeded)41 AddJobW(HANDLE hPrinter, DWORD Level, PBYTE pData, DWORD cbBuf, PDWORD pcbNeeded)
42 {
43 DWORD dwErrorCode;
44 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
45
46 FIXME("AddJobW(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pData, cbBuf, pcbNeeded);
47
48 if (!pHandle)
49 {
50 dwErrorCode = ERROR_INVALID_HANDLE;
51 goto Cleanup;
52 }
53
54 // Do the RPC call
55 RpcTryExcept
56 {
57 dwErrorCode = _RpcAddJob(pHandle->hPrinter, Level, pData, cbBuf, pcbNeeded);
58 }
59 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
60 {
61 dwErrorCode = RpcExceptionCode();
62 ERR("_RpcAddJob failed with exception code %lu!\n", dwErrorCode);
63 }
64 RpcEndExcept;
65
66 if (dwErrorCode == ERROR_SUCCESS)
67 {
68 JOB_INFO_1W* pji1w = (JOB_INFO_1W*)pData;
69
70 // Replace relative offset addresses in the output by absolute pointers.
71 MarshallUpStructure(cbBuf, pData, AddJobInfo1Marshalling.pInfo, AddJobInfo1Marshalling.cbStructureSize, TRUE);
72 pHandle->bJob = TRUE;
73 UpdateTrayIcon( hPrinter, pji1w->JobId );
74 }
75
76 Cleanup:
77 SetLastError(dwErrorCode);
78 return (dwErrorCode == ERROR_SUCCESS);
79 }
80
81 BOOL WINAPI
EnumJobsA(HANDLE hPrinter,DWORD FirstJob,DWORD NoJobs,DWORD Level,PBYTE pJob,DWORD cbBuf,PDWORD pcbNeeded,PDWORD pcReturned)82 EnumJobsA(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
83 {
84 DWORD dwErrorCode, i;
85 JOB_INFO_1W* pji1w = (JOB_INFO_1W*)pJob;
86 JOB_INFO_2A* pji2a = (JOB_INFO_2A*)pJob;
87 JOB_INFO_2W* pji2w = (JOB_INFO_2W*)pJob;
88
89 TRACE("EnumJobsA(%p, %lu, %lu, %lu, %p, %lu, %p, %p)\n", hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned);
90
91 if ( Level == 3 )
92 return EnumJobsW( hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned );
93
94 if ( EnumJobsW( hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned ) )
95 {
96 switch ( Level )
97 {
98 case 1:
99 {
100 for ( i = 0; i < *pcReturned; i++ )
101 {
102 dwErrorCode = UnicodeToAnsiInPlace(pji1w[i].pPrinterName);
103 if (dwErrorCode != ERROR_SUCCESS)
104 {
105 goto Cleanup;
106 }
107 dwErrorCode = UnicodeToAnsiInPlace(pji1w[i].pMachineName);
108 if (dwErrorCode != ERROR_SUCCESS)
109 {
110 goto Cleanup;
111 }
112 dwErrorCode = UnicodeToAnsiInPlace(pji1w[i].pUserName);
113 if (dwErrorCode != ERROR_SUCCESS)
114 {
115 goto Cleanup;
116 }
117 dwErrorCode = UnicodeToAnsiInPlace(pji1w[i].pDocument);
118 if (dwErrorCode != ERROR_SUCCESS)
119 {
120 goto Cleanup;
121 }
122 dwErrorCode = UnicodeToAnsiInPlace(pji1w[i].pDatatype);
123 if (dwErrorCode != ERROR_SUCCESS)
124 {
125 goto Cleanup;
126 }
127 dwErrorCode = UnicodeToAnsiInPlace(pji1w[i].pStatus);
128 if (dwErrorCode != ERROR_SUCCESS)
129 {
130 goto Cleanup;
131 }
132 }
133 }
134 break;
135
136 case 2:
137 {
138 for ( i = 0; i < *pcReturned; i++ )
139 {
140 dwErrorCode = UnicodeToAnsiInPlace(pji2w[i].pPrinterName);
141 if (dwErrorCode != ERROR_SUCCESS)
142 {
143 goto Cleanup;
144 }
145 dwErrorCode = UnicodeToAnsiInPlace(pji2w[i].pMachineName);
146 if (dwErrorCode != ERROR_SUCCESS)
147 {
148 goto Cleanup;
149 }
150 dwErrorCode = UnicodeToAnsiInPlace(pji2w[i].pUserName);
151 if (dwErrorCode != ERROR_SUCCESS)
152 {
153 goto Cleanup;
154 }
155 dwErrorCode = UnicodeToAnsiInPlace(pji2w[i].pDocument);
156 if (dwErrorCode != ERROR_SUCCESS)
157 {
158 goto Cleanup;
159 }
160 dwErrorCode = UnicodeToAnsiInPlace(pji2w[i].pNotifyName);
161 if (dwErrorCode != ERROR_SUCCESS)
162 {
163 goto Cleanup;
164 }
165 dwErrorCode = UnicodeToAnsiInPlace(pji2w[i].pDatatype);
166 if (dwErrorCode != ERROR_SUCCESS)
167 {
168 goto Cleanup;
169 }
170 dwErrorCode = UnicodeToAnsiInPlace(pji2w[i].pPrintProcessor);
171 if (dwErrorCode != ERROR_SUCCESS)
172 {
173 goto Cleanup;
174 }
175 dwErrorCode = UnicodeToAnsiInPlace(pji2w[i].pParameters);
176 if (dwErrorCode != ERROR_SUCCESS)
177 {
178 goto Cleanup;
179 }
180 dwErrorCode = UnicodeToAnsiInPlace(pji2w[i].pStatus);
181 if (dwErrorCode != ERROR_SUCCESS)
182 {
183 goto Cleanup;
184 }
185 if ( pji2w[i].pDevMode )
186 {
187 RosConvertUnicodeDevModeToAnsiDevmode( pji2w[i].pDevMode, pji2a[i].pDevMode );
188 }
189 }
190 }
191 break;
192 }
193 return TRUE;
194 }
195 Cleanup:
196 return FALSE;
197 }
198
199 BOOL WINAPI
EnumJobsW(HANDLE hPrinter,DWORD FirstJob,DWORD NoJobs,DWORD Level,PBYTE pJob,DWORD cbBuf,PDWORD pcbNeeded,PDWORD pcReturned)200 EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
201 {
202 DWORD dwErrorCode;
203 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
204
205 TRACE("EnumJobsW(%p, %lu, %lu, %lu, %p, %lu, %p, %p)\n", hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned);
206
207 if (!pHandle)
208 {
209 dwErrorCode = ERROR_INVALID_HANDLE;
210 goto Cleanup;
211 }
212
213 // Do the RPC call
214 RpcTryExcept
215 {
216 dwErrorCode = _RpcEnumJobs(pHandle->hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned);
217 }
218 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
219 {
220 dwErrorCode = RpcExceptionCode();
221 ERR("_RpcEnumJobs failed with exception code %lu!\n", dwErrorCode);
222 }
223 RpcEndExcept;
224
225 if (dwErrorCode == ERROR_SUCCESS)
226 {
227 // Replace relative offset addresses in the output by absolute pointers for JOB_INFO_1W and JOB_INFO_2W.
228 if (Level <= 2)
229 {
230 ASSERT(Level >= 1);
231 MarshallUpStructuresArray(cbBuf, pJob, *pcReturned, pJobInfoMarshalling[Level]->pInfo, pJobInfoMarshalling[Level]->cbStructureSize, TRUE);
232 }
233 }
234
235 Cleanup:
236 SetLastError(dwErrorCode);
237 return (dwErrorCode == ERROR_SUCCESS);
238 }
239
240 BOOL WINAPI
GetJobA(HANDLE hPrinter,DWORD JobId,DWORD Level,PBYTE pJob,DWORD cbBuf,PDWORD pcbNeeded)241 GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded)
242 {
243 DWORD dwErrorCode;
244 JOB_INFO_1W* pji1w = (JOB_INFO_1W*)pJob;
245 JOB_INFO_2A* pji2a = (JOB_INFO_2A*)pJob;
246 JOB_INFO_2W* pji2w = (JOB_INFO_2W*)pJob;
247
248 FIXME("GetJobA(%p, %lu, %lu, %p, %lu, %p)\n", hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
249
250 if ( Level == 3 )
251 return GetJobW( hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded );
252
253 if ( GetJobW( hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded ) )
254 {
255 switch ( Level )
256 {
257 case 1:
258 dwErrorCode = UnicodeToAnsiInPlace(pji1w->pPrinterName);
259 if (dwErrorCode != ERROR_SUCCESS)
260 {
261 goto Cleanup;
262 }
263 dwErrorCode = UnicodeToAnsiInPlace(pji1w->pMachineName);
264 if (dwErrorCode != ERROR_SUCCESS)
265 {
266 goto Cleanup;
267 }
268 dwErrorCode = UnicodeToAnsiInPlace(pji1w->pUserName);
269 if (dwErrorCode != ERROR_SUCCESS)
270 {
271 goto Cleanup;
272 }
273 dwErrorCode = UnicodeToAnsiInPlace(pji1w->pDocument);
274 if (dwErrorCode != ERROR_SUCCESS)
275 {
276 goto Cleanup;
277 }
278 dwErrorCode = UnicodeToAnsiInPlace(pji1w->pDatatype);
279 if (dwErrorCode != ERROR_SUCCESS)
280 {
281 goto Cleanup;
282 }
283 dwErrorCode = UnicodeToAnsiInPlace(pji1w->pStatus);
284 if (dwErrorCode != ERROR_SUCCESS)
285 {
286 goto Cleanup;
287 }
288 break;
289
290 case 2:
291 dwErrorCode = UnicodeToAnsiInPlace(pji2w->pPrinterName);
292 if (dwErrorCode != ERROR_SUCCESS)
293 {
294 goto Cleanup;
295 }
296 dwErrorCode = UnicodeToAnsiInPlace(pji2w->pMachineName);
297 if (dwErrorCode != ERROR_SUCCESS)
298 {
299 goto Cleanup;
300 }
301 dwErrorCode = UnicodeToAnsiInPlace(pji2w->pUserName);
302 if (dwErrorCode != ERROR_SUCCESS)
303 {
304 goto Cleanup;
305 }
306 dwErrorCode = UnicodeToAnsiInPlace(pji2w->pDocument);
307 if (dwErrorCode != ERROR_SUCCESS)
308 {
309 goto Cleanup;
310 }
311 dwErrorCode = UnicodeToAnsiInPlace(pji2w->pNotifyName);
312 if (dwErrorCode != ERROR_SUCCESS)
313 {
314 goto Cleanup;
315 }
316 dwErrorCode = UnicodeToAnsiInPlace(pji2w->pDatatype);
317 if (dwErrorCode != ERROR_SUCCESS)
318 {
319 goto Cleanup;
320 }
321 dwErrorCode = UnicodeToAnsiInPlace(pji2w->pPrintProcessor);
322 if (dwErrorCode != ERROR_SUCCESS)
323 {
324 goto Cleanup;
325 }
326 dwErrorCode = UnicodeToAnsiInPlace(pji2w->pParameters);
327 if (dwErrorCode != ERROR_SUCCESS)
328 {
329 goto Cleanup;
330 }
331 dwErrorCode = UnicodeToAnsiInPlace(pji2w->pStatus);
332 if (dwErrorCode != ERROR_SUCCESS)
333 {
334 goto Cleanup;
335 }
336 if ( pji2w->pDevMode )
337 {
338 RosConvertUnicodeDevModeToAnsiDevmode( pji2w->pDevMode, pji2a->pDevMode );
339 }
340 break;
341 }
342 return TRUE;
343 }
344 Cleanup:
345 return FALSE;
346 }
347
348 BOOL WINAPI
GetJobW(HANDLE hPrinter,DWORD JobId,DWORD Level,PBYTE pJob,DWORD cbBuf,PDWORD pcbNeeded)349 GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded)
350 {
351 DWORD dwErrorCode;
352 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
353
354 FIXME("GetJobW(%p, %lu, %lu, %p, %lu, %p)\n", hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
355
356 if (!pHandle)
357 {
358 dwErrorCode = ERROR_INVALID_HANDLE;
359 goto Cleanup;
360 }
361
362 // Do the RPC call
363 RpcTryExcept
364 {
365 dwErrorCode = _RpcGetJob(pHandle->hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
366 }
367 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
368 {
369 dwErrorCode = RpcExceptionCode();
370 ERR("_RpcGetJob failed with exception code %lu!\n", dwErrorCode);
371 }
372 RpcEndExcept;
373
374 if (dwErrorCode == ERROR_SUCCESS)
375 {
376 // Replace relative offset addresses in the output by absolute pointers.
377 ASSERT(Level >= 1 && Level <= 2);
378 MarshallUpStructure(cbBuf, pJob, pJobInfoMarshalling[Level]->pInfo, pJobInfoMarshalling[Level]->cbStructureSize, TRUE);
379 }
380
381 Cleanup:
382 SetLastError(dwErrorCode);
383 return (dwErrorCode == ERROR_SUCCESS);
384 }
385
386 BOOL WINAPI
ScheduleJob(HANDLE hPrinter,DWORD dwJobID)387 ScheduleJob(HANDLE hPrinter, DWORD dwJobID)
388 {
389 DWORD dwErrorCode;
390 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
391
392 TRACE("ScheduleJob(%p, %lu)\n", hPrinter, dwJobID);
393
394 if (!pHandle)
395 {
396 dwErrorCode = ERROR_INVALID_HANDLE;
397 goto Cleanup;
398 }
399
400 // Do the RPC call
401 RpcTryExcept
402 {
403 dwErrorCode = _RpcScheduleJob(pHandle->hPrinter, dwJobID);
404 }
405 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
406 {
407 dwErrorCode = RpcExceptionCode();
408 ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode);
409 }
410 RpcEndExcept;
411
412 if ( dwErrorCode == ERROR_SUCCESS )
413 pHandle->bJob = FALSE;
414
415 Cleanup:
416 SetLastError(dwErrorCode);
417 return (dwErrorCode == ERROR_SUCCESS);
418 }
419
420 BOOL WINAPI
SetJobA(HANDLE hPrinter,DWORD JobId,DWORD Level,PBYTE pJobInfo,DWORD Command)421 SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command)
422 {
423 BOOL ret;
424 LPBYTE JobW;
425 UNICODE_STRING usBuffer;
426
427 TRACE("SetJobA(%p, %lu, %lu, %p, %lu)\n", hPrinter, JobId, Level, pJobInfo, Command);
428
429 /* JobId, pPrinterName, pMachineName, pDriverName, Size, Submitted, Time and TotalPages
430 are all ignored by SetJob, so we don't bother copying them */
431 switch(Level)
432 {
433 case 0:
434 JobW = NULL;
435 break;
436 case 1:
437 {
438 JOB_INFO_1A *info1A = (JOB_INFO_1A*)pJobInfo;
439 JOB_INFO_1W *info1W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info1W));
440 ZeroMemory( info1W, sizeof(JOB_INFO_1W) );
441
442 JobW = (LPBYTE)info1W;
443 info1W->pUserName = AsciiToUnicode(&usBuffer, info1A->pUserName);
444 info1W->pDocument = AsciiToUnicode(&usBuffer, info1A->pDocument);
445 info1W->pDatatype = AsciiToUnicode(&usBuffer, info1A->pDatatype);
446 info1W->pStatus = AsciiToUnicode(&usBuffer, info1A->pStatus);
447 info1W->Status = info1A->Status;
448 info1W->Priority = info1A->Priority;
449 info1W->Position = info1A->Position;
450 info1W->PagesPrinted = info1A->PagesPrinted;
451 break;
452 }
453 case 2:
454 {
455 JOB_INFO_2A *info2A = (JOB_INFO_2A*)pJobInfo;
456 JOB_INFO_2W *info2W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info2W));
457 ZeroMemory( info2W, sizeof(JOB_INFO_2W) );
458
459 JobW = (LPBYTE)info2W;
460 info2W->pUserName = AsciiToUnicode(&usBuffer, info2A->pUserName);
461 info2W->pDocument = AsciiToUnicode(&usBuffer, info2A->pDocument);
462 info2W->pNotifyName = AsciiToUnicode(&usBuffer, info2A->pNotifyName);
463 info2W->pDatatype = AsciiToUnicode(&usBuffer, info2A->pDatatype);
464 info2W->pPrintProcessor = AsciiToUnicode(&usBuffer, info2A->pPrintProcessor);
465 info2W->pParameters = AsciiToUnicode(&usBuffer, info2A->pParameters);
466 info2W->pDevMode = info2A->pDevMode ? GdiConvertToDevmodeW(info2A->pDevMode) : NULL;
467 info2W->pStatus = AsciiToUnicode(&usBuffer, info2A->pStatus);
468 info2W->pSecurityDescriptor = info2A->pSecurityDescriptor;
469 info2W->Status = info2A->Status;
470 info2W->Priority = info2A->Priority;
471 info2W->Position = info2A->Position;
472 info2W->StartTime = info2A->StartTime;
473 info2W->UntilTime = info2A->UntilTime;
474 info2W->PagesPrinted = info2A->PagesPrinted;
475 break;
476 }
477 case 3:
478 JobW = HeapAlloc(GetProcessHeap(), 0, sizeof(JOB_INFO_3));
479 memcpy(JobW, pJobInfo, sizeof(JOB_INFO_3));
480 break;
481 default:
482 ERR("Level = %d, unsupported!\n", Level);
483 SetLastError(ERROR_INVALID_LEVEL);
484 return FALSE;
485 }
486
487 ret = SetJobW(hPrinter, JobId, Level, JobW, Command);
488
489 switch(Level)
490 {
491 case 1:
492 {
493 JOB_INFO_1W *info1W = (JOB_INFO_1W*)JobW;
494 if (info1W->pUserName) HeapFree(GetProcessHeap(), 0, info1W->pUserName);
495 if (info1W->pDocument) HeapFree(GetProcessHeap(), 0, info1W->pDocument);
496 if (info1W->pDatatype) HeapFree(GetProcessHeap(), 0, info1W->pDatatype);
497 if (info1W->pStatus) HeapFree(GetProcessHeap(), 0, info1W->pStatus);
498 break;
499 }
500 case 2:
501 {
502 JOB_INFO_2W *info2W = (JOB_INFO_2W*)JobW;
503 if (info2W->pUserName) HeapFree(GetProcessHeap(), 0, info2W->pUserName);
504 if (info2W->pDocument) HeapFree(GetProcessHeap(), 0, info2W->pDocument);
505 if (info2W->pNotifyName) HeapFree(GetProcessHeap(), 0, info2W->pNotifyName);
506 if (info2W->pDatatype) HeapFree(GetProcessHeap(), 0, info2W->pDatatype);
507 if (info2W->pPrintProcessor) HeapFree(GetProcessHeap(), 0, info2W->pPrintProcessor);
508 if (info2W->pParameters) HeapFree(GetProcessHeap(), 0, info2W->pParameters);
509 if (info2W->pDevMode) HeapFree(GetProcessHeap(), 0, info2W->pDevMode);
510 if (info2W->pStatus) HeapFree(GetProcessHeap(), 0, info2W->pStatus);
511 break;
512 }
513 }
514 HeapFree(GetProcessHeap(), 0, JobW);
515
516 return ret;
517 }
518
519 BOOL WINAPI
SetJobW(HANDLE hPrinter,DWORD JobId,DWORD Level,PBYTE pJobInfo,DWORD Command)520 SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command)
521 {
522 DWORD dwErrorCode;
523 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
524 WINSPOOL_JOB_CONTAINER JobContainer;
525
526 TRACE("SetJobW(%p, %lu, %lu, %p, %lu)\n", hPrinter, JobId, Level, pJobInfo, Command);
527
528 if (!pHandle)
529 {
530 dwErrorCode = ERROR_INVALID_HANDLE;
531 goto Cleanup;
532 }
533
534 // pJobContainer->JobInfo is a union of pointers, so we can just set any element to our BYTE pointer.
535 JobContainer.Level = Level;
536 JobContainer.JobInfo.Level1 = (WINSPOOL_JOB_INFO_1*)pJobInfo;
537
538 // Do the RPC call
539 RpcTryExcept
540 {
541 dwErrorCode = _RpcSetJob(pHandle->hPrinter, JobId, &JobContainer, Command);
542 }
543 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
544 {
545 dwErrorCode = RpcExceptionCode();
546 ERR("_RpcSetJob failed with exception code %lu!\n", dwErrorCode);
547 }
548 RpcEndExcept;
549
550 Cleanup:
551 SetLastError(dwErrorCode);
552 return (dwErrorCode == ERROR_SUCCESS);
553 }
554