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