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(LPVIRTNODE *) * vn_num);
229 pvnlist = HeapAlloc(heap, HEAP_ZERO_MEMORY,
230 sizeof(LPVIRTNODE *) * 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,
458 (lpvn->fl & VNLP_COPYIFEXISTS) ? FALSE : TRUE )))
460 ERR("error copying, src: %s -> dst: %s\n", fn_src, fn_dst);
461 res = ERR_VCP_IOFAIL;
464 vcp_status.prgFileRead.dwSoFar++;
465 VCP_Callback(&vcp_status, VCPM_VSTATREAD, 0, 0, VCP_MsgRef);
466 vcp_status.prgFileWrite.dwSoFar++;
467 VCP_Callback(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef);
470 VCP_Callback(&vcp_status, VCPM_VSTATCOPYEND, 0, 0, VCP_MsgRef);
474 /***********************************************************************
475 * VcpClose (SETUPX.201)
477 * Does callbacks (-> vifproc) with VCPM_VSTATCLOSESTART,
478 * VCPM_VSTATCLOSEEND.
480 * fl gets VCPFL_xxx flags to indicate what to do with the
481 * VIRTNODEs (files to mess with) created by e.g. GenInstall()
483 RETERR16 WINAPI VcpClose16(WORD fl, LPCSTR lpszBackupDest)
487 TRACE("(%04x, '%s')\n", fl, lpszBackupDest);
489 /* FIXME: needs to sort VIRTNODEs in case VCPFL_INSPECIFIEDORDER
490 * is not set. This is done by VCP_Callback(VCPM_NODECOMPARE) */
493 memset(&vcp_status, 0, sizeof(VCPSTATUS));
494 /* yes, vcp_status.cbSize is 0 ! */
496 VCP_Callback(&vcp_status, VCPM_VSTATCLOSESTART, 0, 0, VCP_MsgRef);
499 res = VCP_CheckPaths();
502 return res; /* is this ok ? */
506 VCP_Callback(&vcp_status, VCPM_VSTATCLOSEEND, 0, 0, VCP_MsgRef);
513 /***********************************************************************
514 * vcpDefCallbackProc (SETUPX.202)
516 RETERR16 WINAPI vcpDefCallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam,
517 LPARAM lParam, LPARAM lParamRef)
519 static int count = 0;
521 FIXME("(%p, %04x, %04lx, %08lx, %08lx) - what to do here ?\n",
522 lpvObj, uMsg, wParam, lParam, lParamRef);
527 /********************* point-and-click stuff from here ***********************/
529 static HWND hDlgCopy = 0;
530 static HKEY hKeyFiles = 0, hKeyRename = 0, hKeyConflict = 0;
531 static char BackupDir[12];
533 static INT_PTR CALLBACK VCP_UI_FileCopyDlgProc(HWND hWndDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
535 INT_PTR retval = FALSE;
537 if (iMsg == WM_INITDIALOG)
539 ShowWindow(hWndDlg, SW_SHOWNORMAL);
540 UpdateWindow(hWndDlg);
546 static BOOL VCP_UI_GetDialogTemplate(LPCVOID *template32)
551 if (!(hResInfo = FindResourceA(SETUPAPI_hInstance, MAKEINTRESOURCEA(COPYFILEDLGORD), (LPSTR)RT_DIALOG)))
553 if (!(hDlgTmpl32 = LoadResource(SETUPAPI_hInstance, hResInfo )) ||
554 !(*template32 = LockResource( hDlgTmpl32 )))
559 static LRESULT WINAPI
560 VCP_UI_FileCopyWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
562 if (uMsg != WM_CREATE)
563 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
570 FIXME("%04x: unhandled.\n", uMsg);
576 static void VCP_UI_RegisterProgressClass(void)
578 static BOOL registered = FALSE;
585 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
586 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
587 wndClass.lpfnWndProc = VCP_UI_FileCopyWndProc;
588 wndClass.cbClsExtra = 0;
589 wndClass.cbWndExtra = 0;
590 wndClass.hCursor = LoadCursorA (0, (LPSTR)IDC_ARROW);
591 wndClass.hbrBackground = NULL;
592 wndClass.lpszClassName = "setupx_progress";
594 RegisterClassA (&wndClass);
596 SETUPAPI_hInstance = LoadLibraryA( "setupapi.dll" );
599 static RETERR16 VCP_UI_NodeCompare(LPVIRTNODE vn1, LPVIRTNODE vn2)
602 file1 = vsmGetStringRawName16(vn1->vfsSrc.vhstrFileName);
603 file2 = vsmGetStringRawName16(vn2->vfsSrc.vhstrFileName);
604 return (RETERR16)strcmp(file1, file2);
607 static RETERR16 VCP_UI_CopyStart(void)
610 char buf[256]; /* plenty */
614 /* FIXME: should be registered at DLL startup instead */
615 VCP_UI_RegisterProgressClass();
616 if (!(VCP_UI_GetDialogTemplate(&template32)))
619 if (vn_num > 10) /* hack */
621 hDlgCopy = CreateDialogIndirectParamA(SETUPAPI_hInstance, template32, 0,
622 VCP_UI_FileCopyDlgProc, 0);
625 SetDlgItemTextA(hDlgCopy, SOURCESTRORD, "Scanning ...");
626 SetDlgItemTextA(hDlgCopy, DESTSTRORD, "NOT_IMPLEMENTED_YET");
628 strcpy(buf, REG_INSTALLEDFILES);
629 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyFiles))
631 strcat(buf, REGPART_RENAME);
632 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyRename))
634 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT, &hKeyConflict))
637 if (!(RegQueryValueExA(hKeyConflict, "Dirty", NULL, 0, (LPBYTE)&dirty, &len)))
639 /* FIXME: what does SETUPX.DLL do in this case ? */
640 MESSAGE("Warning: another program using SETUPX is already running ! Failed.\n");
644 if (RegSetValueExA(hKeyConflict, "Dirty", 0, REG_BINARY, (LPBYTE)&dirty, 1))
647 if (!(RegQueryValueExA(hKeyConflict, "BackupDirectory", NULL, 0, (LPBYTE)BackupDir, &len)))
648 strcpy(BackupDir, "VCM");
650 /* create C:\WINDOWS\[BackupDir] and set registry key to it */
651 GetWindowsDirectoryA(buf, 256);
653 strcat(buf, BackupDir);
654 if (!(CreateDirectoryA(buf, NULL)))
656 if (RegSetValueExA(hKeyConflict, "BackupDirectory", 0, REG_SZ, (LPBYTE)buf, strlen(buf)+1))
658 RegCloseKey(hKeyConflict);
663 /***********************************************************************
664 * vcpUICallbackProc (SETUPX.213)
666 RETERR16 WINAPI vcpUICallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam,
667 LPARAM lParam, LPARAM lParamRef)
669 static int count = 0;
670 RETERR16 res = VCPN_OK;
673 FIXME("(%p, %04x, %04lx, %08lx, %08lx) - semi-stub\n",
674 lpvObj, uMsg, wParam, lParam, lParamRef);
678 /* unused messages, it seems */
679 case VCPM_DISKPREPINFO:
681 case VCPM_FILENEEDED:
683 case VCPM_NODECREATE:
684 case VCPM_NODEACCEPT:
686 case VCPM_VSTATCLOSESTART:
687 case VCPM_VSTATPATHCHECKSTART:
688 case VCPM_VSTATPATHCHECKEND:
694 case VCPM_NODECOMPARE:
695 res = VCP_UI_NodeCompare((LPVIRTNODE)lpvObj, (LPVIRTNODE)lParam);
699 case VCPM_VSTATWRITE:
700 VCP_Callback(&vcp_status, VCPM_DISKPREPINFO, 0, 0, VCP_MsgRef);
702 case VCPM_VSTATCLOSEEND:
703 RegCloseKey(hKeyFiles);
704 RegCloseKey(hKeyRename);
705 RegDeleteKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT);
707 case VCPM_VSTATCOPYSTART:
708 res = VCP_UI_CopyStart();
710 case VCPM_VSTATCOPYEND:
711 if (hDlgCopy) DestroyWindow(hDlgCopy);
714 FIXME("unhandled msg 0x%04x\n", uMsg);