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)
216 while (vn_last < vn_num)
218 if (pvnlist[vn_last] == NULL)
222 heap = GetProcessHeap();
223 if (vn_last == vn_num)
227 pvnlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, pvnlist,
228 sizeof(LPVIRTNODE *) * vn_num);
230 pvnlist = HeapAlloc(heap, HEAP_ZERO_MEMORY,
231 sizeof(LPVIRTNODE *) * vn_num);
233 pvnlist[vn_last] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VIRTNODE));
234 lpvn = pvnlist[vn_last];
237 lpvn->cbSize = sizeof(VIRTNODE);
240 lpvn->vfsSrc = *vfsSrc;
243 lpvn->vfsDst = *vfsDst;
246 lpvn->lParam = lParam;
247 lpvn->lpExpandVtbl = lpExpandVtbl;
249 lpvn->vhstrDstFinalName = 0xffff; /* FIXME: what is this ? */
251 cbres = VCP_Callback(lpvn, VCPM_NODECREATE, 0, 0, VCP_MsgRef);
252 lpvn->fl |= VFNL_CREATED;
253 cbres = VCP_Callback(lpvn, VCPM_NODEACCEPT, 0, 0, VCP_MsgRef);
258 /***********************************************************************
259 * VcpOpen (SETUPX.200)
261 * Sets up a virtual copy operation.
262 * This means that functions such as GenInstall()
263 * create a VIRTNODE struct for every file to be touched in a .INF file
264 * instead of actually touching the file.
265 * The actual copy/move/rename gets started when VcpClose or
266 * VcpFlush is called; several different callbacks are made
267 * (copy, rename, open, close, version conflicts, ...) on every file copied.
269 RETERR16 WINAPI VcpOpen16(VIFPROC vifproc, LPARAM lparamMsgRef)
271 TRACE("(%p, %08lx)\n", vifproc, lparamMsgRef);
275 VCP_Proc = (FARPROC16)vifproc;
276 VCP_MsgRef = lparamMsgRef;
282 /***********************************************************************
283 * VcpQueueCopy [SETUPX.13]
285 * lpExpandVtbl seems to be deprecated.
286 * fl are the CNFL_xxx and VNFL_xxx flags.
287 * lParam are the VNLP_xxx flags.
289 RETERR16 WINAPI VcpQueueCopy16(
290 LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName,
291 LPCSTR lpszSrcDir, LPCSTR lpszDstDir,
292 LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst,
293 LPEXPANDVTBL lpExpandVtbl,
294 WORD fl, LPARAM lParam
297 VCPFILESPEC vfsSrc, vfsDst;
300 return ERR_VCP_NOTOPEN;
302 TRACE("srcdir: %s, srcfile: %s, dstdir: %s, dstfile: %s\n",
303 lpszSrcDir, lpszSrcFileName, lpszDstDir, lpszDstFileName);
305 TRACE("ldidSrc == %d, ldidDst == %d\n", ldidSrc, ldidDst);
307 vfsSrc.ldid = ldidSrc;
308 vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir);
309 vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName);
311 vfsDst.ldid = ldidDst;
312 vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
313 vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
315 return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, fl, lParam,
319 /***********************************************************************
320 * VcpQueueDelete [SETUPX.17]
322 * Is lParamRef the same as lParam in VcpQueueCopy ?
323 * Damn docu !! Err... which docu ?
325 RETERR16 WINAPI VcpQueueDelete16(
326 LPCSTR lpszDstFileName,
335 return ERR_VCP_NOTOPEN;
337 vfsDst.ldid = ldidDst;
338 vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
339 vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
341 return VCP_VirtnodeCreate(NULL, &vfsDst, VNFL_DELETE, lParamRef, 0);
344 /***********************************************************************
345 * VcpQueueRename [SETUPX.204]
348 RETERR16 WINAPI VcpQueueRename16(
349 LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName,
350 LPCSTR lpszSrcDir, LPCSTR lpszDstDir,
351 LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst,
355 VCPFILESPEC vfsSrc, vfsDst;
358 return ERR_VCP_NOTOPEN;
360 vfsSrc.ldid = ldidSrc;
361 vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir);
362 vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName);
364 vfsDst.ldid = ldidDst;
365 vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
366 vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
368 return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, VNFL_RENAME, lParam,
372 /***********************************************************************
373 * VcpEnumFiles (SETUPX.@)
375 INT16 WINAPI VcpEnumFiles(VCPENUMPROC vep, LPARAM lParamRef)
379 for (n = 0; n < vn_last; n++)
380 vep(pvnlist[n], lParamRef);
382 return 0; /* FIXME: return value ? */
385 /***********************************************************************
386 * VcpExplain (SETUPX.411)
388 LPCSTR WINAPI VcpExplain16(LPVIRTNODE lpVn, DWORD dwWhat)
390 static char buffer[MAX_PATH]; /* FIXME: is this how it's done ? */
397 LPVCPFILESPEC lpvfs =
398 (dwWhat == VCPEX_SRC_FULL) ? &lpVn->vfsSrc : &lpVn->vfsDst;
400 /* if we have an ldid, use it, otherwise use the string */
401 /* from the vhstrlist array */
402 if (lpvfs->ldid != 0xffff)
403 CtlGetLddPath16(lpvfs->ldid, buffer);
405 strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrDir));
407 strcat(buffer, "\\");
408 strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrFileName));
412 FIXME("%d unimplemented !\n", dwWhat);
413 strcpy(buffer, "Unknown error");
419 static RETERR16 VCP_CheckPaths(void)
425 cbres = VCP_Callback(&vcp_status, VCPM_VSTATPATHCHECKSTART, 0, 0, VCP_MsgRef);
426 for (n = 0; n < vn_num; n++)
430 /* FIXME: check paths of all VIRTNODEs here ! */
431 cbres = VCP_Callback(&lpvn->vfsDst, VCPM_CHECKPATH, 0, (DWORD)lpvn, VCP_MsgRef);
433 cbres = VCP_Callback(&vcp_status, VCPM_VSTATPATHCHECKEND, 0, 0, VCP_MsgRef);
437 static RETERR16 VCP_CopyFiles(void)
439 char fn_src[MAX_PATH], fn_dst[MAX_PATH];
440 RETERR16 res = OK, cbres;
444 cbres = VCP_Callback(&vcp_status, VCPM_VSTATCOPYSTART, 0, 0, VCP_MsgRef);
445 for (n = 0; n < vn_num; n++)
448 if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_COPY)) continue;
449 /* FIXME: need to send VCPM_VSTATNEWDISK notification sometimes */
450 strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL));
451 strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL));
452 /* FIXME: what is this VCPM_VSTATWRITE here for ?
453 * I guess it's to signal successful destination file creation */
454 cbres = VCP_Callback(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef);
456 /* FIXME: need to do the file copy in small chunks for notifications */
457 TRACE("copying '%s' to '%s'\n", fn_src, fn_dst);
458 /* perform the file copy */
459 if (!(CopyFileA(fn_src, fn_dst,
460 (lpvn->fl & VNLP_COPYIFEXISTS) ? FALSE : TRUE )))
462 ERR("error copying, src: %s -> dst: %s\n", fn_src, fn_dst);
463 res = ERR_VCP_IOFAIL;
466 vcp_status.prgFileRead.dwSoFar++;
467 cbres = VCP_Callback(&vcp_status, VCPM_VSTATREAD, 0, 0, VCP_MsgRef);
468 vcp_status.prgFileWrite.dwSoFar++;
469 cbres = VCP_Callback(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef);
472 cbres = VCP_Callback(&vcp_status, VCPM_VSTATCOPYEND, 0, 0, VCP_MsgRef);
476 /***********************************************************************
477 * VcpClose (SETUPX.201)
479 * Does callbacks (-> vifproc) with VCPM_VSTATCLOSESTART,
480 * VCPM_VSTATCLOSEEND.
482 * fl gets VCPFL_xxx flags to indicate what to do with the
483 * VIRTNODEs (files to mess with) created by e.g. GenInstall()
485 RETERR16 WINAPI VcpClose16(WORD fl, LPCSTR lpszBackupDest)
488 WORD cbres = VCPN_PROCEED;
490 TRACE("(%04x, '%s')\n", fl, lpszBackupDest);
492 /* FIXME: needs to sort VIRTNODEs in case VCPFL_INSPECIFIEDORDER
493 * is not set. This is done by VCP_Callback(VCPM_NODECOMPARE) */
496 memset(&vcp_status, 0, sizeof(VCPSTATUS));
497 /* yes, vcp_status.cbSize is 0 ! */
499 cbres = VCP_Callback(&vcp_status, VCPM_VSTATCLOSESTART, 0, 0, VCP_MsgRef);
502 res = VCP_CheckPaths();
505 return res; /* is this ok ? */
509 cbres = VCP_Callback(&vcp_status, VCPM_VSTATCLOSEEND, 0, 0, VCP_MsgRef);
516 /***********************************************************************
517 * vcpDefCallbackProc (SETUPX.202)
519 RETERR16 WINAPI vcpDefCallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam,
520 LPARAM lParam, LPARAM lParamRef)
522 static int count = 0;
524 FIXME("(%p, %04x, %04lx, %08lx, %08lx) - what to do here ?\n",
525 lpvObj, uMsg, wParam, lParam, lParamRef);
530 /********************* point-and-click stuff from here ***********************/
532 static HWND hDlgCopy = 0;
533 static HKEY hKeyFiles = 0, hKeyRename = 0, hKeyConflict = 0;
534 static char BackupDir[12];
536 static INT_PTR CALLBACK VCP_UI_FileCopyDlgProc(HWND hWndDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
538 INT_PTR retval = FALSE;
540 if (iMsg == WM_INITDIALOG)
542 ShowWindow(hWndDlg, SW_SHOWNORMAL);
543 UpdateWindow(hWndDlg);
549 static BOOL VCP_UI_GetDialogTemplate(LPCVOID *template32)
554 if (!(hResInfo = FindResourceA(SETUPAPI_hInstance, MAKEINTRESOURCEA(COPYFILEDLGORD), (LPSTR)RT_DIALOG)))
556 if (!(hDlgTmpl32 = LoadResource(SETUPAPI_hInstance, hResInfo )) ||
557 !(*template32 = LockResource( hDlgTmpl32 )))
562 static LRESULT WINAPI
563 VCP_UI_FileCopyWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
565 if (uMsg != WM_CREATE)
566 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
573 FIXME("%04x: unhandled.\n", uMsg);
579 static void VCP_UI_RegisterProgressClass(void)
581 static BOOL registered = FALSE;
588 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
589 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
590 wndClass.lpfnWndProc = VCP_UI_FileCopyWndProc;
591 wndClass.cbClsExtra = 0;
592 wndClass.cbWndExtra = 0;
593 wndClass.hCursor = LoadCursorA (0, (LPSTR)IDC_ARROW);
594 wndClass.hbrBackground = NULL;
595 wndClass.lpszClassName = "setupx_progress";
597 RegisterClassA (&wndClass);
599 SETUPAPI_hInstance = LoadLibraryA( "setupapi.dll" );
602 static RETERR16 VCP_UI_NodeCompare(LPVIRTNODE vn1, LPVIRTNODE vn2)
605 file1 = vsmGetStringRawName16(vn1->vfsSrc.vhstrFileName);
606 file2 = vsmGetStringRawName16(vn2->vfsSrc.vhstrFileName);
607 return (RETERR16)strcmp(file1, file2);
610 static RETERR16 VCP_UI_CopyStart(void)
613 char buf[256]; /* plenty */
617 /* FIXME: should be registered at DLL startup instead */
618 VCP_UI_RegisterProgressClass();
619 if (!(VCP_UI_GetDialogTemplate(&template32)))
622 if (vn_num > 10) /* hack */
624 hDlgCopy = CreateDialogIndirectParamA(SETUPAPI_hInstance, template32, 0,
625 VCP_UI_FileCopyDlgProc, 0);
628 SetDlgItemTextA(hDlgCopy, SOURCESTRORD, "Scanning ...");
629 SetDlgItemTextA(hDlgCopy, DESTSTRORD, "NOT_IMPLEMENTED_YET");
631 strcpy(buf, REG_INSTALLEDFILES);
632 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyFiles))
634 strcat(buf, REGPART_RENAME);
635 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyRename))
637 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT, &hKeyConflict))
640 if (!(RegQueryValueExA(hKeyConflict, "Dirty", NULL, 0, (LPBYTE)&dirty, &len)))
642 /* FIXME: what does SETUPX.DLL do in this case ? */
643 MESSAGE("Warning: another program using SETUPX is already running ! Failed.\n");
647 if (RegSetValueExA(hKeyConflict, "Dirty", 0, REG_BINARY, (LPBYTE)&dirty, 1))
650 if (!(RegQueryValueExA(hKeyConflict, "BackupDirectory", NULL, 0, (LPBYTE)BackupDir, &len)))
651 strcpy(BackupDir, "VCM");
653 /* create C:\WINDOWS\[BackupDir] and set registry key to it */
654 GetWindowsDirectoryA(buf, 256);
656 strcat(buf, BackupDir);
657 if (!(CreateDirectoryA(buf, NULL)))
659 if (RegSetValueExA(hKeyConflict, "BackupDirectory", 0, REG_SZ, (LPBYTE)buf, strlen(buf)+1))
661 RegCloseKey(hKeyConflict);
666 /***********************************************************************
667 * vcpUICallbackProc (SETUPX.213)
669 RETERR16 WINAPI vcpUICallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam,
670 LPARAM lParam, LPARAM lParamRef)
672 static int count = 0;
673 RETERR16 res = VCPN_OK, cbres;
676 FIXME("(%p, %04x, %04lx, %08lx, %08lx) - semi-stub\n",
677 lpvObj, uMsg, wParam, lParam, lParamRef);
681 /* unused messages, it seems */
682 case VCPM_DISKPREPINFO:
684 case VCPM_FILENEEDED:
686 case VCPM_NODECREATE:
687 case VCPM_NODEACCEPT:
689 case VCPM_VSTATCLOSESTART:
690 case VCPM_VSTATPATHCHECKSTART:
691 case VCPM_VSTATPATHCHECKEND:
697 case VCPM_NODECOMPARE:
698 res = VCP_UI_NodeCompare((LPVIRTNODE)lpvObj, (LPVIRTNODE)lParam);
702 case VCPM_VSTATWRITE:
703 cbres = VCP_Callback(&vcp_status, VCPM_DISKPREPINFO, 0, 0, VCP_MsgRef);
705 case VCPM_VSTATCLOSEEND:
706 RegCloseKey(hKeyFiles);
707 RegCloseKey(hKeyRename);
708 RegDeleteKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT);
710 case VCPM_VSTATCOPYSTART:
711 res = VCP_UI_CopyStart();
713 case VCPM_VSTATCOPYEND:
714 if (hDlgCopy) DestroyWindow(hDlgCopy);
717 FIXME("unhandled msg 0x%04x\n", uMsg);