2 * SetupAPI virtual copy operations
4 * FIXME: we now rely on builtin setupapi.dll for dialog resources.
5 * This is bad ! We ought to have 16bit resource handling working.
9 #include "debugtools.h"
14 #include "stackframe.h"
16 #include "setupapi_private.h"
18 DEFAULT_DEBUG_CHANNEL(setupapi);
20 /* ### start build ### */
21 extern WORD CALLBACK VCP_CallTo16_word_lwwll(FARPROC16,LPVOID,UINT16,WPARAM,LPARAM,LPARAM);
22 /* ### stop build ### */
24 static FARPROC16 VCP_Proc = NULL;
25 static LPARAM VCP_MsgRef = 0;
27 #define VCP_CALLBACK(obj,msg,wParam,lParam,lParamRef) \
29 VCP_CallTo16_word_lwwll(VCP_Proc, obj,msg,wParam,lParam,lParamRef) : OK;
31 static BOOL VCP_opened = FALSE;
33 static VCPSTATUS vcp_status;
35 static HINSTANCE SETUPAPI_hInstance;
37 /****************************** VHSTR management ******************************/
40 * This is a totally braindead implementation for now;
41 * I don't care about speed at all ! Size and implementation time
42 * is much more important IMHO. I could have created some sophisticated
43 * tree structure, but... what the hell ! :-)
50 static VHSTR_STRUCT **vhstrlist = NULL;
51 static VHSTR vhstr_alloc = 0;
53 #define VALID_VHSTR(x) ((x < vhstr_alloc) && (vhstrlist[x]) && (vhstrlist[x]->refcount))
55 /***********************************************************************
56 * vsmStringAdd (SETUPX.207)
58 VHSTR WINAPI vsmStringAdd16(LPCSTR lpszName)
64 TRACE("add string '%s'\n", lpszName);
65 /* search whether string already inserted */
66 for (n = 0; n < vhstr_alloc; n++)
68 if ((vhstrlist[n]) && (vhstrlist[n]->refcount))
70 TRACE("comp %d\n", n);
71 if (!strcmp(vhstrlist[n]->pStr, lpszName))
73 vhstrlist[n]->refcount++;
79 /* hmm, not found yet, let's insert it */
80 for (n = 0; n < vhstr_alloc; n++)
82 if ((!(vhstrlist[n])) || (!(vhstrlist[n]->refcount)))
88 heap = GetProcessHeap();
89 if (n == vhstr_alloc) /* hmm, no free index found yet */
93 vhstrlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, vhstrlist,
94 sizeof(VHSTR_STRUCT *) * vhstr_alloc);
97 return 0xffff; /* failure */
98 if (!vhstrlist[index])
99 vhstrlist[index] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VHSTR_STRUCT));
100 vhstrlist[index]->refcount = 1;
101 vhstrlist[index]->pStr = HeapAlloc(heap, 0, strlen(lpszName)+1);
102 strcpy((LPSTR)vhstrlist[index]->pStr, lpszName);
106 /***********************************************************************
107 * vsmStringDelete (SETUPX.206)
109 INT16 WINAPI vsmStringDelete16(VHSTR vhstr)
111 if (VALID_VHSTR(vhstr))
113 vhstrlist[vhstr]->refcount--;
114 if (!vhstrlist[vhstr]->refcount)
116 HeapFree(GetProcessHeap(), 0, (LPSTR)vhstrlist[vhstr]->pStr);
117 vhstrlist[vhstr]->pStr = NULL;
122 /* string not found */
127 * vsmStringFind() - not exported from a standard SETUPX.DLL, it seems
129 VHSTR WINAPI vsmStringFind16(LPCSTR lpszName)
132 for (n = 0; n < vhstr_alloc; n++)
133 if ((vhstrlist[n]) && (vhstrlist[n]->refcount) && (!strcmp(vhstrlist[n]->pStr, lpszName)))
138 /***********************************************************************
139 * vsmGetStringName (SETUPX.205)
141 * Pretty correct, I guess
143 INT16 WINAPI vsmGetStringName16(VHSTR vhstr, LPSTR lpszBuffer, int cbBuffer)
145 if (VALID_VHSTR(vhstr))
147 int len = strlen(vhstrlist[vhstr]->pStr)+1;
151 strcpy(lpszBuffer, vhstrlist[vhstr]->pStr);
158 /***********************************************************************
159 * vsmStringCompare (not exported from a standard SETUPX.DLL, it seems)
161 INT16 WINAPI vsmStringCompare16(VHSTR vhstrA, VHSTR vhstrB)
163 if ((!VALID_VHSTR(vhstrA)) || (!VALID_VHSTR(vhstrB)))
164 return VCPN_FAIL; /* correct ? */
165 return strcmp(vhstrlist[vhstrA]->pStr, vhstrlist[vhstrB]->pStr);
168 /***********************************************************************
169 * vsmGetStringRawName (SETUPX.208)
171 LPCSTR WINAPI vsmGetStringRawName16(VHSTR vhstr)
173 return (VALID_VHSTR(vhstr)) ? vhstrlist[vhstr]->pStr : NULL;
177 /***************************** VIRTNODE management ****************************/
178 static LPVIRTNODE *pvnlist = NULL;
179 static DWORD vn_num = 0;
180 static DWORD vn_last = 0;
182 RETERR16 VCP_VirtnodeCreate(LPVCPFILESPEC vfsSrc, LPVCPFILESPEC vfsDst, WORD fl, LPARAM lParam, LPEXPANDVTBL lpExpandVtbl)
188 while (vn_last < vn_num)
190 if (pvnlist[vn_last] == NULL)
194 heap = GetProcessHeap();
195 if (vn_last == vn_num)
198 pvnlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, pvnlist,
199 sizeof(LPVIRTNODE *) * vn_num);
201 pvnlist[vn_last] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VIRTNODE));
202 lpvn = pvnlist[vn_last];
205 lpvn->cbSize = sizeof(VIRTNODE);
208 memcpy(&lpvn->vfsSrc, vfsSrc, sizeof(VCPFILESPEC));
211 memcpy(&lpvn->vfsDst, vfsDst, sizeof(VCPFILESPEC));
214 lpvn->lParam = lParam;
215 lpvn->lpExpandVtbl = lpExpandVtbl;
217 lpvn->vhstrDstFinalName = 0xffff; /* FIXME: what is this ? */
219 cbres = VCP_CALLBACK(lpvn, VCPM_NODECREATE, 0, 0, VCP_MsgRef);
220 lpvn->fl |= VFNL_CREATED;
221 cbres = VCP_CALLBACK(lpvn, VCPM_NODEACCEPT, 0, 0, VCP_MsgRef);
226 BOOL VCP_VirtnodeDelete(LPVIRTNODE lpvnDel)
231 for (n = 0; n < vn_last; n++)
233 if (pvnlist[n] == lpvnDel)
235 cbres = VCP_CALLBACK(lpvnDel, VCPM_NODEDESTROY, 0, 0, VCP_MsgRef);
236 HeapFree(GetProcessHeap(), 0, lpvnDel);
244 /***********************************************************************
245 * VcpOpen (SETUPX.200)
247 * Sets up a virtual copy operation.
248 * This means that functions such as GenInstall()
249 * create a VIRTNODE struct for every file to be touched in a .INF file
250 * instead of actually touching the file.
251 * The actual copy/move/rename gets started when VcpClose or
252 * VcpFlush is called; several different callbacks are made
253 * (copy, rename, open, close, version conflicts, ...) on every file copied.
255 RETERR16 WINAPI VcpOpen16(VIFPROC vifproc, LPARAM lparamMsgRef)
257 TRACE("(%p, %08lx)\n", vifproc, lparamMsgRef);
261 VCP_Proc = (FARPROC16)vifproc;
262 VCP_MsgRef = lparamMsgRef;
264 /* load SETUPAPI needed for dialog resources etc. */
265 SETUPAPI_hInstance = LoadLibraryA("setupapi.dll");
266 if (!SETUPAPI_hInstance)
268 ERR("Could not load sibling setupapi.dll\n");
269 return ERR_VCP_NOMEM;
275 /***********************************************************************
276 * VcpQueueCopy [SETUPX.13]
278 * lpExpandVtbl seems to be deprecated.
279 * fl are the CNFL_xxx and VNFL_xxx flags.
280 * lParam are the VNLP_xxx flags.
282 RETERR16 WINAPI VcpQueueCopy16(
283 LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName,
284 LPCSTR lpszSrcDir, LPCSTR lpszDstDir,
285 LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst,
286 LPEXPANDVTBL lpExpandVtbl,
287 WORD fl, LPARAM lParam
290 VCPFILESPEC vfsSrc, vfsDst;
293 return ERR_VCP_NOTOPEN;
295 vfsSrc.ldid = ldidSrc;
296 vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir);
297 vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName);
299 vfsDst.ldid = ldidDst;
300 vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
301 vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
303 return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, fl, lParam,
307 /***********************************************************************
308 * VcpQueueDelete [SETUPX.17]
310 * Is lParamRef the same as lParam in VcpQueueCopy ?
311 * Damn docu !! Err... which docu ?
313 RETERR16 WINAPI VcpQueueDelete16(
314 LPCSTR lpszDstFileName,
317 LPEXPANDVTBL lpExpandVtbl,
324 return ERR_VCP_NOTOPEN;
326 vfsDst.ldid = ldidDst;
327 vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
328 vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
330 return VCP_VirtnodeCreate(NULL, &vfsDst, VNFL_DELETE, lParamRef,
334 /***********************************************************************
335 * VcpQueueRename [SETUPX.204]
338 RETERR16 WINAPI VcpQueueRename16(
339 LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName,
340 LPCSTR lpszSrcDir, LPCSTR lpszDstDir,
341 LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst,
345 VCPFILESPEC vfsSrc, vfsDst;
348 return ERR_VCP_NOTOPEN;
350 vfsSrc.ldid = ldidSrc;
351 vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir);
352 vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName);
354 vfsDst.ldid = ldidDst;
355 vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
356 vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
358 return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, VNFL_RENAME, lParam,
362 /***********************************************************************
363 * VcpEnumFiles (SETUPX.@)
365 INT16 WINAPI VcpEnumFiles(VCPENUMPROC vep, LPARAM lParamRef)
369 for (n = 0; n < vn_last; n++)
370 vep(pvnlist[n], lParamRef);
372 return 0; /* FIXME: return value ? */
375 /***********************************************************************
376 * VcpExplain (SETUPX.?)
378 LPCSTR WINAPI VcpExplain16(LPVIRTNODE lpVn, DWORD dwWhat)
380 static char buffer[MAX_PATH]; /* FIXME: is this how it's done ? */
387 LPVCPFILESPEC lpvfs =
388 (dwWhat == VCPEX_SRC_FULL) ? &lpVn->vfsSrc : &lpVn->vfsDst;
390 if (lpvfs->ldid != 0xffff)
391 CtlGetLddPath16(lpvfs->ldid, buffer);
392 strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrDir));
393 strcat(buffer, "\\");
394 strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrFileName));
398 FIXME("%ld unimplemented !\n", dwWhat);
399 strcpy(buffer, "Unknown error");
405 RETERR16 VCP_CheckPaths(void)
411 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATPATHCHECKSTART, 0, 0, VCP_MsgRef);
412 for (n = 0; n < vn_num; n++)
416 /* FIXME: check paths of all VIRTNODEs here ! */
417 cbres = VCP_CALLBACK(&lpvn->vfsDst, VCPM_CHECKPATH, 0, (DWORD)lpvn, VCP_MsgRef);
419 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATPATHCHECKEND, 0, 0, VCP_MsgRef);
423 RETERR16 VCP_CopyFiles(void)
425 char fn_src[MAX_PATH], fn_dst[MAX_PATH];
426 RETERR16 res = OK, cbres;
430 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATCOPYSTART, 0, 0, VCP_MsgRef);
431 for (n = 0; n < vn_num; n++)
434 if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_COPY)) continue;
435 /* FIXME: need to send VCPM_VSTATNEWDISK notification sometimes */
436 strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL));
437 strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL));
438 /* FIXME: what is this VCPM_VSTATWRITE here for ?
439 * I guess it's to signal successful destination file creation */
440 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef);
441 /* FIXME: need to do the file copy in small chunks for notifications */
442 TRACE("copying '%s' to '%s'\n", fn_src, fn_dst);
444 if (!(CopyFileA(fn_src, fn_dst, TRUE)))
445 res = ERR_VCP_IOFAIL;
447 vcp_status.prgFileRead.dwSoFar++;
448 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATREAD, 0, 0, VCP_MsgRef);
449 vcp_status.prgFileWrite.dwSoFar++;
450 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef);
453 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATCOPYEND, 0, 0, VCP_MsgRef);
457 /***********************************************************************
458 * VcpFlush - internal (not exported), but documented
460 * VNFL_NOW is used for VcpFlush.
462 RETERR16 VcpFlush16(WORD fl, LPCSTR lpszBackupDest)
467 /***********************************************************************
468 * VcpClose (SETUPX.201)
470 * Does callbacks (-> vifproc) with VCPM_VSTATCLOSESTART,
471 * VCPM_VSTATCLOSEEND.
473 * fl gets VCPFL_xxx flags to indicate what to do with the
474 * VIRTNODEs (files to mess with) created by e.g. GenInstall()
476 RETERR16 WINAPI VcpClose16(WORD fl, LPCSTR lpszBackupDest)
479 WORD cbres = VCPN_PROCEED;
481 TRACE("(%04x, '%s')\n", fl, lpszBackupDest);
483 /* FIXME: needs to sort virtnodes in case VCPFL_INSPECIFIEDORDER
484 * is not set. This is done by VCP_CALLBACK(VCPM_NODECOMPARE) */
487 memset(&vcp_status, 0, sizeof(VCPSTATUS));
488 /* yes, vcp_status.cbSize is 0 ! */
490 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATCLOSESTART, 0, 0, VCP_MsgRef);
493 res = VCP_CheckPaths();
496 return res; /* is this ok ? */
500 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATCLOSEEND, 0, 0, VCP_MsgRef);
503 FreeLibrary(SETUPAPI_hInstance);
508 RETERR16 VCP_RenameFiles(void)
510 char fn_src[MAX_PATH], fn_dst[MAX_PATH];
511 RETERR16 res = OK, cbres;
515 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATRENAMESTART, 0, 0, VCP_MsgRef);
516 for (n = 0; n < vn_num; n++)
519 if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_RENAME)) continue;
520 strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL));
521 strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL));
522 cbres = VCP_CALLBACK(&lpvn->vfsDst, VCPM_FILEOPENOUT, 0, (LPARAM)lpvn, VCP_MsgRef);
523 if (!(MoveFileExA(fn_src, fn_dst, MOVEFILE_REPLACE_EXISTING)))
524 res = ERR_VCP_IOFAIL;
526 VCP_VirtnodeDelete(lpvn);
528 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATRENAMEEND, 0, 0, VCP_MsgRef);
532 /***********************************************************************
533 * vcpDefCallbackProc (SETUPX.202)
535 RETERR16 WINAPI vcpDefCallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam,
536 LPARAM lParam, LPARAM lParamRef)
538 static int count = 0;
540 FIXME("(%p, %04x, %04x, %08lx, %08lx) - what to do here ?\n",
541 lpvObj, uMsg, wParam, lParam, lParamRef);
546 /********************* point-and-click stuff from here ***********************/
548 static HWND hDlgCopy = 0;
549 static HKEY hKeyFiles = 0, hKeyRename = 0, hKeyConflict = 0;
550 static char BackupDir[12];
552 static BOOL CALLBACK VCP_UI_FileCopyDlgProc(HWND hWndDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
556 if (iMsg == WM_INITDIALOG)
558 ShowWindow(hWndDlg, SW_SHOWNORMAL);
559 UpdateWindow(hWndDlg);
565 BOOL VCP_UI_GetDialogTemplate(LPCVOID *template32)
567 HANDLE hResInfo, hDlgTmpl32;
569 if (!(hResInfo = FindResourceA(SETUPAPI_hInstance, MAKEINTRESOURCEA(COPYFILEDLGORD), RT_DIALOGA)))
571 if (!(hDlgTmpl32 = LoadResource(SETUPAPI_hInstance, hResInfo )) ||
572 !(*template32 = LockResource( hDlgTmpl32 )))
577 static LRESULT WINAPI
578 VCP_UI_FileCopyWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
580 if (uMsg != WM_CREATE)
581 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
588 FIXME("%04x: unhandled.\n", uMsg);
594 void VCP_UI_RegisterProgressClass(void)
596 static BOOL registered = FALSE;
603 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
604 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
605 wndClass.lpfnWndProc = (WNDPROC)VCP_UI_FileCopyWndProc;
606 wndClass.cbClsExtra = 0;
607 wndClass.cbWndExtra = 0;
608 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
609 wndClass.hbrBackground = (HBRUSH)NULL;
610 wndClass.lpszClassName = "setupx_progress";
612 RegisterClassA (&wndClass);
615 RETERR16 VCP_UI_NodeCompare(LPVIRTNODE vn1, LPVIRTNODE vn2)
618 file1 = vsmGetStringRawName16(vn1->vfsSrc.vhstrFileName);
619 file2 = vsmGetStringRawName16(vn2->vfsSrc.vhstrFileName);
620 return (RETERR16)strcmp(file1, file2);
623 RETERR16 VCP_UI_CopyStart(void)
626 char buf[256]; /* plenty */
630 /* FIXME: should be registered at DLL startup instead */
631 VCP_UI_RegisterProgressClass();
632 if (!(VCP_UI_GetDialogTemplate(&template32)))
635 hDlgCopy = CreateDialogIndirectParamA(SETUPAPI_hInstance, template32, 0,
636 VCP_UI_FileCopyDlgProc, 0);
639 SetDlgItemTextA(hDlgCopy, SOURCESTRORD, "Scanning ...");
640 SetDlgItemTextA(hDlgCopy, DESTSTRORD, "NOT_IMPLEMENTED_YET");
641 strcpy(buf, REG_INSTALLEDFILES);
642 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyFiles))
644 strcat(buf, REGPART_RENAME);
645 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyRename))
647 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT, &hKeyConflict))
650 if (!(RegQueryValueExA(hKeyConflict, "Dirty", NULL, 0, (LPBYTE)&dirty, &len)))
652 /* FIXME: what does SETUPX.DLL do in this case ? */
653 MESSAGE("Warning: another program using SETUPX is already running ! Failed.\n");
657 if (RegSetValueExA(hKeyConflict, "Dirty", 0, REG_BINARY, (LPBYTE)&dirty, 1))
660 if (!(RegQueryValueExA(hKeyConflict, "BackupDirectory", NULL, 0, BackupDir, &len)))
661 strcpy(BackupDir, "VCM");
663 /* create C:\WINDOWS\[BackupDir] and set registry key to it */
664 GetWindowsDirectoryA(buf, 256);
666 strcat(buf, BackupDir);
667 if (!(CreateDirectoryA(buf, NULL)))
669 if (RegSetValueExA(hKeyConflict, "BackupDirectory", 0, REG_SZ, (LPBYTE)buf, strlen(buf)+1))
671 RegCloseKey(hKeyConflict);
676 /***********************************************************************
677 * vcpUICallbackProc (SETUPX.213)
679 RETERR16 WINAPI vcpUICallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam,
680 LPARAM lParam, LPARAM lParamRef)
682 static int count = 0;
683 RETERR16 res = VCPN_OK, cbres;
686 FIXME("(%p, %04x, %04x, %08lx, %08lx) - semi-stub\n",
687 lpvObj, uMsg, wParam, lParam, lParamRef);
691 /* unused messages, it seems */
692 case VCPM_DISKPREPINFO:
694 case VCPM_FILENEEDED:
696 case VCPM_NODECREATE:
697 case VCPM_NODEACCEPT:
699 case VCPM_VSTATCLOSESTART:
700 case VCPM_VSTATPATHCHECKSTART:
701 case VCPM_VSTATPATHCHECKEND:
707 case VCPM_NODECOMPARE:
708 res = VCP_UI_NodeCompare((LPVIRTNODE)lpvObj, (LPVIRTNODE)lParam);
712 case VCPM_VSTATWRITE:
713 cbres = VCP_CALLBACK(&vcp_status, VCPM_DISKPREPINFO, 0, 0, VCP_MsgRef);
715 case VCPM_VSTATCLOSEEND:
716 RegCloseKey(hKeyFiles);
717 RegCloseKey(hKeyRename);
718 RegDeleteKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT);
720 case VCPM_VSTATCOPYSTART:
721 res = VCP_UI_CopyStart();
723 case VCPM_VSTATCOPYEND:
724 DestroyWindow(hDlgCopy);
727 FIXME("unhandled msg 0x%04x\n", uMsg);