Added an unknown VxD error code.
[wine] / dlls / gdi / printdrv.c
1 /* 
2  * Implementation of some printer driver bits
3  * 
4  * Copyright 1996 John Harvey
5  * Copyright 1998 Huw Davies
6  * Copyright 1998 Andreas Mohr
7  * Copyright 1999 Klaas van Gend
8  */
9
10 #include "config.h"
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <ctype.h>
16 #include <errno.h>
17 #include <unistd.h>
18 #include <fcntl.h>
19 #include "winbase.h"
20 #include "wine/winbase16.h"
21 #include "wine/wingdi16.h"
22 #include "winspool.h"
23 #include "winerror.h"
24 #include "winreg.h"
25 #include "debugtools.h"
26 #include "gdi.h"
27 #include "callback.h"
28 #include "options.h"
29 #include "heap.h"
30 #include "file.h"
31
32 DEFAULT_DEBUG_CHANNEL(print);
33
34 static char PrinterModel[]      = "Printer Model";
35 static char DefaultDevMode[]    = "Default DevMode";
36 static char PrinterDriverData[] = "PrinterDriverData";
37 static char Printers[]          = "System\\CurrentControlSet\\Control\\Print\\Printers\\";
38
39 /******************************************************************
40  *                  StartDoc16  [GDI.377]
41  *
42  */
43 INT16 WINAPI StartDoc16( HDC16 hdc, const DOCINFO16 *lpdoc )
44 {
45     DOCINFOA docA;
46
47     docA.cbSize = lpdoc->cbSize;
48     docA.lpszDocName = MapSL(lpdoc->lpszDocName);
49     docA.lpszOutput = MapSL(lpdoc->lpszOutput);
50
51     if(lpdoc->cbSize >= 14)
52         docA.lpszDatatype = MapSL(lpdoc->lpszDatatype);
53     else
54         docA.lpszDatatype = NULL;
55
56     if(lpdoc->cbSize >= 18)
57         docA.fwType = lpdoc->fwType;
58     else
59         docA.fwType = 0;
60
61     return StartDocA(hdc, &docA);
62 }
63
64 /******************************************************************
65  *                  StartDocA  [GDI32.347]
66  *
67  * StartDoc calls the STARTDOC Escape with the input data pointing to DocName
68  * and the output data (which is used as a second input parameter).pointing at
69  * the whole docinfo structure.  This seems to be an undocumented feature of
70  * the STARTDOC Escape. 
71  */
72 INT WINAPI StartDocA(HDC hdc, const DOCINFOA* doc)
73 {
74     INT ret;
75     DC *dc = DC_GetDCPtr( hdc );
76
77     TRACE("DocName = '%s' Output = '%s' Datatype = '%s'\n",
78           doc->lpszDocName, doc->lpszOutput, doc->lpszDatatype);
79
80     if(!dc) return SP_ERROR;
81
82     if(dc->funcs->pStartDoc)
83         ret = dc->funcs->pStartDoc( dc, doc );
84     else
85         ret = Escape(hdc, STARTDOC, strlen(doc->lpszDocName),
86                       doc->lpszDocName, (LPVOID)doc);
87     GDI_ReleaseObj( hdc );
88     return ret;
89 }
90
91 /*************************************************************************
92  *                  StartDocW [GDI32.348]
93  * 
94  */
95 INT WINAPI StartDocW(HDC hdc, const DOCINFOW* doc)
96 {
97     DOCINFOA docA;
98     INT ret;
99
100     docA.cbSize = doc->cbSize;
101     docA.lpszDocName = doc->lpszDocName ? 
102       HEAP_strdupWtoA( GetProcessHeap(), 0, doc->lpszDocName ) : NULL;
103     docA.lpszOutput = doc->lpszOutput ?
104       HEAP_strdupWtoA( GetProcessHeap(), 0, doc->lpszOutput ) : NULL;
105     docA.lpszDatatype = doc->lpszDatatype ?
106       HEAP_strdupWtoA( GetProcessHeap(), 0, doc->lpszDatatype ) : NULL;
107     docA.fwType = doc->fwType;
108
109     ret = StartDocA(hdc, &docA);
110
111     if(docA.lpszDocName)
112         HeapFree( GetProcessHeap(), 0, (LPSTR)docA.lpszDocName );
113     if(docA.lpszOutput)
114         HeapFree( GetProcessHeap(), 0, (LPSTR)docA.lpszOutput );
115     if(docA.lpszDatatype)
116         HeapFree( GetProcessHeap(), 0, (LPSTR)docA.lpszDatatype );
117
118     return ret;
119 }
120
121 /******************************************************************
122  *                  EndDoc16  [GDI.378]
123  *
124  */
125 INT16 WINAPI EndDoc16(HDC16 hdc)
126 {
127     return EndDoc(hdc);
128 }
129
130 /******************************************************************
131  *                  EndDoc  [GDI32.76]
132  *
133  */
134 INT WINAPI EndDoc(HDC hdc)
135 {
136     INT ret;
137     DC *dc = DC_GetDCPtr( hdc );
138     if(!dc) return SP_ERROR;
139
140     if(dc->funcs->pEndDoc)
141         ret = dc->funcs->pEndDoc( dc );
142     else
143         ret = Escape(hdc, ENDDOC, 0, 0, 0);
144     GDI_ReleaseObj( hdc );
145     return ret;
146 }
147
148 /******************************************************************
149  *                  StartPage16  [GDI.379]
150  *
151  */
152 INT16 WINAPI StartPage16(HDC16 hdc)
153 {
154     return StartPage(hdc);
155 }
156
157 /******************************************************************
158  *                  StartPage  [GDI32.349]
159  *
160  */
161 INT WINAPI StartPage(HDC hdc)
162 {
163     INT ret = 1;
164     DC *dc = DC_GetDCPtr( hdc );
165     if(!dc) return SP_ERROR;
166
167     if(dc->funcs->pStartPage)
168         ret = dc->funcs->pStartPage( dc );
169     else
170         FIXME("stub\n");
171     GDI_ReleaseObj( hdc );
172     return ret;
173 }
174
175 /******************************************************************
176  *                  EndPage16  [GDI.380]
177  *
178  */
179 INT16 WINAPI EndPage16( HDC16 hdc )
180 {
181     return EndPage(hdc);
182 }
183
184 /******************************************************************
185  *                  EndPage  [GDI32.77]
186  *
187  */
188 INT WINAPI EndPage(HDC hdc)
189 {
190     INT ret;
191     DC *dc = DC_GetDCPtr( hdc );
192     if(!dc) return SP_ERROR;
193
194     if(dc->funcs->pEndPage)
195         ret = dc->funcs->pEndPage( dc );
196     else
197         ret = Escape(hdc, NEWFRAME, 0, 0, 0);
198     GDI_ReleaseObj( hdc );
199     if (!QueryAbort16( hdc, 0 ))
200     {
201         EndDoc( hdc );
202         ret = 0;
203     }
204     return ret;
205 }
206
207 /******************************************************************************
208  *                 AbortDoc16  [GDI.382]
209  */
210 INT16 WINAPI AbortDoc16(HDC16 hdc)
211 {
212     return AbortDoc(hdc);
213 }
214
215 /******************************************************************************
216  *                 AbortDoc  [GDI32.105]
217  */
218 INT WINAPI AbortDoc(HDC hdc)
219 {
220     INT ret;
221     DC *dc = DC_GetDCPtr( hdc );
222     if(!dc) return SP_ERROR;
223
224     if(dc->funcs->pAbortDoc)
225         ret = dc->funcs->pAbortDoc( dc );
226     else
227         ret = Escape(hdc, ABORTDOC, 0, 0, 0);
228     GDI_ReleaseObj( hdc );
229     return ret;
230 }
231
232 /**********************************************************************
233  *           QueryAbort16   (GDI.155)
234  *
235  *  Calls the app's AbortProc function if avail.
236  *
237  * RETURNS
238  * TRUE if no AbortProc avail or AbortProc wants to continue printing.
239  * FALSE if AbortProc wants to abort printing.
240  */
241 BOOL16 WINAPI QueryAbort16(HDC16 hdc, INT16 reserved)
242 {
243     BOOL ret = TRUE;
244     DC *dc = DC_GetDCPtr( hdc );
245
246     if(!dc) {
247         ERR("Invalid hdc %04x\n", hdc);
248         return FALSE;
249     }
250
251     if (dc->pAbortProc) ret = dc->pAbortProc(hdc, 0);
252     GDI_ReleaseObj( hdc );
253     return ret;
254 }
255
256 /* ### start build ### */
257 extern WORD CALLBACK PRTDRV_CallTo16_word_ww(FARPROC16,WORD,WORD);
258 /* ### stop build ### */
259
260 /**********************************************************************
261  *           SetAbortProc16   (GDI.381)
262  *
263  */
264 INT16 WINAPI SetAbortProc16(HDC16 hdc, SEGPTR abrtprc)
265 {
266     ABORTPROC proc32 = (ABORTPROC)THUNK_Alloc((FARPROC16)abrtprc,
267                                               (RELAY)PRTDRV_CallTo16_word_ww);
268     return SetAbortProc(hdc, proc32);
269 }
270
271 /**********************************************************************
272  *           SetAbortProc   (GDI32.301)
273  *
274  */
275 INT WINAPI SetAbortProc(HDC hdc, ABORTPROC abrtprc)
276 {
277     DC *dc = DC_GetDCPtr( hdc );
278
279     if(dc->pAbortProc) THUNK_Free((FARPROC)dc->pAbortProc);
280     dc->pAbortProc = abrtprc;
281     GDI_ReleaseObj( hdc );
282     return TRUE;
283 }
284
285
286 /****************** misc. printer related functions */
287
288 /*
289  * The following function should implement a queing system
290  */
291 struct hpq 
292 {
293     struct hpq  *next;
294     int          tag;
295     int          key;
296 };
297
298 static struct hpq *hpqueue;
299
300 /**********************************************************************
301  *           CreatePQ   (GDI.230)
302  *
303  */
304 HPQ16 WINAPI CreatePQ16(INT16 size) 
305 {
306 #if 0
307     HGLOBAL16 hpq = 0;
308     WORD tmp_size;
309     LPWORD pPQ;
310
311     tmp_size = size << 2;
312     if (!(hpq = GlobalAlloc16(GMEM_SHARE|GMEM_MOVEABLE, tmp_size + 8)))
313        return 0xffff;
314     pPQ = GlobalLock16(hpq);
315     *pPQ++ = 0;
316     *pPQ++ = tmp_size;
317     *pPQ++ = 0;
318     *pPQ++ = 0;
319     GlobalUnlock16(hpq);
320
321     return (HPQ16)hpq;
322 #else
323     FIXME("(%d): stub\n",size);
324     return 1;
325 #endif
326 }
327
328 /**********************************************************************
329  *           DeletePQ   (GDI.235)
330  *
331  */
332 INT16 WINAPI DeletePQ16(HPQ16 hPQ) 
333 {
334     return GlobalFree16((HGLOBAL16)hPQ);
335 }
336
337 /**********************************************************************
338  *           ExtractPQ   (GDI.232)
339  *
340  */
341 INT16 WINAPI ExtractPQ16(HPQ16 hPQ) 
342
343     struct hpq *queue, *prev, *current, *currentPrev;
344     int key = 0, tag = -1;
345     currentPrev = prev = NULL;
346     queue = current = hpqueue;
347     if (current)
348         key = current->key;
349     
350     while (current)
351     {
352         currentPrev = current;
353         current = current->next;
354         if (current)
355         {
356             if (current->key < key)
357             {
358                 queue = current;
359                 prev = currentPrev;
360             }
361         }
362     }
363     if (queue)
364     {
365         tag = queue->tag;
366         
367         if (prev)
368             prev->next = queue->next;
369         else
370             hpqueue = queue->next;
371         HeapFree(GetProcessHeap(), 0, queue);
372     }
373     
374     TRACE("%x got tag %d key %d\n", hPQ, tag, key); 
375
376     return tag;
377 }
378
379 /**********************************************************************
380  *           InsertPQ   (GDI.233)
381  *
382  */
383 INT16 WINAPI InsertPQ16(HPQ16 hPQ, INT16 tag, INT16 key) 
384 {
385     struct hpq *queueItem = HeapAlloc(GetProcessHeap(), 0, sizeof(struct hpq));
386     if(queueItem == NULL) {
387         ERR("Memory exausted!\n");
388         return FALSE;
389     }
390     queueItem->next = hpqueue;
391     hpqueue = queueItem;
392     queueItem->key = key;
393     queueItem->tag = tag;
394     
395     FIXME("(%x %d %d): stub???\n", hPQ, tag, key);
396     return TRUE;
397 }
398
399 /**********************************************************************
400  *           MinPQ   (GDI.231)
401  *
402  */
403 INT16 WINAPI MinPQ16(HPQ16 hPQ) 
404 {
405     FIXME("(%x): stub\n", hPQ); 
406     return 0;
407 }
408
409 /**********************************************************************
410  *           SizePQ   (GDI.234)
411  *
412  */
413 INT16 WINAPI SizePQ16(HPQ16 hPQ, INT16 sizechange) 
414 {  
415     FIXME("(%x %d): stub\n", hPQ, sizechange); 
416     return -1; 
417 }
418
419
420
421 /* 
422  * The following functions implement part of the spooling process to 
423  * print manager.  I would like to see wine have a version of print managers
424  * that used LPR/LPD.  For simplicity print jobs will be sent to a file for
425  * now.
426  */
427 typedef struct PRINTJOB
428 {
429     char        *pszOutput;
430     char        *pszTitle;
431     HDC16       hDC;
432     HANDLE16    hHandle;
433     int         nIndex;
434     int         fd;
435 } PRINTJOB, *PPRINTJOB;
436
437 #define MAX_PRINT_JOBS 1
438 #define SP_OK 1
439
440 PPRINTJOB gPrintJobsTable[MAX_PRINT_JOBS];
441
442
443 static PPRINTJOB FindPrintJobFromHandle(HANDLE16 hHandle)
444 {
445     return gPrintJobsTable[0];
446 }
447
448 static int CreateSpoolFile(LPCSTR pszOutput)
449 {
450     int fd=-1;
451     char psCmd[1024];
452     char *psCmdP = psCmd;
453
454     /* TTD convert the 'output device' into a spool file name */
455
456     if (pszOutput == NULL || *pszOutput == '\0')
457       return -1;
458
459     if (!strncmp("LPR:",pszOutput,4))
460       sprintf(psCmd,"|lpr -P%s",pszOutput+4);
461     else
462       PROFILE_GetWineIniString("spooler",pszOutput,"",psCmd,sizeof(psCmd));
463     TRACE("Got printerSpoolCommand '%s' for output device '%s'\n",
464           psCmd, pszOutput);
465     if (!*psCmd)
466         psCmdP = (char *)pszOutput;
467     else
468     {
469         while (*psCmdP && isspace(*psCmdP))
470         {
471             psCmdP++;
472         };
473         if (!*psCmdP)
474             return -1;
475     }
476     if (*psCmdP == '|')
477     {
478         int fds[2];
479         if (pipe(fds))
480             return -1;
481         if (fork() == 0)
482         {
483             psCmdP++;
484
485             TRACE("In child need to exec %s\n",psCmdP);
486             close(0);
487             dup2(fds[0],0);
488             close (fds[1]);
489             system(psCmdP);
490             exit(0);
491             
492         }
493         close (fds[0]);
494         fd = fds[1];
495         TRACE("Need to execute a cmnd and pipe the output to it\n");
496     }
497     else
498     {
499         DOS_FULL_NAME fullName;
500
501         TRACE("Just assume it's a file\n");
502
503         /**
504          * The file name can be dos based, we have to find its
505          * Unix correspondant file name
506          */
507         DOSFS_GetFullName(psCmdP, FALSE, &fullName);
508
509         if ((fd = open(fullName.long_name, O_CREAT | O_TRUNC | O_WRONLY , 0600)) < 0)
510         {
511             ERR("Failed to create spool file %s (%s)\n", 
512                 fullName.long_name, strerror(errno));
513         }
514     }
515     return fd;
516 }
517
518 static int FreePrintJob(HANDLE16 hJob)
519 {
520     int nRet = SP_ERROR;
521     PPRINTJOB pPrintJob;
522
523     pPrintJob = FindPrintJobFromHandle(hJob);
524     if (pPrintJob != NULL)
525     {
526         gPrintJobsTable[pPrintJob->nIndex] = NULL;
527         HeapFree(GetProcessHeap(), 0, pPrintJob->pszOutput);
528         HeapFree(GetProcessHeap(), 0, pPrintJob->pszTitle);
529         if (pPrintJob->fd >= 0) close(pPrintJob->fd);
530         HeapFree(GetProcessHeap(), 0, pPrintJob);
531         nRet = SP_OK;
532     }
533     return nRet;
534 }
535
536 /**********************************************************************
537  *           OpenJob   (GDI.240)
538  *
539  */
540 HPJOB16 WINAPI OpenJob16(LPCSTR lpOutput, LPCSTR lpTitle, HDC16 hDC)
541 {
542     HPJOB16 hHandle = (HPJOB16)SP_ERROR;
543     PPRINTJOB pPrintJob;
544
545     TRACE("'%s' '%s' %04x\n", lpOutput, lpTitle, hDC);
546
547     pPrintJob = gPrintJobsTable[0];
548     if (pPrintJob == NULL)
549     {
550         int fd;
551
552         /* Try an create a spool file */
553         fd = CreateSpoolFile(lpOutput);
554         if (fd >= 0)
555         {
556             pPrintJob = HeapAlloc(GetProcessHeap(), 0, sizeof(PRINTJOB));
557             if(pPrintJob == NULL) {
558                 WARN("Memory exausted!\n");
559                 return hHandle;
560             }
561             
562             hHandle = 1;
563
564             pPrintJob->pszOutput = HEAP_strdupA(GetProcessHeap(), 0, lpOutput);
565             if(lpTitle)
566                 pPrintJob->pszTitle = HEAP_strdupA(GetProcessHeap(), 0, lpTitle);
567             pPrintJob->hDC = hDC;
568             pPrintJob->fd = fd;
569             pPrintJob->nIndex = 0;
570             pPrintJob->hHandle = hHandle; 
571             gPrintJobsTable[pPrintJob->nIndex] = pPrintJob; 
572         }
573     }
574     TRACE("return %04x\n", hHandle);
575     return hHandle;
576 }
577
578 /**********************************************************************
579  *           CloseJob   (GDI.243)
580  *
581  */
582 INT16 WINAPI CloseJob16(HPJOB16 hJob)
583 {
584     int nRet = SP_ERROR;
585     PPRINTJOB pPrintJob = NULL;
586
587     TRACE("%04x\n", hJob);
588
589     pPrintJob = FindPrintJobFromHandle(hJob);
590     if (pPrintJob != NULL)
591     {
592         /* Close the spool file */
593         close(pPrintJob->fd);
594         FreePrintJob(hJob);
595         nRet  = 1;
596     }
597     return nRet;
598 }
599
600 /**********************************************************************
601  *           WriteSpool   (GDI.241)
602  *
603  */
604 INT16 WINAPI WriteSpool16(HPJOB16 hJob, LPSTR lpData, INT16 cch)
605 {
606     int nRet = SP_ERROR;
607     PPRINTJOB pPrintJob = NULL;
608
609     TRACE("%04x %08lx %04x\n", hJob, (DWORD)lpData, cch);
610
611     pPrintJob = FindPrintJobFromHandle(hJob);
612     if (pPrintJob != NULL && pPrintJob->fd >= 0 && cch)
613     {
614         if (write(pPrintJob->fd, lpData, cch) != cch)
615           nRet = SP_OUTOFDISK;
616         else
617           nRet = cch;
618 #if 0
619         /* FIXME: We just cannot call 16 bit functions from here, since we
620          * have acquired several locks (DC). And we do not really need to.
621          */
622         if (pPrintJob->hDC == 0) {
623             TRACE("hDC == 0 so no QueryAbort\n");
624         }
625         else if (!(QueryAbort16(pPrintJob->hDC, (nRet == SP_OUTOFDISK) ? nRet : 0 )))
626         {
627             CloseJob16(hJob); /* printing aborted */
628             nRet = SP_APPABORT;
629         }
630 #endif
631     }
632     return nRet;
633 }
634
635 typedef INT WINAPI (*MSGBOX_PROC)( HWND, LPCSTR, LPCSTR, UINT );
636
637 /**********************************************************************
638  *           WriteDialog   (GDI.242)
639  *
640  */
641 INT16 WINAPI WriteDialog16(HPJOB16 hJob, LPSTR lpMsg, INT16 cchMsg)
642 {
643     HMODULE mod;
644     MSGBOX_PROC pMessageBoxA;
645     INT16 ret = 0;
646
647     TRACE("%04x %04x '%s'\n", hJob,  cchMsg, lpMsg);
648
649     if ((mod = GetModuleHandleA("user32.dll")))
650     {
651         if ((pMessageBoxA = (MSGBOX_PROC)GetProcAddress( mod, "MessageBoxA" )))
652             ret = pMessageBoxA(0, lpMsg, "Printing Error", MB_OKCANCEL);
653     }
654     return ret;
655 }
656
657
658 /**********************************************************************
659  *           DeleteJob  (GDI.244)
660  *
661  */
662 INT16 WINAPI DeleteJob16(HPJOB16 hJob, INT16 nNotUsed)
663 {
664     int nRet;
665
666     TRACE("%04x\n", hJob);
667
668     nRet = FreePrintJob(hJob);
669     return nRet;
670 }
671
672 /* 
673  * The following two function would allow a page to be sent to the printer
674  * when it has been processed.  For simplicity they havn't been implemented.
675  * This means a whole job has to be processed before it is sent to the printer.
676  */
677
678 /**********************************************************************
679  *           StartSpoolPage   (GDI.246)
680  *
681  */
682 INT16 WINAPI StartSpoolPage16(HPJOB16 hJob)
683 {
684     FIXME("StartSpoolPage GDI.246 unimplemented\n");
685     return 1;
686
687 }
688
689
690 /**********************************************************************
691  *           EndSpoolPage   (GDI.247)
692  *
693  */
694 INT16 WINAPI EndSpoolPage16(HPJOB16 hJob)
695 {
696     FIXME("EndSpoolPage GDI.247 unimplemented\n");
697     return 1;
698 }
699
700
701 /**********************************************************************
702  *           GetSpoolJob   (GDI.245)
703  *
704  */
705 DWORD WINAPI GetSpoolJob16(int nOption, LONG param)
706 {
707     DWORD retval = 0;
708     TRACE("In GetSpoolJob param 0x%lx noption %d\n",param, nOption);
709     return retval;
710 }
711
712
713 /******************************************************************
714  *                  DrvGetPrinterDataInternal
715  *
716  * Helper for DrvGetPrinterData
717  */
718 static DWORD DrvGetPrinterDataInternal(LPSTR RegStr_Printer,
719 LPBYTE lpPrinterData, int cbData, int what)
720 {
721     DWORD res = -1;
722     HKEY hkey;
723     DWORD dwType, cbQueryData;
724
725     if (!(RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey))) {
726         if (what == INT_PD_DEFAULT_DEVMODE) { /* "Default DevMode" */
727             if (!(RegQueryValueExA(hkey, DefaultDevMode, 0, &dwType, 0, &cbQueryData))) {
728                 if (!lpPrinterData)
729                     res = cbQueryData;
730                 else if ((cbQueryData) && (cbQueryData <= cbData)) {
731                     cbQueryData = cbData;
732                     if (RegQueryValueExA(hkey, DefaultDevMode, 0,
733                                 &dwType, lpPrinterData, &cbQueryData))
734                         res = cbQueryData;
735                 }
736             }
737         } else { /* "Printer Driver" */
738             cbQueryData = 32;
739             RegQueryValueExA(hkey, "Printer Driver", 0,
740                         &dwType, lpPrinterData, &cbQueryData);
741             res = cbQueryData;
742         }
743     }
744     if (hkey) RegCloseKey(hkey);
745     return res;
746 }
747
748 /******************************************************************
749  *                DrvGetPrinterData     [GDI.282]
750  *
751  */
752 DWORD WINAPI DrvGetPrinterData16(LPSTR lpPrinter, LPSTR lpProfile,
753                                LPDWORD lpType, LPBYTE lpPrinterData,
754                                int cbData, LPDWORD lpNeeded)
755 {
756     LPSTR RegStr_Printer;
757     HKEY hkey = 0, hkey2 = 0;
758     DWORD res = 0;
759     DWORD dwType, PrinterAttr, cbPrinterAttr, SetData, size;
760
761     if (HIWORD(lpPrinter))
762             TRACE("printer %s\n",lpPrinter);
763     else
764             TRACE("printer %p\n",lpPrinter);
765     if (HIWORD(lpProfile))
766             TRACE("profile %s\n",lpProfile);
767     else
768             TRACE("profile %p\n",lpProfile);
769     TRACE("lpType %p\n",lpType);
770
771     if ((!lpPrinter) || (!lpProfile) || (!lpNeeded))
772         return ERROR_INVALID_PARAMETER;
773
774     RegStr_Printer = HeapAlloc(GetProcessHeap(), 0,
775                                strlen(Printers) + strlen(lpPrinter) + 2);
776     strcpy(RegStr_Printer, Printers);
777     strcat(RegStr_Printer, lpPrinter);
778
779     if (((DWORD)lpProfile == INT_PD_DEFAULT_DEVMODE) || (HIWORD(lpProfile) &&
780     (!strcmp(lpProfile, DefaultDevMode)))) {
781         size = DrvGetPrinterDataInternal(RegStr_Printer, lpPrinterData, cbData,
782                                          INT_PD_DEFAULT_DEVMODE);
783         if (size+1) {
784             *lpNeeded = size;
785             if ((lpPrinterData) && (*lpNeeded > cbData))
786                 res = ERROR_MORE_DATA;
787         }
788         else res = ERROR_INVALID_PRINTER_NAME;
789     }
790     else
791     if (((DWORD)lpProfile == INT_PD_DEFAULT_MODEL) || (HIWORD(lpProfile) &&
792     (!strcmp(lpProfile, PrinterModel)))) {
793         *lpNeeded = 32;
794         if (!lpPrinterData) goto failed;
795         if (cbData < 32) {
796             res = ERROR_MORE_DATA;
797             goto failed;
798         }
799         size = DrvGetPrinterDataInternal(RegStr_Printer, lpPrinterData, cbData,
800                                          INT_PD_DEFAULT_MODEL);
801         if ((size+1) && (lpType))
802             *lpType = REG_SZ;
803         else
804             res = ERROR_INVALID_PRINTER_NAME;
805     }
806     else
807     {
808         if ((res = RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey)))
809             goto failed;
810         cbPrinterAttr = 4;
811         if ((res = RegQueryValueExA(hkey, "Attributes", 0,
812                         &dwType, (LPBYTE)&PrinterAttr, &cbPrinterAttr)))
813             goto failed;
814         if ((res = RegOpenKeyA(hkey, PrinterDriverData, &hkey2)))
815             goto failed;
816         *lpNeeded = cbData;
817         res = RegQueryValueExA(hkey2, lpProfile, 0,
818                 lpType, lpPrinterData, lpNeeded);
819         if ((res != ERROR_CANTREAD) &&
820          ((PrinterAttr &
821         (PRINTER_ATTRIBUTE_ENABLE_BIDI|PRINTER_ATTRIBUTE_NETWORK))
822         == PRINTER_ATTRIBUTE_NETWORK))
823         {
824             if (!(res) && (*lpType == REG_DWORD) && (*(LPDWORD)lpPrinterData == -1))
825                 res = ERROR_INVALID_DATA;
826         }
827         else
828         {
829             SetData = -1;
830             RegSetValueExA(hkey2, lpProfile, 0, REG_DWORD, (LPBYTE)&SetData, 4); /* no result returned */
831         }
832     }
833         
834 failed:
835     if (hkey2) RegCloseKey(hkey2);
836     if (hkey) RegCloseKey(hkey);
837     HeapFree(GetProcessHeap(), 0, RegStr_Printer);
838     return res;
839 }
840
841
842 /******************************************************************
843  *                 DrvSetPrinterData     [GDI.281]
844  *
845  */
846 DWORD WINAPI DrvSetPrinterData16(LPSTR lpPrinter, LPSTR lpProfile,
847                                DWORD lpType, LPBYTE lpPrinterData,
848                                DWORD dwSize)
849 {
850     LPSTR RegStr_Printer;
851     HKEY hkey = 0;
852     DWORD res = 0;
853
854     if (HIWORD(lpPrinter))
855             TRACE("printer %s\n",lpPrinter);
856     else
857             TRACE("printer %p\n",lpPrinter);
858     if (HIWORD(lpProfile))
859             TRACE("profile %s\n",lpProfile);
860     else
861             TRACE("profile %p\n",lpProfile);
862     TRACE("lpType %08lx\n",lpType);
863
864     if ((!lpPrinter) || (!lpProfile) ||
865     ((DWORD)lpProfile == INT_PD_DEFAULT_MODEL) || (HIWORD(lpProfile) &&
866     (!strcmp(lpProfile, PrinterModel))))
867         return ERROR_INVALID_PARAMETER;
868
869     RegStr_Printer = HeapAlloc(GetProcessHeap(), 0,
870                         strlen(Printers) + strlen(lpPrinter) + 2);
871     strcpy(RegStr_Printer, Printers);
872     strcat(RegStr_Printer, lpPrinter);
873
874     if (((DWORD)lpProfile == INT_PD_DEFAULT_DEVMODE) || (HIWORD(lpProfile) &&
875     (!strcmp(lpProfile, DefaultDevMode)))) {
876         if ( RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey) 
877              != ERROR_SUCCESS ||
878              RegSetValueExA(hkey, DefaultDevMode, 0, REG_BINARY, 
879                               lpPrinterData, dwSize) != ERROR_SUCCESS )
880                 res = ERROR_INVALID_PRINTER_NAME;
881     }
882     else
883     {
884         strcat(RegStr_Printer, "\\");
885
886         if( (res = RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey)) ==
887             ERROR_SUCCESS ) {
888
889             if (!lpPrinterData) 
890                 res = RegDeleteValueA(hkey, lpProfile);
891             else
892                 res = RegSetValueExA(hkey, lpProfile, 0, lpType,
893                                        lpPrinterData, dwSize);
894         }
895     }
896
897     if (hkey) RegCloseKey(hkey);
898     HeapFree(GetProcessHeap(), 0, RegStr_Printer);
899     return res;
900 }