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,
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);
568 static void VCP_UI_RegisterProgressClass(void)
570 static BOOL registered = FALSE;
577 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
578 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
579 wndClass.lpfnWndProc = VCP_UI_FileCopyWndProc;
580 wndClass.cbClsExtra = 0;
581 wndClass.cbWndExtra = 0;
582 wndClass.hCursor = LoadCursorA (0, (LPSTR)IDC_ARROW);
583 wndClass.hbrBackground = NULL;
584 wndClass.lpszClassName = "setupx_progress";
586 RegisterClassA (&wndClass);
588 SETUPAPI_hInstance = LoadLibraryA( "setupapi.dll" );
591 static RETERR16 VCP_UI_NodeCompare(LPVIRTNODE vn1, LPVIRTNODE vn2)
594 file1 = vsmGetStringRawName16(vn1->vfsSrc.vhstrFileName);
595 file2 = vsmGetStringRawName16(vn2->vfsSrc.vhstrFileName);
596 return (RETERR16)strcmp(file1, file2);
599 static RETERR16 VCP_UI_CopyStart(void)
602 char buf[256]; /* plenty */
606 /* FIXME: should be registered at DLL startup instead */
607 VCP_UI_RegisterProgressClass();
608 if (!(VCP_UI_GetDialogTemplate(&template32)))
611 if (vn_num > 10) /* hack */
613 hDlgCopy = CreateDialogIndirectParamA(SETUPAPI_hInstance, template32, 0,
614 VCP_UI_FileCopyDlgProc, 0);
617 SetDlgItemTextA(hDlgCopy, SOURCESTRORD, "Scanning ...");
618 SetDlgItemTextA(hDlgCopy, DESTSTRORD, "NOT_IMPLEMENTED_YET");
620 strcpy(buf, REG_INSTALLEDFILES);
621 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyFiles))
623 strcat(buf, REGPART_RENAME);
624 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyRename))
626 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT, &hKeyConflict))
629 if (!(RegQueryValueExA(hKeyConflict, "Dirty", NULL, 0, (LPBYTE)&dirty, &len)))
631 /* FIXME: what does SETUPX.DLL do in this case ? */
632 MESSAGE("Warning: another program using SETUPX is already running ! Failed.\n");
636 if (RegSetValueExA(hKeyConflict, "Dirty", 0, REG_BINARY, (LPBYTE)&dirty, 1))
639 if (!(RegQueryValueExA(hKeyConflict, "BackupDirectory", NULL, 0, (LPBYTE)BackupDir, &len)))
640 strcpy(BackupDir, "VCM");
642 /* create C:\WINDOWS\[BackupDir] and set registry key to it */
643 GetWindowsDirectoryA(buf, 256);
645 strcat(buf, BackupDir);
646 if (!(CreateDirectoryA(buf, NULL)))
648 if (RegSetValueExA(hKeyConflict, "BackupDirectory", 0, REG_SZ, (LPBYTE)buf, strlen(buf)+1))
650 RegCloseKey(hKeyConflict);
655 /***********************************************************************
656 * vcpUICallbackProc (SETUPX.213)
658 RETERR16 WINAPI vcpUICallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam,
659 LPARAM lParam, LPARAM lParamRef)
661 static int count = 0;
662 RETERR16 res = VCPN_OK;
665 FIXME("(%p, %04x, %04lx, %08lx, %08lx) - semi-stub\n",
666 lpvObj, uMsg, wParam, lParam, lParamRef);
670 /* unused messages, it seems */
671 case VCPM_DISKPREPINFO:
673 case VCPM_FILENEEDED:
675 case VCPM_NODECREATE:
676 case VCPM_NODEACCEPT:
678 case VCPM_VSTATCLOSESTART:
679 case VCPM_VSTATPATHCHECKSTART:
680 case VCPM_VSTATPATHCHECKEND:
686 case VCPM_NODECOMPARE:
687 res = VCP_UI_NodeCompare((LPVIRTNODE)lpvObj, (LPVIRTNODE)lParam);
691 case VCPM_VSTATWRITE:
692 VCP_Callback(&vcp_status, VCPM_DISKPREPINFO, 0, 0, VCP_MsgRef);
694 case VCPM_VSTATCLOSEEND:
695 RegCloseKey(hKeyFiles);
696 RegCloseKey(hKeyRename);
697 RegDeleteKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT);
699 case VCPM_VSTATCOPYSTART:
700 res = VCP_UI_CopyStart();
702 case VCPM_VSTATCOPYEND:
703 if (hDlgCopy) DestroyWindow(hDlgCopy);
706 FIXME("unhandled msg 0x%04x\n", uMsg);