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.
13 #include "setupapi_private.h"
14 #include "debugtools.h"
16 DEFAULT_DEBUG_CHANNEL(setupapi);
18 /* ### start build ### */
19 extern WORD CALLBACK VCP_CallTo16_word_lwwll(FARPROC16,LPVOID,UINT16,WPARAM,LPARAM,LPARAM);
20 /* ### stop build ### */
22 static FARPROC16 VCP_Proc = NULL;
23 static LPARAM VCP_MsgRef = 0;
25 #define VCP_CALLBACK(obj,msg,wParam,lParam,lParamRef) \
27 VCP_CallTo16_word_lwwll(VCP_Proc, obj,msg,wParam,lParam,lParamRef) : OK;
29 static BOOL VCP_opened = FALSE;
31 static VCPSTATUS vcp_status;
33 static HINSTANCE SETUPAPI_hInstance;
35 /****************************** VHSTR management ******************************/
38 * This is a totally braindead implementation for now;
39 * I don't care about speed at all ! Size and implementation time
40 * is much more important IMHO. I could have created some sophisticated
41 * tree structure, but... what the hell ! :-)
48 static VHSTR_STRUCT **vhstrlist = NULL;
49 static VHSTR vhstr_alloc = 0;
51 #define VALID_VHSTR(x) ((x < vhstr_alloc) && (vhstrlist[x]) && (vhstrlist[x]->refcount))
53 /***********************************************************************
54 * vsmStringAdd (SETUPX.207)
56 VHSTR WINAPI vsmStringAdd16(LPCSTR lpszName)
62 TRACE("add string '%s'\n", lpszName);
63 /* search whether string already inserted */
64 TRACE("searching for existing string...\n");
65 for (n = 0; n < vhstr_alloc; n++)
67 if ((vhstrlist[n]) && (vhstrlist[n]->refcount))
69 TRACE("checking item: %d\n", n);
70 if (!strcmp(vhstrlist[n]->pStr, lpszName))
73 vhstrlist[n]->refcount++;
79 /* hmm, not found yet, let's insert it */
80 TRACE("inserting item\n");
81 for (n = 0; n < vhstr_alloc; n++)
83 if ((!(vhstrlist[n])) || (!(vhstrlist[n]->refcount)))
89 heap = GetProcessHeap();
90 if (n == vhstr_alloc) /* hmm, no free index found yet */
94 vhstrlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, vhstrlist,
95 sizeof(VHSTR_STRUCT *) * vhstr_alloc);
98 return 0xffff; /* failure */
99 if (!vhstrlist[index])
100 vhstrlist[index] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VHSTR_STRUCT));
101 vhstrlist[index]->refcount = 1;
102 vhstrlist[index]->pStr = HeapAlloc(heap, 0, strlen(lpszName)+1);
103 strcpy((LPSTR)vhstrlist[index]->pStr, lpszName);
107 /***********************************************************************
108 * vsmStringDelete (SETUPX.206)
110 INT16 WINAPI vsmStringDelete16(VHSTR vhstr)
112 if (VALID_VHSTR(vhstr))
114 vhstrlist[vhstr]->refcount--;
115 if (!vhstrlist[vhstr]->refcount)
117 HeapFree(GetProcessHeap(), 0, (LPSTR)vhstrlist[vhstr]->pStr);
118 vhstrlist[vhstr]->pStr = NULL;
123 /* string not found */
128 * vsmStringFind() - not exported from a standard SETUPX.DLL, it seems
130 VHSTR WINAPI vsmStringFind16(LPCSTR lpszName)
133 for (n = 0; n < vhstr_alloc; n++)
134 if ((vhstrlist[n]) && (vhstrlist[n]->refcount) && (!strcmp(vhstrlist[n]->pStr, lpszName)))
139 /***********************************************************************
140 * vsmGetStringName (SETUPX.205)
142 * Pretty correct, I guess
144 INT16 WINAPI vsmGetStringName16(VHSTR vhstr, LPSTR lpszBuffer, int cbBuffer)
146 if (VALID_VHSTR(vhstr))
148 int len = strlen(vhstrlist[vhstr]->pStr)+1;
152 strcpy(lpszBuffer, vhstrlist[vhstr]->pStr);
159 /***********************************************************************
160 * vsmStringCompare (not exported from a standard SETUPX.DLL, it seems)
162 INT16 WINAPI vsmStringCompare16(VHSTR vhstrA, VHSTR vhstrB)
164 if ((!VALID_VHSTR(vhstrA)) || (!VALID_VHSTR(vhstrB)))
165 return VCPN_FAIL; /* correct ? */
166 return strcmp(vhstrlist[vhstrA]->pStr, vhstrlist[vhstrB]->pStr);
169 /***********************************************************************
170 * vsmGetStringRawName (SETUPX.208)
172 LPCSTR WINAPI vsmGetStringRawName16(VHSTR vhstr)
174 return (VALID_VHSTR(vhstr)) ? vhstrlist[vhstr]->pStr : NULL;
178 /***************************** VIRTNODE management ****************************/
179 static LPVIRTNODE *pvnlist = NULL;
180 static DWORD vn_num = 0;
181 static DWORD vn_last = 0;
183 RETERR16 VCP_VirtnodeCreate(LPVCPFILESPEC vfsSrc, LPVCPFILESPEC vfsDst, WORD fl, LPARAM lParam, LPEXPANDVTBL lpExpandVtbl)
189 while (vn_last < vn_num)
191 if (pvnlist[vn_last] == NULL)
195 heap = GetProcessHeap();
196 if (vn_last == vn_num)
199 pvnlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, pvnlist,
200 sizeof(LPVIRTNODE *) * vn_num);
202 pvnlist[vn_last] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VIRTNODE));
203 lpvn = pvnlist[vn_last];
206 lpvn->cbSize = sizeof(VIRTNODE);
209 memcpy(&lpvn->vfsSrc, vfsSrc, sizeof(VCPFILESPEC));
212 memcpy(&lpvn->vfsDst, vfsDst, sizeof(VCPFILESPEC));
215 lpvn->lParam = lParam;
216 lpvn->lpExpandVtbl = lpExpandVtbl;
218 lpvn->vhstrDstFinalName = 0xffff; /* FIXME: what is this ? */
220 cbres = VCP_CALLBACK(lpvn, VCPM_NODECREATE, 0, 0, VCP_MsgRef);
221 lpvn->fl |= VFNL_CREATED;
222 cbres = VCP_CALLBACK(lpvn, VCPM_NODEACCEPT, 0, 0, VCP_MsgRef);
227 BOOL VCP_VirtnodeDelete(LPVIRTNODE lpvnDel)
232 for (n = 0; n < vn_last; n++)
234 if (pvnlist[n] == lpvnDel)
236 cbres = VCP_CALLBACK(lpvnDel, VCPM_NODEDESTROY, 0, 0, VCP_MsgRef);
237 HeapFree(GetProcessHeap(), 0, lpvnDel);
245 /***********************************************************************
246 * VcpOpen (SETUPX.200)
248 * Sets up a virtual copy operation.
249 * This means that functions such as GenInstall()
250 * create a VIRTNODE struct for every file to be touched in a .INF file
251 * instead of actually touching the file.
252 * The actual copy/move/rename gets started when VcpClose or
253 * VcpFlush is called; several different callbacks are made
254 * (copy, rename, open, close, version conflicts, ...) on every file copied.
256 RETERR16 WINAPI VcpOpen16(VIFPROC vifproc, LPARAM lparamMsgRef)
258 TRACE("(%p, %08lx)\n", vifproc, lparamMsgRef);
262 VCP_Proc = (FARPROC16)vifproc;
263 VCP_MsgRef = lparamMsgRef;
265 /* load SETUPAPI needed for dialog resources etc. */
266 SETUPAPI_hInstance = LoadLibraryA("setupapi.dll");
267 if (!SETUPAPI_hInstance)
269 ERR("Could not load sibling setupapi.dll\n");
270 return ERR_VCP_NOMEM;
276 /***********************************************************************
277 * VcpQueueCopy [SETUPX.13]
279 * lpExpandVtbl seems to be deprecated.
280 * fl are the CNFL_xxx and VNFL_xxx flags.
281 * lParam are the VNLP_xxx flags.
283 RETERR16 WINAPI VcpQueueCopy16(
284 LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName,
285 LPCSTR lpszSrcDir, LPCSTR lpszDstDir,
286 LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst,
287 LPEXPANDVTBL lpExpandVtbl,
288 WORD fl, LPARAM lParam
291 VCPFILESPEC vfsSrc, vfsDst;
294 return ERR_VCP_NOTOPEN;
296 TRACE("srcdir: %s, srcfile: %s, dstdir: %s, dstfile: %s\n",
297 lpszSrcDir, lpszSrcFileName, lpszDstDir, lpszDstFileName);
299 TRACE("ldidSrc == %d, ldidDst == %d\n", ldidSrc, ldidDst);
301 vfsSrc.ldid = ldidSrc;
302 vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir);
303 vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName);
305 vfsDst.ldid = ldidDst;
306 vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
307 vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
309 return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, fl, lParam,
313 /***********************************************************************
314 * VcpQueueDelete [SETUPX.17]
316 * Is lParamRef the same as lParam in VcpQueueCopy ?
317 * Damn docu !! Err... which docu ?
319 RETERR16 WINAPI VcpQueueDelete16(
320 LPCSTR lpszDstFileName,
329 return ERR_VCP_NOTOPEN;
331 vfsDst.ldid = ldidDst;
332 vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
333 vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
335 return VCP_VirtnodeCreate(NULL, &vfsDst, VNFL_DELETE, lParamRef, 0);
338 /***********************************************************************
339 * VcpQueueRename [SETUPX.204]
342 RETERR16 WINAPI VcpQueueRename16(
343 LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName,
344 LPCSTR lpszSrcDir, LPCSTR lpszDstDir,
345 LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst,
349 VCPFILESPEC vfsSrc, vfsDst;
352 return ERR_VCP_NOTOPEN;
354 vfsSrc.ldid = ldidSrc;
355 vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir);
356 vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName);
358 vfsDst.ldid = ldidDst;
359 vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
360 vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
362 return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, VNFL_RENAME, lParam,
366 /***********************************************************************
367 * VcpEnumFiles (SETUPX.@)
369 INT16 WINAPI VcpEnumFiles(VCPENUMPROC vep, LPARAM lParamRef)
373 for (n = 0; n < vn_last; n++)
374 vep(pvnlist[n], lParamRef);
376 return 0; /* FIXME: return value ? */
379 /***********************************************************************
380 * VcpExplain (SETUPX.411)
382 LPCSTR WINAPI VcpExplain16(LPVIRTNODE lpVn, DWORD dwWhat)
384 static char buffer[MAX_PATH]; /* FIXME: is this how it's done ? */
391 LPVCPFILESPEC lpvfs =
392 (dwWhat == VCPEX_SRC_FULL) ? &lpVn->vfsSrc : &lpVn->vfsDst;
394 /* if we have an ldid, use it, otherwise use the string */
395 /* from the vhstrlist array */
396 if (lpvfs->ldid != 0xffff)
397 CtlGetLddPath16(lpvfs->ldid, buffer);
399 strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrDir));
401 strcat(buffer, "\\");
402 strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrFileName));
406 FIXME("%ld unimplemented !\n", dwWhat);
407 strcpy(buffer, "Unknown error");
413 RETERR16 VCP_CheckPaths(void)
419 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATPATHCHECKSTART, 0, 0, VCP_MsgRef);
420 for (n = 0; n < vn_num; n++)
424 /* FIXME: check paths of all VIRTNODEs here ! */
425 cbres = VCP_CALLBACK(&lpvn->vfsDst, VCPM_CHECKPATH, 0, (DWORD)lpvn, VCP_MsgRef);
427 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATPATHCHECKEND, 0, 0, VCP_MsgRef);
431 RETERR16 VCP_CopyFiles(void)
433 char fn_src[MAX_PATH], fn_dst[MAX_PATH];
434 RETERR16 res = OK, cbres;
438 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATCOPYSTART, 0, 0, VCP_MsgRef);
439 for (n = 0; n < vn_num; n++)
442 if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_COPY)) continue;
443 /* FIXME: need to send VCPM_VSTATNEWDISK notification sometimes */
444 strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL));
445 strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL));
446 /* FIXME: what is this VCPM_VSTATWRITE here for ?
447 * I guess it's to signal successful destination file creation */
448 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef);
450 /* FIXME: need to do the file copy in small chunks for notifications */
451 TRACE("copying '%s' to '%s'\n", fn_src, fn_dst);
452 /* perform the file copy */
453 if (!(CopyFileA(fn_src, fn_dst, TRUE)))
455 ERR("error copying, src: %s -> dst: %s\n", fn_src, fn_dst);
456 res = ERR_VCP_IOFAIL;
459 vcp_status.prgFileRead.dwSoFar++;
460 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATREAD, 0, 0, VCP_MsgRef);
461 vcp_status.prgFileWrite.dwSoFar++;
462 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef);
465 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATCOPYEND, 0, 0, VCP_MsgRef);
469 /***********************************************************************
470 * VcpFlush - internal (not exported), but documented
472 * VNFL_NOW is used for VcpFlush.
474 RETERR16 VcpFlush16(WORD fl, LPCSTR lpszBackupDest)
479 /***********************************************************************
480 * VcpClose (SETUPX.201)
482 * Does callbacks (-> vifproc) with VCPM_VSTATCLOSESTART,
483 * VCPM_VSTATCLOSEEND.
485 * fl gets VCPFL_xxx flags to indicate what to do with the
486 * VIRTNODEs (files to mess with) created by e.g. GenInstall()
488 RETERR16 WINAPI VcpClose16(WORD fl, LPCSTR lpszBackupDest)
491 WORD cbres = VCPN_PROCEED;
493 TRACE("(%04x, '%s')\n", fl, lpszBackupDest);
495 /* FIXME: needs to sort virtnodes in case VCPFL_INSPECIFIEDORDER
496 * is not set. This is done by VCP_CALLBACK(VCPM_NODECOMPARE) */
499 memset(&vcp_status, 0, sizeof(VCPSTATUS));
500 /* yes, vcp_status.cbSize is 0 ! */
502 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATCLOSESTART, 0, 0, VCP_MsgRef);
505 res = VCP_CheckPaths();
508 return res; /* is this ok ? */
512 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATCLOSEEND, 0, 0, VCP_MsgRef);
515 FreeLibrary(SETUPAPI_hInstance);
520 RETERR16 VCP_RenameFiles(void)
522 char fn_src[MAX_PATH], fn_dst[MAX_PATH];
523 RETERR16 res = OK, cbres;
527 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATRENAMESTART, 0, 0, VCP_MsgRef);
528 for (n = 0; n < vn_num; n++)
531 if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_RENAME)) continue;
532 strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL));
533 strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL));
534 cbres = VCP_CALLBACK(&lpvn->vfsDst, VCPM_FILEOPENOUT, 0, (LPARAM)lpvn, VCP_MsgRef);
535 if (!(MoveFileExA(fn_src, fn_dst, MOVEFILE_REPLACE_EXISTING)))
536 res = ERR_VCP_IOFAIL;
538 VCP_VirtnodeDelete(lpvn);
540 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATRENAMEEND, 0, 0, VCP_MsgRef);
544 /***********************************************************************
545 * vcpDefCallbackProc (SETUPX.202)
547 RETERR16 WINAPI vcpDefCallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam,
548 LPARAM lParam, LPARAM lParamRef)
550 static int count = 0;
552 FIXME("(%p, %04x, %04x, %08lx, %08lx) - what to do here ?\n",
553 lpvObj, uMsg, wParam, lParam, lParamRef);
558 /********************* point-and-click stuff from here ***********************/
560 static HWND hDlgCopy = 0;
561 static HKEY hKeyFiles = 0, hKeyRename = 0, hKeyConflict = 0;
562 static char BackupDir[12];
564 static BOOL CALLBACK VCP_UI_FileCopyDlgProc(HWND hWndDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
568 if (iMsg == WM_INITDIALOG)
570 ShowWindow(hWndDlg, SW_SHOWNORMAL);
571 UpdateWindow(hWndDlg);
577 BOOL VCP_UI_GetDialogTemplate(LPCVOID *template32)
579 HANDLE hResInfo, hDlgTmpl32;
581 if (!(hResInfo = FindResourceA(SETUPAPI_hInstance, MAKEINTRESOURCEA(COPYFILEDLGORD), RT_DIALOGA)))
583 if (!(hDlgTmpl32 = LoadResource(SETUPAPI_hInstance, hResInfo )) ||
584 !(*template32 = LockResource( hDlgTmpl32 )))
589 static LRESULT WINAPI
590 VCP_UI_FileCopyWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
592 if (uMsg != WM_CREATE)
593 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
600 FIXME("%04x: unhandled.\n", uMsg);
606 void VCP_UI_RegisterProgressClass(void)
608 static BOOL registered = FALSE;
615 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
616 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
617 wndClass.lpfnWndProc = (WNDPROC)VCP_UI_FileCopyWndProc;
618 wndClass.cbClsExtra = 0;
619 wndClass.cbWndExtra = 0;
620 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
621 wndClass.hbrBackground = (HBRUSH)NULL;
622 wndClass.lpszClassName = "setupx_progress";
624 RegisterClassA (&wndClass);
627 RETERR16 VCP_UI_NodeCompare(LPVIRTNODE vn1, LPVIRTNODE vn2)
630 file1 = vsmGetStringRawName16(vn1->vfsSrc.vhstrFileName);
631 file2 = vsmGetStringRawName16(vn2->vfsSrc.vhstrFileName);
632 return (RETERR16)strcmp(file1, file2);
635 RETERR16 VCP_UI_CopyStart(void)
638 char buf[256]; /* plenty */
642 /* FIXME: should be registered at DLL startup instead */
643 VCP_UI_RegisterProgressClass();
644 if (!(VCP_UI_GetDialogTemplate(&template32)))
647 hDlgCopy = CreateDialogIndirectParamA(SETUPAPI_hInstance, template32, 0,
648 VCP_UI_FileCopyDlgProc, 0);
651 SetDlgItemTextA(hDlgCopy, SOURCESTRORD, "Scanning ...");
652 SetDlgItemTextA(hDlgCopy, DESTSTRORD, "NOT_IMPLEMENTED_YET");
653 strcpy(buf, REG_INSTALLEDFILES);
654 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyFiles))
656 strcat(buf, REGPART_RENAME);
657 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyRename))
659 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT, &hKeyConflict))
662 if (!(RegQueryValueExA(hKeyConflict, "Dirty", NULL, 0, (LPBYTE)&dirty, &len)))
664 /* FIXME: what does SETUPX.DLL do in this case ? */
665 MESSAGE("Warning: another program using SETUPX is already running ! Failed.\n");
669 if (RegSetValueExA(hKeyConflict, "Dirty", 0, REG_BINARY, (LPBYTE)&dirty, 1))
672 if (!(RegQueryValueExA(hKeyConflict, "BackupDirectory", NULL, 0, BackupDir, &len)))
673 strcpy(BackupDir, "VCM");
675 /* create C:\WINDOWS\[BackupDir] and set registry key to it */
676 GetWindowsDirectoryA(buf, 256);
678 strcat(buf, BackupDir);
679 if (!(CreateDirectoryA(buf, NULL)))
681 if (RegSetValueExA(hKeyConflict, "BackupDirectory", 0, REG_SZ, (LPBYTE)buf, strlen(buf)+1))
683 RegCloseKey(hKeyConflict);
688 /***********************************************************************
689 * vcpUICallbackProc (SETUPX.213)
691 RETERR16 WINAPI vcpUICallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam,
692 LPARAM lParam, LPARAM lParamRef)
694 static int count = 0;
695 RETERR16 res = VCPN_OK, cbres;
698 FIXME("(%p, %04x, %04x, %08lx, %08lx) - semi-stub\n",
699 lpvObj, uMsg, wParam, lParam, lParamRef);
703 /* unused messages, it seems */
704 case VCPM_DISKPREPINFO:
706 case VCPM_FILENEEDED:
708 case VCPM_NODECREATE:
709 case VCPM_NODEACCEPT:
711 case VCPM_VSTATCLOSESTART:
712 case VCPM_VSTATPATHCHECKSTART:
713 case VCPM_VSTATPATHCHECKEND:
719 case VCPM_NODECOMPARE:
720 res = VCP_UI_NodeCompare((LPVIRTNODE)lpvObj, (LPVIRTNODE)lParam);
724 case VCPM_VSTATWRITE:
725 cbres = VCP_CALLBACK(&vcp_status, VCPM_DISKPREPINFO, 0, 0, VCP_MsgRef);
727 case VCPM_VSTATCLOSEEND:
728 RegCloseKey(hKeyFiles);
729 RegCloseKey(hKeyRename);
730 RegDeleteKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT);
732 case VCPM_VSTATCOPYSTART:
733 res = VCP_UI_CopyStart();
735 case VCPM_VSTATCOPYEND:
736 DestroyWindow(hDlgCopy);
739 FIXME("unhandled msg 0x%04x\n", uMsg);