Remove string.c because all of its functions were moved to crtdll.c
[wine] / misc / 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 <stdlib.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <errno.h>
14 #include <unistd.h>
15 #include <fcntl.h>
16 #include "ldt.h"
17 #include "winbase.h"
18 #include "wine/wingdi16.h"
19 #include "winspool.h"
20 #include "winerror.h"
21 #include "winreg.h"
22 #include "debugtools.h"
23 #include "gdi.h"
24 #include "dc.h"
25 #include "callback.h"
26 #include "xmalloc.h"
27 #include "options.h"
28
29 DEFAULT_DEBUG_CHANNEL(print)
30
31 static char PrinterModel[]      = "Printer Model";
32 static char DefaultDevMode[]    = "Default DevMode";
33 static char PrinterDriverData[] = "PrinterDriverData";
34 static char Printers[]          = "System\\CurrentControlSet\\Control\\Print\\Printers\\";
35
36 /******************************************************************
37  *                  StartDoc16  [GDI.377]
38  *
39  */
40 INT16 WINAPI StartDoc16( HDC16 hdc, const DOCINFO16 *lpdoc )
41 {
42   INT16 retVal;
43   TRACE("(%p)\n", lpdoc );
44   TRACE("%d 0x%lx:0x%p 0x%lx:0x%p\n",lpdoc->cbSize,
45         lpdoc->lpszDocName,PTR_SEG_TO_LIN(lpdoc->lpszDocName),
46         lpdoc->lpszOutput,PTR_SEG_TO_LIN(lpdoc->lpszOutput));
47   TRACE("%d %s %s\n",lpdoc->cbSize,
48         (LPSTR)PTR_SEG_TO_LIN(lpdoc->lpszDocName),
49         (LPSTR)PTR_SEG_TO_LIN(lpdoc->lpszOutput));
50   retVal =  Escape16(hdc, STARTDOC,
51     strlen((LPSTR)PTR_SEG_TO_LIN(lpdoc->lpszDocName)), lpdoc->lpszDocName, 0);
52   TRACE("Escape16 returned %d\n",retVal);
53   return retVal;
54 }
55
56 /******************************************************************
57  *                  EndPage16  [GDI.380]
58  *
59  */
60 INT16 WINAPI EndPage16( HDC16 hdc )
61 {
62   INT16 retVal;
63   retVal =  Escape16(hdc, NEWFRAME, 0, 0, 0);
64   TRACE("Escape16 returned %d\n",retVal);
65   return retVal;
66 }
67
68 /******************************************************************
69  *                  StartDoc32A  [GDI32.347]
70  *
71  */
72 INT WINAPI StartDocA(HDC hdc ,const DOCINFOA* doc)
73 {
74     return  Escape(hdc,
75                    STARTDOC,
76                    strlen(doc->lpszDocName),
77                    doc->lpszDocName,
78                    0);
79 }
80
81 /*************************************************************************
82  *                  StartDoc32W [GDI32.348]
83  * 
84  */
85 INT WINAPI StartDocW(HDC hdc, const DOCINFOW* doc) {
86   FIXME("stub\n");
87   SetLastError(ERROR_CALL_NOT_IMPLEMENTED); 
88   return 0; /* failure*/
89 }
90
91 /******************************************************************
92  *                  StartPage32  [GDI32.349]
93  *
94  */
95 INT WINAPI StartPage(HDC hdc)
96 {
97   FIXME("stub\n");
98   return 1;
99 }
100
101 /******************************************************************
102  *                  EndPage32  [GDI32.77]
103  *
104  */
105 INT WINAPI EndPage(HDC hdc)
106 {
107   return Escape(hdc, NEWFRAME, 0, 0, 0);
108 }
109
110 /******************************************************************
111  *                  EndDoc16  [GDI.378]
112  *
113  */
114 INT16 WINAPI EndDoc16(HDC16 hdc)
115 {
116   return  Escape16(hdc, ENDDOC, 0, 0, 0);
117 }
118
119 /******************************************************************
120  *                  EndDoc32  [GDI32.76]
121  *
122  */
123 INT WINAPI EndDoc(HDC hdc)
124 {
125   return Escape(hdc, ENDDOC, 0, 0, 0);
126 }
127
128 /******************************************************************************
129  *                 AbortDoc16  [GDI.382]
130  */
131 INT16 WINAPI AbortDoc16(HDC16 hdc)
132 {
133   return Escape16(hdc, ABORTDOC, 0, 0, 0);
134 }
135
136 /******************************************************************************
137  *                 AbortDoc32  [GDI32.0]
138  */
139 INT WINAPI AbortDoc(HDC hdc)
140 {
141     FIXME("(%d): stub\n", hdc);
142     return 1;
143 }
144
145 /**********************************************************************
146  *           QueryAbort   (GDI.155)
147  *
148  *  Calls the app's AbortProc function if avail.
149  *
150  * RETURNS
151  * TRUE if no AbortProc avail or AbortProc wants to continue printing.
152  * FALSE if AbortProc wants to abort printing.
153  */
154 BOOL16 WINAPI QueryAbort16(HDC16 hdc, INT16 reserved)
155 {
156     DC *dc = DC_GetDCPtr( hdc );
157
158     if ((!dc) || (!dc->w.lpfnPrint))
159         return TRUE;
160     return Callbacks->CallDrvAbortProc(dc->w.lpfnPrint, hdc, 0);
161 }
162
163 /**********************************************************************
164  *           SetAbortProc16   (GDI.381)
165  *
166  */
167 INT16 WINAPI SetAbortProc16(HDC16 hdc, SEGPTR abrtprc)
168 {
169     return Escape16(hdc, SETABORTPROC, 0, abrtprc, (SEGPTR)0);
170
171
172 /**********************************************************************
173  *           SetAbortProc32   (GDI32.301)
174  *
175  */
176 INT WINAPI SetAbortProc(HDC hdc, ABORTPROC abrtprc)
177 {
178     FIXME("stub\n");
179     return 1;
180 }
181
182
183 /****************** misc. printer related functions */
184
185 /*
186  * The following function should implement a queing system
187  */
188 #ifndef HPQ 
189 #define HPQ WORD
190 #endif
191 struct hpq 
192 {
193     struct hpq  *next;
194     int          tag;
195     int          key;
196 };
197
198 static struct hpq *hpqueue;
199
200 /**********************************************************************
201  *           CreatePQ   (GDI.230)
202  *
203  */
204 HPQ WINAPI CreatePQ16(int size) 
205 {
206 #if 0
207     HGLOBAL16 hpq = 0;
208     WORD tmp_size;
209     LPWORD pPQ;
210
211     tmp_size = size << 2;
212     if (!(hpq = GlobalAlloc16(GMEM_SHARE|GMEM_MOVEABLE, tmp_size + 8)))
213        return 0xffff;
214     pPQ = GlobalLock16(hpq);
215     *pPQ++ = 0;
216     *pPQ++ = tmp_size;
217     *pPQ++ = 0;
218     *pPQ++ = 0;
219     GlobalUnlock16(hpq);
220
221     return (HPQ)hpq;
222 #else
223     FIXME("(%d): stub\n",size);
224     return 1;
225 #endif
226 }
227
228 /**********************************************************************
229  *           DeletePQ   (GDI.235)
230  *
231  */
232 int WINAPI DeletePQ16(HPQ hPQ) 
233 {
234     return GlobalFree16((HGLOBAL16)hPQ);
235 }
236
237 /**********************************************************************
238  *           ExtractPQ   (GDI.232)
239  *
240  */
241 int WINAPI ExtractPQ16(HPQ hPQ) 
242
243     struct hpq *queue, *prev, *current, *currentPrev;
244     int key = 0, tag = -1;
245     currentPrev = prev = NULL;
246     queue = current = hpqueue;
247     if (current)
248         key = current->key;
249     
250     while (current)
251     {
252         currentPrev = current;
253         current = current->next;
254         if (current)
255         {
256             if (current->key < key)
257             {
258                 queue = current;
259                 prev = currentPrev;
260             }
261         }
262     }
263     if (queue)
264     {
265         tag = queue->tag;
266         
267         if (prev)
268             prev->next = queue->next;
269         else
270             hpqueue = queue->next;
271         free(queue);
272     }
273     
274     TRACE("%x got tag %d key %d\n", hPQ, tag, key); 
275
276     return tag;
277 }
278
279 /**********************************************************************
280  *           InsertPQ   (GDI.233)
281  *
282  */
283 int WINAPI InsertPQ16(HPQ hPQ, int tag, int key) 
284 {
285     struct hpq *queueItem = xmalloc(sizeof(struct hpq));
286     queueItem->next = hpqueue;
287     hpqueue = queueItem;
288     queueItem->key = key;
289     queueItem->tag = tag;
290     
291     FIXME("(%x %d %d): stub???\n", hPQ, tag, key);
292     return TRUE;
293 }
294
295 /**********************************************************************
296  *           MinPQ   (GDI.231)
297  *
298  */
299 int WINAPI MinPQ16(HPQ hPQ) 
300 {
301     FIXME("(%x): stub\n", hPQ); 
302     return 0;
303 }
304
305 /**********************************************************************
306  *           SizePQ   (GDI.234)
307  *
308  */
309 int WINAPI SizePQ16(HPQ hPQ, int sizechange) 
310 {  
311     FIXME("(%x %d): stub\n", hPQ, sizechange); 
312     return -1; 
313 }
314
315
316
317 /* 
318  * The following functions implement part of the spooling process to 
319  * print manager.  I would like to see wine have a version of print managers
320  * that used LPR/LPD.  For simplicity print jobs will be sent to a file for
321  * now.
322  */
323 typedef struct PRINTJOB
324 {
325     char        *pszOutput;
326     char        *pszTitle;
327     HDC16       hDC;
328     HANDLE16    hHandle;
329     int         nIndex;
330     int         fd;
331 } PRINTJOB, *PPRINTJOB;
332
333 #define MAX_PRINT_JOBS 1
334 #define SP_OK 1
335
336 PPRINTJOB gPrintJobsTable[MAX_PRINT_JOBS];
337
338
339 static PPRINTJOB FindPrintJobFromHandle(HANDLE16 hHandle)
340 {
341     return gPrintJobsTable[0];
342 }
343
344 /* TTD Need to do some DOS->UNIX file conversion here */
345 static int CreateSpoolFile(LPSTR pszOutput)
346 {
347     int fd=-1;
348     char psCmd[1024];
349     char *psCmdP = psCmd;
350
351     /* TTD convert the 'output device' into a spool file name */
352
353     if (pszOutput == NULL || *pszOutput == '\0')
354       return -1;
355
356     PROFILE_GetWineIniString( "spooler", pszOutput, "", psCmd, sizeof(psCmd) );
357     TRACE("Got printerSpoolCommand '%s' for output device '%s'\n",
358           psCmd, pszOutput);
359     if (!*psCmd)
360         psCmdP = pszOutput;
361     else
362     {
363         while (*psCmdP && isspace(*psCmdP))
364         {
365             psCmdP++;
366         };
367         if (!*psCmdP)
368             return -1;
369     }
370     if (*psCmdP == '|')
371     {
372         int fds[2];
373         if (pipe(fds))
374             return -1;
375         if (fork() == 0)
376         {
377             psCmdP++;
378
379             TRACE("In child need to exec %s\n",psCmdP);
380             close(0);
381             dup2(fds[0],0);
382             close (fds[1]);
383             system(psCmdP);
384             exit(0);
385             
386         }
387         close (fds[0]);
388         fd = fds[1];
389         TRACE("Need to execute a cmnd and pipe the output to it\n");
390     }
391     else
392     {
393         TRACE("Just assume its a file\n");
394
395         if ((fd = open(psCmdP, O_CREAT | O_TRUNC | O_WRONLY , 0600)) < 0)
396         {
397             ERR("Failed to create spool file %s, errno = %d\n", 
398                 psCmdP, errno);
399         }
400     }
401     return fd;
402 }
403
404 static int FreePrintJob(HANDLE16 hJob)
405 {
406     int nRet = SP_ERROR;
407     PPRINTJOB pPrintJob;
408
409     pPrintJob = FindPrintJobFromHandle(hJob);
410     if (pPrintJob != NULL)
411     {
412         gPrintJobsTable[pPrintJob->nIndex] = NULL;
413         free(pPrintJob->pszOutput);
414         free(pPrintJob->pszTitle);
415         if (pPrintJob->fd >= 0) close(pPrintJob->fd);
416         free(pPrintJob);
417         nRet = SP_OK;
418     }
419     return nRet;
420 }
421
422 /**********************************************************************
423  *           OpenJob   (GDI.240)
424  *
425  */
426 HANDLE16 WINAPI OpenJob16(LPSTR lpOutput, LPSTR lpTitle, HDC16 hDC)
427 {
428     HANDLE16 hHandle = (HANDLE16)SP_ERROR;
429     PPRINTJOB pPrintJob;
430
431     TRACE("'%s' '%s' %04x\n", lpOutput, lpTitle, hDC);
432
433     pPrintJob = gPrintJobsTable[0];
434     if (pPrintJob == NULL)
435     {
436         int fd;
437
438         /* Try an create a spool file */
439         fd = CreateSpoolFile(lpOutput);
440         if (fd >= 0)
441         {
442             hHandle = 1;
443
444             pPrintJob = xmalloc(sizeof(PRINTJOB));
445             memset(pPrintJob, 0, sizeof(PRINTJOB));
446
447             pPrintJob->pszOutput = strdup(lpOutput);
448             if(lpTitle)
449                 pPrintJob->pszTitle = strdup(lpTitle);
450             pPrintJob->hDC = hDC;
451             pPrintJob->fd = fd;
452             pPrintJob->nIndex = 0;
453             pPrintJob->hHandle = hHandle; 
454             gPrintJobsTable[pPrintJob->nIndex] = pPrintJob; 
455         }
456     }
457     TRACE("return %04x\n", hHandle);
458     return hHandle;
459 }
460
461 /**********************************************************************
462  *           CloseJob   (GDI.243)
463  *
464  */
465 int WINAPI CloseJob16(HANDLE16 hJob)
466 {
467     int nRet = SP_ERROR;
468     PPRINTJOB pPrintJob = NULL;
469
470     TRACE("%04x\n", hJob);
471
472     pPrintJob = FindPrintJobFromHandle(hJob);
473     if (pPrintJob != NULL)
474     {
475         /* Close the spool file */
476         close(pPrintJob->fd);
477         FreePrintJob(hJob);
478         nRet  = 1;
479     }
480     return nRet;
481 }
482
483 /**********************************************************************
484  *           WriteSpool   (GDI.241)
485  *
486  */
487 int WINAPI WriteSpool16(HANDLE16 hJob, LPSTR lpData, WORD cch)
488 {
489     int nRet = SP_ERROR;
490     PPRINTJOB pPrintJob = NULL;
491
492     TRACE("%04x %08lx %04x\n", hJob, (DWORD)lpData, cch);
493
494     pPrintJob = FindPrintJobFromHandle(hJob);
495     if (pPrintJob != NULL && pPrintJob->fd >= 0 && cch)
496     {
497         if (write(pPrintJob->fd, lpData, cch) != cch)
498           nRet = SP_OUTOFDISK;
499         else
500           nRet = cch;
501         if (pPrintJob->hDC == 0) {
502             TRACE("hDC == 0 so no QueryAbort\n");
503         }
504         else if (!(QueryAbort16(pPrintJob->hDC, (nRet == SP_OUTOFDISK) ? nRet : 0 )))
505         {
506             CloseJob16(hJob); /* printing aborted */
507             nRet = SP_APPABORT;
508         }
509     }
510     return nRet;
511 }
512
513 /**********************************************************************
514  *           WriteDialog   (GDI.242)
515  *
516  */
517 int WINAPI WriteDialog16(HANDLE16 hJob, LPSTR lpMsg, WORD cchMsg)
518 {
519     int nRet = 0;
520
521     TRACE("%04x %04x '%s'\n", hJob,  cchMsg, lpMsg);
522
523     nRet = MessageBox16(0, lpMsg, "Printing Error", MB_OKCANCEL);
524     return nRet;
525 }
526
527
528 /**********************************************************************
529  *           DeleteJob  (GDI.244)
530  *
531  */
532 int WINAPI DeleteJob16(HANDLE16 hJob, WORD wNotUsed)
533 {
534     int nRet;
535
536     TRACE("%04x\n", hJob);
537
538     nRet = FreePrintJob(hJob);
539     return nRet;
540 }
541
542 /* 
543  * The following two function would allow a page to be sent to the printer
544  * when it has been processed.  For simplicity they havn't been implemented.
545  * This means a whole job has to be processed before it is sent to the printer.
546  */
547
548 /**********************************************************************
549  *           StartSpoolPage   (GDI.246)
550  *
551  */
552 int WINAPI StartSpoolPage16(HANDLE16 hJob)
553 {
554     FIXME("StartSpoolPage GDI.246 unimplemented\n");
555     return 1;
556
557 }
558
559
560 /**********************************************************************
561  *           EndSpoolPage   (GDI.247)
562  *
563  */
564 int WINAPI EndSpoolPage16(HANDLE16 hJob)
565 {
566     FIXME("EndSpoolPage GDI.247 unimplemented\n");
567     return 1;
568 }
569
570
571 /**********************************************************************
572  *           GetSpoolJob   (GDI.245)
573  *
574  */
575 DWORD WINAPI GetSpoolJob16(int nOption, LONG param)
576 {
577     DWORD retval = 0;
578     TRACE("In GetSpoolJob param 0x%lx noption %d\n",param, nOption);
579     return retval;
580 }
581
582
583 /******************************************************************
584  *                  DrvGetPrinterDataInternal
585  *
586  * Helper for DrvGetPrinterData
587  */
588 static DWORD DrvGetPrinterDataInternal(LPSTR RegStr_Printer,
589 LPBYTE lpPrinterData, int cbData, int what)
590 {
591     DWORD res = -1;
592     HKEY hkey;
593     DWORD dwType, cbQueryData;
594
595     if (!(RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey))) {
596         if (what == INT_PD_DEFAULT_DEVMODE) { /* "Default DevMode" */
597             if (!(RegQueryValueExA(hkey, DefaultDevMode, 0, &dwType, 0, &cbQueryData))) {
598                 if (!lpPrinterData)
599                     res = cbQueryData;
600                 else if ((cbQueryData) && (cbQueryData <= cbData)) {
601                     cbQueryData = cbData;
602                     if (RegQueryValueExA(hkey, DefaultDevMode, 0,
603                                 &dwType, lpPrinterData, &cbQueryData))
604                         res = cbQueryData;
605                 }
606             }
607         } else { /* "Printer Driver" */
608             cbQueryData = 32;
609             RegQueryValueExA(hkey, "Printer Driver", 0,
610                         &dwType, lpPrinterData, &cbQueryData);
611             res = cbQueryData;
612         }
613     }
614     if (hkey) RegCloseKey(hkey);
615     return res;
616 }
617
618 /******************************************************************
619  *                DrvGetPrinterData     [GDI.282]
620  *
621  */
622 DWORD WINAPI DrvGetPrinterData16(LPSTR lpPrinter, LPSTR lpProfile,
623                                LPDWORD lpType, LPBYTE lpPrinterData,
624                                int cbData, LPDWORD lpNeeded)
625 {
626     LPSTR RegStr_Printer;
627     HKEY hkey = 0, hkey2 = 0;
628     DWORD res = 0;
629     DWORD dwType, PrinterAttr, cbPrinterAttr, SetData, size;
630
631     if (HIWORD(lpPrinter))
632             TRACE("printer %s\n",lpPrinter);
633     else
634             TRACE("printer %p\n",lpPrinter);
635     if (HIWORD(lpProfile))
636             TRACE("profile %s\n",lpProfile);
637     else
638             TRACE("profile %p\n",lpProfile);
639     TRACE("lpType %p\n",lpType);
640
641     if ((!lpPrinter) || (!lpProfile) || (!lpNeeded))
642         return ERROR_INVALID_PARAMETER;
643
644     RegStr_Printer = HeapAlloc(GetProcessHeap(), 0,
645                                strlen(Printers) + strlen(lpPrinter) + 2);
646     strcpy(RegStr_Printer, Printers);
647     strcat(RegStr_Printer, lpPrinter);
648
649     if (((DWORD)lpProfile == INT_PD_DEFAULT_DEVMODE) || (HIWORD(lpProfile) &&
650     (!strcmp(lpProfile, DefaultDevMode)))) {
651         size = DrvGetPrinterDataInternal(RegStr_Printer, lpPrinterData, cbData,
652                                          INT_PD_DEFAULT_DEVMODE);
653         if (size+1) {
654             *lpNeeded = size;
655             if ((lpPrinterData) && (*lpNeeded > cbData))
656                 res = ERROR_MORE_DATA;
657         }
658         else res = ERROR_INVALID_PRINTER_NAME;
659     }
660     else
661     if (((DWORD)lpProfile == INT_PD_DEFAULT_MODEL) || (HIWORD(lpProfile) &&
662     (!strcmp(lpProfile, PrinterModel)))) {
663         *lpNeeded = 32;
664         if (!lpPrinterData) goto failed;
665         if (cbData < 32) {
666             res = ERROR_MORE_DATA;
667             goto failed;
668         }
669         size = DrvGetPrinterDataInternal(RegStr_Printer, lpPrinterData, cbData,
670                                          INT_PD_DEFAULT_MODEL);
671         if ((size+1) && (lpType))
672             *lpType = REG_SZ;
673         else
674             res = ERROR_INVALID_PRINTER_NAME;
675     }
676     else
677     {
678         if ((res = RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey)))
679             goto failed;
680         cbPrinterAttr = 4;
681         if ((res = RegQueryValueExA(hkey, "Attributes", 0,
682                         &dwType, (LPBYTE)&PrinterAttr, &cbPrinterAttr)))
683             goto failed;
684         if ((res = RegOpenKeyA(hkey, PrinterDriverData, &hkey2)))
685             goto failed;
686         *lpNeeded = cbData;
687         res = RegQueryValueExA(hkey2, lpProfile, 0,
688                 lpType, lpPrinterData, lpNeeded);
689         if ((res != ERROR_CANTREAD) &&
690          ((PrinterAttr &
691         (PRINTER_ATTRIBUTE_ENABLE_BIDI|PRINTER_ATTRIBUTE_NETWORK))
692         == PRINTER_ATTRIBUTE_NETWORK))
693         {
694             if (!(res) && (*lpType == REG_DWORD) && (*(LPDWORD)lpPrinterData == -1))
695                 res = ERROR_INVALID_DATA;
696         }
697         else
698         {
699             SetData = -1;
700             RegSetValueExA(hkey2, lpProfile, 0, REG_DWORD, (LPBYTE)&SetData, 4); /* no result returned */
701         }
702     }
703         
704 failed:
705     if (hkey2) RegCloseKey(hkey2);
706     if (hkey) RegCloseKey(hkey);
707     HeapFree(GetProcessHeap(), 0, RegStr_Printer);
708     return res;
709 }
710
711
712 /******************************************************************
713  *                 DrvSetPrinterData     [GDI.281]
714  *
715  */
716 DWORD WINAPI DrvSetPrinterData16(LPSTR lpPrinter, LPSTR lpProfile,
717                                DWORD lpType, LPBYTE lpPrinterData,
718                                DWORD dwSize)
719 {
720     LPSTR RegStr_Printer;
721     HKEY hkey = 0;
722     DWORD res = 0;
723
724     if (HIWORD(lpPrinter))
725             TRACE("printer %s\n",lpPrinter);
726     else
727             TRACE("printer %p\n",lpPrinter);
728     if (HIWORD(lpProfile))
729             TRACE("profile %s\n",lpProfile);
730     else
731             TRACE("profile %p\n",lpProfile);
732     TRACE("lpType %08lx\n",lpType);
733
734     if ((!lpPrinter) || (!lpProfile) ||
735     ((DWORD)lpProfile == INT_PD_DEFAULT_MODEL) || (HIWORD(lpProfile) &&
736     (!strcmp(lpProfile, PrinterModel))))
737         return ERROR_INVALID_PARAMETER;
738
739     RegStr_Printer = HeapAlloc(GetProcessHeap(), 0,
740                         strlen(Printers) + strlen(lpPrinter) + 2);
741     strcpy(RegStr_Printer, Printers);
742     strcat(RegStr_Printer, lpPrinter);
743
744     if (((DWORD)lpProfile == INT_PD_DEFAULT_DEVMODE) || (HIWORD(lpProfile) &&
745     (!strcmp(lpProfile, DefaultDevMode)))) {
746         if ( RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey) 
747              != ERROR_SUCCESS ||
748              RegSetValueExA(hkey, DefaultDevMode, 0, REG_BINARY, 
749                               lpPrinterData, dwSize) != ERROR_SUCCESS )
750                 res = ERROR_INVALID_PRINTER_NAME;
751     }
752     else
753     {
754         strcat(RegStr_Printer, "\\");
755
756         if( (res = RegOpenKeyA(HKEY_LOCAL_MACHINE, RegStr_Printer, &hkey)) ==
757             ERROR_SUCCESS ) {
758
759             if (!lpPrinterData) 
760                 res = RegDeleteValueA(hkey, lpProfile);
761             else
762                 res = RegSetValueExA(hkey, lpProfile, 0, lpType,
763                                        lpPrinterData, dwSize);
764         }
765     }
766
767     if (hkey) RegCloseKey(hkey);
768     HeapFree(GetProcessHeap(), 0, RegStr_Printer);
769     return res;
770 }