2 * SetupAPI virtual copy operations
4 * Copyright 2001 Andreas Mohr
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * FIXME: we now rely on builtin setupapi.dll for dialog resources.
21 * This is bad ! We ought to have 16bit resource handling working.
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
38 /* copied from setupapi */
39 #define COPYFILEDLGORD 1000
40 #define SOURCESTRORD 500
41 #define DESTSTRORD 501
42 #define PROGRESSORD 502
44 #define REG_INSTALLEDFILES "System\\CurrentControlSet\\Control\\InstalledFiles"
45 #define REGPART_RENAME "\\Rename"
46 #define REG_VERSIONCONFLICT "Software\\Microsoft\\VersionConflictManager"
48 static FARPROC16 VCP_Proc = NULL;
49 static LPARAM VCP_MsgRef = 0;
51 static BOOL VCP_opened = FALSE;
53 static VCPSTATUS vcp_status;
55 static HMODULE SETUPAPI_hInstance;
57 static WORD VCP_Callback( LPVOID obj, UINT16 msg, WPARAM16 wParam, LPARAM lParam, LPARAM lParamRef )
63 args[7] = HIWORD(obj);
64 args[6] = LOWORD(obj);
67 args[3] = HIWORD(lParam);
68 args[2] = LOWORD(lParam);
69 args[1] = HIWORD(lParamRef);
70 args[0] = LOWORD(lParamRef);
71 WOWCallback16Ex( (DWORD)VCP_Proc, WCB16_PASCAL, sizeof(args), args, &ret );
76 /****************************** VHSTR management ******************************/
79 * This is a totally braindead implementation for now;
80 * I don't care about speed at all ! Size and implementation time
81 * is much more important IMHO. I could have created some sophisticated
82 * tree structure, but... what the hell ! :-)
89 static VHSTR_STRUCT **vhstrlist = NULL;
90 static VHSTR vhstr_alloc = 0;
92 #define VALID_VHSTR(x) ((x < vhstr_alloc) && (vhstrlist[x]) && (vhstrlist[x]->refcount))
94 /***********************************************************************
95 * vsmStringAdd (SETUPX.207)
97 VHSTR WINAPI vsmStringAdd16(LPCSTR lpszName)
100 VHSTR index = 0xffff;
104 TRACE("add string '%s'\n", lpszName);
105 /* search whether string already inserted */
106 TRACE("searching for existing string...\n");
107 for (n = 0; n < vhstr_alloc; n++)
109 if ((vhstrlist[n]) && (vhstrlist[n]->refcount))
111 TRACE("checking item: %d\n", n);
112 if (!strcmp(vhstrlist[n]->pStr, lpszName))
115 vhstrlist[n]->refcount++;
121 /* hmm, not found yet, let's insert it */
122 TRACE("inserting item\n");
123 for (n = 0; n < vhstr_alloc; n++)
125 if ((!(vhstrlist[n])) || (!(vhstrlist[n]->refcount)))
131 heap = GetProcessHeap();
132 if (n == vhstr_alloc) /* hmm, no free index found yet */
138 vhstrlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, vhstrlist,
139 sizeof(VHSTR_STRUCT *) * vhstr_alloc);
141 vhstrlist = HeapAlloc(heap, HEAP_ZERO_MEMORY,
142 sizeof(VHSTR_STRUCT *) * vhstr_alloc);
145 return 0xffff; /* failure */
146 if (!vhstrlist[index])
147 vhstrlist[index] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VHSTR_STRUCT));
148 vhstrlist[index]->refcount = 1;
149 str = HeapAlloc(heap, 0, strlen(lpszName)+1);
150 strcpy(str, lpszName);
151 vhstrlist[index]->pStr = str;
155 /***********************************************************************
156 * vsmStringDelete (SETUPX.206)
158 INT16 WINAPI vsmStringDelete16(VHSTR vhstr)
160 if (VALID_VHSTR(vhstr))
162 vhstrlist[vhstr]->refcount--;
163 if (!vhstrlist[vhstr]->refcount)
165 HeapFree(GetProcessHeap(), 0, (LPSTR)vhstrlist[vhstr]->pStr);
166 vhstrlist[vhstr]->pStr = NULL;
171 /* string not found */
175 /***********************************************************************
176 * vsmGetStringName (SETUPX.205)
178 * Pretty correct, I guess
180 INT16 WINAPI vsmGetStringName16(VHSTR vhstr, LPSTR lpszBuffer, int cbBuffer)
182 if (VALID_VHSTR(vhstr))
184 int len = strlen(vhstrlist[vhstr]->pStr)+1;
188 strcpy(lpszBuffer, vhstrlist[vhstr]->pStr);
195 /***********************************************************************
196 * vsmGetStringRawName (SETUPX.208)
198 LPCSTR WINAPI vsmGetStringRawName16(VHSTR vhstr)
200 return (VALID_VHSTR(vhstr)) ? vhstrlist[vhstr]->pStr : NULL;
204 /***************************** VIRTNODE management ****************************/
205 static LPVIRTNODE *pvnlist = NULL;
206 static DWORD vn_num = 0;
207 static DWORD vn_last = 0;
209 static RETERR16 VCP_VirtnodeCreate(const VCPFILESPEC *vfsSrc, const VCPFILESPEC *vfsDst,
210 WORD fl, LPARAM lParam, LPEXPANDVTBL lpExpandVtbl)
215 while (vn_last < vn_num)
217 if (pvnlist[vn_last] == NULL)
221 heap = GetProcessHeap();
222 if (vn_last == vn_num)
226 pvnlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, pvnlist,
227 sizeof(*pvnlist) * vn_num);
229 pvnlist = HeapAlloc(heap, HEAP_ZERO_MEMORY,
230 sizeof(*pvnlist) * vn_num);
232 pvnlist[vn_last] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VIRTNODE));
233 lpvn = pvnlist[vn_last];
236 lpvn->cbSize = sizeof(VIRTNODE);
239 lpvn->vfsSrc = *vfsSrc;
242 lpvn->vfsDst = *vfsDst;
245 lpvn->lParam = lParam;
246 lpvn->lpExpandVtbl = lpExpandVtbl;
248 lpvn->vhstrDstFinalName = 0xffff; /* FIXME: what is this ? */
250 VCP_Callback(lpvn, VCPM_NODECREATE, 0, 0, VCP_MsgRef);
251 lpvn->fl |= VFNL_CREATED;
252 VCP_Callback(lpvn, VCPM_NODEACCEPT, 0, 0, VCP_MsgRef);
257 /***********************************************************************
258 * VcpOpen (SETUPX.200)
260 * Sets up a virtual copy operation.
261 * This means that functions such as GenInstall()
262 * create a VIRTNODE struct for every file to be touched in a .INF file
263 * instead of actually touching the file.
264 * The actual copy/move/rename gets started when VcpClose or
265 * VcpFlush is called; several different callbacks are made
266 * (copy, rename, open, close, version conflicts, ...) on every file copied.
268 RETERR16 WINAPI VcpOpen16(VIFPROC vifproc, LPARAM lparamMsgRef)
270 TRACE("(%p, %08lx)\n", vifproc, lparamMsgRef);
274 VCP_Proc = (FARPROC16)vifproc;
275 VCP_MsgRef = lparamMsgRef;
281 /***********************************************************************
282 * VcpQueueCopy [SETUPX.13]
284 * lpExpandVtbl seems to be deprecated.
285 * fl are the CNFL_xxx and VNFL_xxx flags.
286 * lParam are the VNLP_xxx flags.
288 RETERR16 WINAPI VcpQueueCopy16(
289 LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName,
290 LPCSTR lpszSrcDir, LPCSTR lpszDstDir,
291 LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst,
292 LPEXPANDVTBL lpExpandVtbl,
293 WORD fl, LPARAM lParam
296 VCPFILESPEC vfsSrc, vfsDst;
299 return ERR_VCP_NOTOPEN;
301 TRACE("srcdir: %s, srcfile: %s, dstdir: %s, dstfile: %s\n",
302 lpszSrcDir, lpszSrcFileName, lpszDstDir, lpszDstFileName);
304 TRACE("ldidSrc == %d, ldidDst == %d\n", ldidSrc, ldidDst);
306 vfsSrc.ldid = ldidSrc;
307 vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir);
308 vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName);
310 vfsDst.ldid = ldidDst;
311 vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
312 vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
314 return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, fl, lParam,
318 /***********************************************************************
319 * VcpQueueDelete [SETUPX.17]
321 * Is lParamRef the same as lParam in VcpQueueCopy ?
322 * Damn docu !! Err... which docu ?
324 RETERR16 WINAPI VcpQueueDelete16(
325 LPCSTR lpszDstFileName,
334 return ERR_VCP_NOTOPEN;
336 vfsDst.ldid = ldidDst;
337 vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
338 vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
340 return VCP_VirtnodeCreate(NULL, &vfsDst, VNFL_DELETE, lParamRef, 0);
343 /***********************************************************************
344 * VcpQueueRename [SETUPX.204]
347 RETERR16 WINAPI VcpQueueRename16(
348 LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName,
349 LPCSTR lpszSrcDir, LPCSTR lpszDstDir,
350 LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst,
354 VCPFILESPEC vfsSrc, vfsDst;
357 return ERR_VCP_NOTOPEN;
359 vfsSrc.ldid = ldidSrc;
360 vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir);
361 vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName);
363 vfsDst.ldid = ldidDst;
364 vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
365 vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
367 return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, VNFL_RENAME, lParam,
371 /***********************************************************************
372 * VcpEnumFiles (SETUPX.@)
374 INT16 WINAPI VcpEnumFiles(VCPENUMPROC vep, LPARAM lParamRef)
378 for (n = 0; n < vn_last; n++)
379 vep(pvnlist[n], lParamRef);
381 return 0; /* FIXME: return value ? */
384 /***********************************************************************
385 * VcpExplain (SETUPX.411)
387 LPCSTR WINAPI VcpExplain16(LPVIRTNODE lpVn, DWORD dwWhat)
389 static char buffer[MAX_PATH]; /* FIXME: is this how it's done ? */
396 LPVCPFILESPEC lpvfs =
397 (dwWhat == VCPEX_SRC_FULL) ? &lpVn->vfsSrc : &lpVn->vfsDst;
399 /* if we have an ldid, use it, otherwise use the string */
400 /* from the vhstrlist array */
401 if (lpvfs->ldid != 0xffff)
402 CtlGetLddPath16(lpvfs->ldid, buffer);
404 strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrDir));
406 strcat(buffer, "\\");
407 strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrFileName));
411 FIXME("%d unimplemented !\n", dwWhat);
412 strcpy(buffer, "Unknown error");
418 static RETERR16 VCP_CheckPaths(void)
423 VCP_Callback(&vcp_status, VCPM_VSTATPATHCHECKSTART, 0, 0, VCP_MsgRef);
424 for (n = 0; n < vn_num; n++)
428 /* FIXME: check paths of all VIRTNODEs here ! */
429 VCP_Callback(&lpvn->vfsDst, VCPM_CHECKPATH, 0, (DWORD)lpvn, VCP_MsgRef);
431 VCP_Callback(&vcp_status, VCPM_VSTATPATHCHECKEND, 0, 0, VCP_MsgRef);
435 static RETERR16 VCP_CopyFiles(void)
437 char fn_src[MAX_PATH], fn_dst[MAX_PATH];
442 VCP_Callback(&vcp_status, VCPM_VSTATCOPYSTART, 0, 0, VCP_MsgRef);
443 for (n = 0; n < vn_num; n++)
446 if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_COPY)) continue;
447 /* FIXME: need to send VCPM_VSTATNEWDISK notification sometimes */
448 strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL));
449 strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL));
450 /* FIXME: what is this VCPM_VSTATWRITE here for ?
451 * I guess it's to signal successful destination file creation */
452 VCP_Callback(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef);
454 /* FIXME: need to do the file copy in small chunks for notifications */
455 TRACE("copying '%s' to '%s'\n", fn_src, fn_dst);
456 /* perform the file copy */
457 if (!CopyFileA(fn_src, fn_dst, !(lpvn->fl & VNLP_COPYIFEXISTS)))
459 ERR("error copying, src: %s -> dst: %s\n", fn_src, fn_dst);
460 res = ERR_VCP_IOFAIL;
463 vcp_status.prgFileRead.dwSoFar++;
464 VCP_Callback(&vcp_status, VCPM_VSTATREAD, 0, 0, VCP_MsgRef);
465 vcp_status.prgFileWrite.dwSoFar++;
466 VCP_Callback(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef);
469 VCP_Callback(&vcp_status, VCPM_VSTATCOPYEND, 0, 0, VCP_MsgRef);
473 /***********************************************************************
474 * VcpClose (SETUPX.201)
476 * Does callbacks (-> vifproc) with VCPM_VSTATCLOSESTART,
477 * VCPM_VSTATCLOSEEND.
479 * fl gets VCPFL_xxx flags to indicate what to do with the
480 * VIRTNODEs (files to mess with) created by e.g. GenInstall()
482 RETERR16 WINAPI VcpClose16(WORD fl, LPCSTR lpszBackupDest)
486 TRACE("(%04x, '%s')\n", fl, lpszBackupDest);
488 /* FIXME: needs to sort VIRTNODEs in case VCPFL_INSPECIFIEDORDER
489 * is not set. This is done by VCP_Callback(VCPM_NODECOMPARE) */
492 memset(&vcp_status, 0, sizeof(VCPSTATUS));
493 /* yes, vcp_status.cbSize is 0 ! */
495 VCP_Callback(&vcp_status, VCPM_VSTATCLOSESTART, 0, 0, VCP_MsgRef);
498 res = VCP_CheckPaths();
501 return res; /* is this ok ? */
505 VCP_Callback(&vcp_status, VCPM_VSTATCLOSEEND, 0, 0, VCP_MsgRef);
512 /***********************************************************************
513 * vcpDefCallbackProc (SETUPX.202)
515 RETERR16 WINAPI vcpDefCallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam,
516 LPARAM lParam, LPARAM lParamRef)
518 static int count = 0;
520 FIXME("(%p, %04x, %04lx, %08lx, %08lx) - what to do here ?\n",
521 lpvObj, uMsg, wParam, lParam, lParamRef);
526 /********************* point-and-click stuff from here ***********************/
528 static HWND hDlgCopy = 0;
529 static HKEY hKeyFiles = 0, hKeyRename = 0, hKeyConflict = 0;
530 static char BackupDir[12];
532 static INT_PTR CALLBACK VCP_UI_FileCopyDlgProc(HWND hWndDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
534 INT_PTR retval = FALSE;
536 if (iMsg == WM_INITDIALOG)
538 ShowWindow(hWndDlg, SW_SHOWNORMAL);
539 UpdateWindow(hWndDlg);
545 static BOOL VCP_UI_GetDialogTemplate(LPCVOID *template32)
550 if (!(hResInfo = FindResourceA(SETUPAPI_hInstance, MAKEINTRESOURCEA(COPYFILEDLGORD), (LPSTR)RT_DIALOG)))
552 if (!(hDlgTmpl32 = LoadResource(SETUPAPI_hInstance, hResInfo )) ||
553 !(*template32 = LockResource( hDlgTmpl32 )))
558 static LRESULT WINAPI
559 VCP_UI_FileCopyWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
561 if (uMsg != WM_CREATE)
562 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
567 static void VCP_UI_RegisterProgressClass(void)
569 static BOOL registered = FALSE;
576 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
577 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
578 wndClass.lpfnWndProc = VCP_UI_FileCopyWndProc;
579 wndClass.cbClsExtra = 0;
580 wndClass.cbWndExtra = 0;
581 wndClass.hCursor = LoadCursorA (0, (LPSTR)IDC_ARROW);
582 wndClass.hbrBackground = NULL;
583 wndClass.lpszClassName = "setupx_progress";
585 RegisterClassA (&wndClass);
587 SETUPAPI_hInstance = LoadLibraryA( "setupapi.dll" );
590 static RETERR16 VCP_UI_NodeCompare(LPVIRTNODE vn1, LPVIRTNODE vn2)
594 file1 = vsmGetStringRawName16(vn1->vfsSrc.vhstrFileName);
595 file2 = vsmGetStringRawName16(vn2->vfsSrc.vhstrFileName);
597 ret = strcmp(file1, file2);
598 /* Looks too complicated, but in optimized strcpy we might get
599 * a 32bit wide difference and would truncate it to 16 bit, so
600 * erroneously returning equality. */
601 if (ret < 0) return -1;
602 if (ret > 0) return 1;
606 static RETERR16 VCP_UI_CopyStart(void)
609 char buf[256]; /* plenty */
613 /* FIXME: should be registered at DLL startup instead */
614 VCP_UI_RegisterProgressClass();
615 if (!(VCP_UI_GetDialogTemplate(&template32)))
618 if (vn_num > 10) /* hack */
620 hDlgCopy = CreateDialogIndirectParamA(SETUPAPI_hInstance, template32, 0,
621 VCP_UI_FileCopyDlgProc, 0);
624 SetDlgItemTextA(hDlgCopy, SOURCESTRORD, "Scanning ...");
625 SetDlgItemTextA(hDlgCopy, DESTSTRORD, "NOT_IMPLEMENTED_YET");
627 strcpy(buf, REG_INSTALLEDFILES);
628 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyFiles))
630 strcat(buf, REGPART_RENAME);
631 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyRename))
633 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT, &hKeyConflict))
636 if (!(RegQueryValueExA(hKeyConflict, "Dirty", NULL, 0, (LPBYTE)&dirty, &len)))
638 /* FIXME: what does SETUPX.DLL do in this case ? */
639 MESSAGE("Warning: another program using SETUPX is already running ! Failed.\n");
643 if (RegSetValueExA(hKeyConflict, "Dirty", 0, REG_BINARY, (LPBYTE)&dirty, 1))
646 if (!(RegQueryValueExA(hKeyConflict, "BackupDirectory", NULL, 0, (LPBYTE)BackupDir, &len)))
647 strcpy(BackupDir, "VCM");
649 /* create C:\WINDOWS\[BackupDir] and set registry key to it */
650 GetWindowsDirectoryA(buf, 256);
652 strcat(buf, BackupDir);
653 if (!(CreateDirectoryA(buf, NULL)))
655 if (RegSetValueExA(hKeyConflict, "BackupDirectory", 0, REG_SZ, (LPBYTE)buf, strlen(buf)+1))
657 RegCloseKey(hKeyConflict);
662 /***********************************************************************
663 * vcpUICallbackProc (SETUPX.213)
665 RETERR16 WINAPI vcpUICallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam,
666 LPARAM lParam, LPARAM lParamRef)
668 static int count = 0;
669 RETERR16 res = VCPN_OK;
672 FIXME("(%p, %04x, %04lx, %08lx, %08lx) - semi-stub\n",
673 lpvObj, uMsg, wParam, lParam, lParamRef);
677 /* unused messages, it seems */
678 case VCPM_DISKPREPINFO:
680 case VCPM_FILENEEDED:
682 case VCPM_NODECREATE:
683 case VCPM_NODEACCEPT:
685 case VCPM_VSTATCLOSESTART:
686 case VCPM_VSTATPATHCHECKSTART:
687 case VCPM_VSTATPATHCHECKEND:
693 case VCPM_NODECOMPARE:
694 res = VCP_UI_NodeCompare((LPVIRTNODE)lpvObj, (LPVIRTNODE)lParam);
698 case VCPM_VSTATWRITE:
699 VCP_Callback(&vcp_status, VCPM_DISKPREPINFO, 0, 0, VCP_MsgRef);
701 case VCPM_VSTATCLOSEEND:
702 RegCloseKey(hKeyFiles);
703 RegCloseKey(hKeyRename);
704 RegDeleteKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT);
706 case VCPM_VSTATCOPYSTART:
707 res = VCP_UI_CopyStart();
709 case VCPM_VSTATCOPYEND:
710 if (hDlgCopy) DestroyWindow(hDlgCopy);
713 FIXME("unhandled msg 0x%04x\n", uMsg);