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"
15 #include "setupapi_private.h"
17 DEFAULT_DEBUG_CHANNEL(setupapi);
19 /* ### start build ### */
20 extern WORD CALLBACK VCP_CallTo16_word_lwwll(FARPROC16,LPVOID,UINT16,WPARAM,LPARAM,LPARAM);
21 /* ### stop build ### */
23 static FARPROC16 VCP_Proc = NULL;
24 static LPARAM VCP_MsgRef = 0;
26 #define VCP_CALLBACK(obj,msg,wParam,lParam,lParamRef) \
28 VCP_CallTo16_word_lwwll(VCP_Proc, obj,msg,wParam,lParam,lParamRef) : OK;
30 static BOOL VCP_opened = FALSE;
32 static VCPSTATUS vcp_status;
34 static HINSTANCE SETUPAPI_hInstance;
36 /****************************** VHSTR management ******************************/
39 * This is a totally braindead implementation for now;
40 * I don't care about speed at all ! Size and implementation time
41 * is much more important IMHO. I could have created some sophisticated
42 * tree structure, but... what the hell ! :-)
49 static VHSTR_STRUCT **vhstrlist = NULL;
50 static VHSTR vhstr_alloc = 0;
52 #define VALID_VHSTR(x) ((x < vhstr_alloc) && (vhstrlist[x]) && (vhstrlist[x]->refcount))
54 /***********************************************************************
55 * vsmStringAdd (SETUPX.207)
57 VHSTR WINAPI vsmStringAdd16(LPCSTR lpszName)
63 TRACE("add string '%s'\n", lpszName);
64 /* search whether string already inserted */
65 TRACE("searching for existing string...\n");
66 for (n = 0; n < vhstr_alloc; n++)
68 if ((vhstrlist[n]) && (vhstrlist[n]->refcount))
70 TRACE("checking item: %d\n", n);
71 if (!strcmp(vhstrlist[n]->pStr, lpszName))
74 vhstrlist[n]->refcount++;
80 /* hmm, not found yet, let's insert it */
81 TRACE("inserting item\n");
82 for (n = 0; n < vhstr_alloc; n++)
84 if ((!(vhstrlist[n])) || (!(vhstrlist[n]->refcount)))
90 heap = GetProcessHeap();
91 if (n == vhstr_alloc) /* hmm, no free index found yet */
95 vhstrlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, vhstrlist,
96 sizeof(VHSTR_STRUCT *) * vhstr_alloc);
99 return 0xffff; /* failure */
100 if (!vhstrlist[index])
101 vhstrlist[index] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VHSTR_STRUCT));
102 vhstrlist[index]->refcount = 1;
103 vhstrlist[index]->pStr = HeapAlloc(heap, 0, strlen(lpszName)+1);
104 strcpy((LPSTR)vhstrlist[index]->pStr, lpszName);
108 /***********************************************************************
109 * vsmStringDelete (SETUPX.206)
111 INT16 WINAPI vsmStringDelete16(VHSTR vhstr)
113 if (VALID_VHSTR(vhstr))
115 vhstrlist[vhstr]->refcount--;
116 if (!vhstrlist[vhstr]->refcount)
118 HeapFree(GetProcessHeap(), 0, (LPSTR)vhstrlist[vhstr]->pStr);
119 vhstrlist[vhstr]->pStr = NULL;
124 /* string not found */
129 * vsmStringFind() - not exported from a standard SETUPX.DLL, it seems
131 VHSTR WINAPI vsmStringFind16(LPCSTR lpszName)
134 for (n = 0; n < vhstr_alloc; n++)
135 if ((vhstrlist[n]) && (vhstrlist[n]->refcount) && (!strcmp(vhstrlist[n]->pStr, lpszName)))
140 /***********************************************************************
141 * vsmGetStringName (SETUPX.205)
143 * Pretty correct, I guess
145 INT16 WINAPI vsmGetStringName16(VHSTR vhstr, LPSTR lpszBuffer, int cbBuffer)
147 if (VALID_VHSTR(vhstr))
149 int len = strlen(vhstrlist[vhstr]->pStr)+1;
153 strcpy(lpszBuffer, vhstrlist[vhstr]->pStr);
160 /***********************************************************************
161 * vsmStringCompare (not exported from a standard SETUPX.DLL, it seems)
163 INT16 WINAPI vsmStringCompare16(VHSTR vhstrA, VHSTR vhstrB)
165 if ((!VALID_VHSTR(vhstrA)) || (!VALID_VHSTR(vhstrB)))
166 return VCPN_FAIL; /* correct ? */
167 return strcmp(vhstrlist[vhstrA]->pStr, vhstrlist[vhstrB]->pStr);
170 /***********************************************************************
171 * vsmGetStringRawName (SETUPX.208)
173 LPCSTR WINAPI vsmGetStringRawName16(VHSTR vhstr)
175 return (VALID_VHSTR(vhstr)) ? vhstrlist[vhstr]->pStr : NULL;
179 /***************************** VIRTNODE management ****************************/
180 static LPVIRTNODE *pvnlist = NULL;
181 static DWORD vn_num = 0;
182 static DWORD vn_last = 0;
184 RETERR16 VCP_VirtnodeCreate(LPVCPFILESPEC vfsSrc, LPVCPFILESPEC vfsDst, WORD fl, LPARAM lParam, LPEXPANDVTBL lpExpandVtbl)
190 while (vn_last < vn_num)
192 if (pvnlist[vn_last] == NULL)
196 heap = GetProcessHeap();
197 if (vn_last == vn_num)
200 pvnlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, pvnlist,
201 sizeof(LPVIRTNODE *) * vn_num);
203 pvnlist[vn_last] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VIRTNODE));
204 lpvn = pvnlist[vn_last];
207 lpvn->cbSize = sizeof(VIRTNODE);
210 memcpy(&lpvn->vfsSrc, vfsSrc, sizeof(VCPFILESPEC));
213 memcpy(&lpvn->vfsDst, vfsDst, sizeof(VCPFILESPEC));
216 lpvn->lParam = lParam;
217 lpvn->lpExpandVtbl = lpExpandVtbl;
219 lpvn->vhstrDstFinalName = 0xffff; /* FIXME: what is this ? */
221 cbres = VCP_CALLBACK(lpvn, VCPM_NODECREATE, 0, 0, VCP_MsgRef);
222 lpvn->fl |= VFNL_CREATED;
223 cbres = VCP_CALLBACK(lpvn, VCPM_NODEACCEPT, 0, 0, VCP_MsgRef);
228 BOOL VCP_VirtnodeDelete(LPVIRTNODE lpvnDel)
233 for (n = 0; n < vn_last; n++)
235 if (pvnlist[n] == lpvnDel)
237 cbres = VCP_CALLBACK(lpvnDel, VCPM_NODEDESTROY, 0, 0, VCP_MsgRef);
238 HeapFree(GetProcessHeap(), 0, lpvnDel);
246 /***********************************************************************
247 * VcpOpen (SETUPX.200)
249 * Sets up a virtual copy operation.
250 * This means that functions such as GenInstall()
251 * create a VIRTNODE struct for every file to be touched in a .INF file
252 * instead of actually touching the file.
253 * The actual copy/move/rename gets started when VcpClose or
254 * VcpFlush is called; several different callbacks are made
255 * (copy, rename, open, close, version conflicts, ...) on every file copied.
257 RETERR16 WINAPI VcpOpen16(VIFPROC vifproc, LPARAM lparamMsgRef)
259 TRACE("(%p, %08lx)\n", vifproc, lparamMsgRef);
263 VCP_Proc = (FARPROC16)vifproc;
264 VCP_MsgRef = lparamMsgRef;
266 /* load SETUPAPI needed for dialog resources etc. */
267 SETUPAPI_hInstance = LoadLibraryA("setupapi.dll");
268 if (!SETUPAPI_hInstance)
270 ERR("Could not load sibling setupapi.dll\n");
271 return ERR_VCP_NOMEM;
277 /***********************************************************************
278 * VcpQueueCopy [SETUPX.13]
280 * lpExpandVtbl seems to be deprecated.
281 * fl are the CNFL_xxx and VNFL_xxx flags.
282 * lParam are the VNLP_xxx flags.
284 RETERR16 WINAPI VcpQueueCopy16(
285 LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName,
286 LPCSTR lpszSrcDir, LPCSTR lpszDstDir,
287 LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst,
288 LPEXPANDVTBL lpExpandVtbl,
289 WORD fl, LPARAM lParam
292 VCPFILESPEC vfsSrc, vfsDst;
295 return ERR_VCP_NOTOPEN;
297 TRACE("srcdir: %s, srcfile: %s, dstdir: %s, dstfile: %s\n",
298 lpszSrcDir, lpszSrcFileName, lpszDstDir, lpszDstFileName);
300 TRACE("ldidSrc == %d, ldidDst == %d\n", ldidSrc, ldidDst);
302 vfsSrc.ldid = ldidSrc;
303 vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir);
304 vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName);
306 vfsDst.ldid = ldidDst;
307 vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
308 vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
310 return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, fl, lParam,
314 /***********************************************************************
315 * VcpQueueDelete [SETUPX.17]
317 * Is lParamRef the same as lParam in VcpQueueCopy ?
318 * Damn docu !! Err... which docu ?
320 RETERR16 WINAPI VcpQueueDelete16(
321 LPCSTR lpszDstFileName,
330 return ERR_VCP_NOTOPEN;
332 vfsDst.ldid = ldidDst;
333 vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
334 vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
336 return VCP_VirtnodeCreate(NULL, &vfsDst, VNFL_DELETE, lParamRef, 0);
339 /***********************************************************************
340 * VcpQueueRename [SETUPX.204]
343 RETERR16 WINAPI VcpQueueRename16(
344 LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName,
345 LPCSTR lpszSrcDir, LPCSTR lpszDstDir,
346 LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst,
350 VCPFILESPEC vfsSrc, vfsDst;
353 return ERR_VCP_NOTOPEN;
355 vfsSrc.ldid = ldidSrc;
356 vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir);
357 vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName);
359 vfsDst.ldid = ldidDst;
360 vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
361 vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
363 return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, VNFL_RENAME, lParam,
367 /***********************************************************************
368 * VcpEnumFiles (SETUPX.@)
370 INT16 WINAPI VcpEnumFiles(VCPENUMPROC vep, LPARAM lParamRef)
374 for (n = 0; n < vn_last; n++)
375 vep(pvnlist[n], lParamRef);
377 return 0; /* FIXME: return value ? */
380 /***********************************************************************
381 * VcpExplain (SETUPX.411)
383 LPCSTR WINAPI VcpExplain16(LPVIRTNODE lpVn, DWORD dwWhat)
385 static char buffer[MAX_PATH]; /* FIXME: is this how it's done ? */
392 LPVCPFILESPEC lpvfs =
393 (dwWhat == VCPEX_SRC_FULL) ? &lpVn->vfsSrc : &lpVn->vfsDst;
395 /* if we have an ldid, use it, otherwise use the string */
396 /* from the vhstrlist array */
397 if (lpvfs->ldid != 0xffff)
398 CtlGetLddPath16(lpvfs->ldid, buffer);
400 strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrDir));
402 strcat(buffer, "\\");
403 strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrFileName));
407 FIXME("%ld unimplemented !\n", dwWhat);
408 strcpy(buffer, "Unknown error");
414 RETERR16 VCP_CheckPaths(void)
420 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATPATHCHECKSTART, 0, 0, VCP_MsgRef);
421 for (n = 0; n < vn_num; n++)
425 /* FIXME: check paths of all VIRTNODEs here ! */
426 cbres = VCP_CALLBACK(&lpvn->vfsDst, VCPM_CHECKPATH, 0, (DWORD)lpvn, VCP_MsgRef);
428 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATPATHCHECKEND, 0, 0, VCP_MsgRef);
432 RETERR16 VCP_CopyFiles(void)
434 char fn_src[MAX_PATH], fn_dst[MAX_PATH];
435 RETERR16 res = OK, cbres;
439 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATCOPYSTART, 0, 0, VCP_MsgRef);
440 for (n = 0; n < vn_num; n++)
443 if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_COPY)) continue;
444 /* FIXME: need to send VCPM_VSTATNEWDISK notification sometimes */
445 strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL));
446 strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL));
447 /* FIXME: what is this VCPM_VSTATWRITE here for ?
448 * I guess it's to signal successful destination file creation */
449 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef);
451 /* FIXME: need to do the file copy in small chunks for notifications */
452 TRACE("copying '%s' to '%s'\n", fn_src, fn_dst);
453 /* perform the file copy */
454 if (!(CopyFileA(fn_src, fn_dst, TRUE)))
456 ERR("error copying, src: %s -> dst: %s\n", fn_src, fn_dst);
457 res = ERR_VCP_IOFAIL;
460 vcp_status.prgFileRead.dwSoFar++;
461 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATREAD, 0, 0, VCP_MsgRef);
462 vcp_status.prgFileWrite.dwSoFar++;
463 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef);
466 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATCOPYEND, 0, 0, VCP_MsgRef);
470 /***********************************************************************
471 * VcpFlush - internal (not exported), but documented
473 * VNFL_NOW is used for VcpFlush.
475 RETERR16 VcpFlush16(WORD fl, LPCSTR lpszBackupDest)
480 /***********************************************************************
481 * VcpClose (SETUPX.201)
483 * Does callbacks (-> vifproc) with VCPM_VSTATCLOSESTART,
484 * VCPM_VSTATCLOSEEND.
486 * fl gets VCPFL_xxx flags to indicate what to do with the
487 * VIRTNODEs (files to mess with) created by e.g. GenInstall()
489 RETERR16 WINAPI VcpClose16(WORD fl, LPCSTR lpszBackupDest)
492 WORD cbres = VCPN_PROCEED;
494 TRACE("(%04x, '%s')\n", fl, lpszBackupDest);
496 /* FIXME: needs to sort virtnodes in case VCPFL_INSPECIFIEDORDER
497 * is not set. This is done by VCP_CALLBACK(VCPM_NODECOMPARE) */
500 memset(&vcp_status, 0, sizeof(VCPSTATUS));
501 /* yes, vcp_status.cbSize is 0 ! */
503 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATCLOSESTART, 0, 0, VCP_MsgRef);
506 res = VCP_CheckPaths();
509 return res; /* is this ok ? */
513 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATCLOSEEND, 0, 0, VCP_MsgRef);
516 FreeLibrary(SETUPAPI_hInstance);
521 RETERR16 VCP_RenameFiles(void)
523 char fn_src[MAX_PATH], fn_dst[MAX_PATH];
524 RETERR16 res = OK, cbres;
528 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATRENAMESTART, 0, 0, VCP_MsgRef);
529 for (n = 0; n < vn_num; n++)
532 if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_RENAME)) continue;
533 strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL));
534 strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL));
535 cbres = VCP_CALLBACK(&lpvn->vfsDst, VCPM_FILEOPENOUT, 0, (LPARAM)lpvn, VCP_MsgRef);
536 if (!(MoveFileExA(fn_src, fn_dst, MOVEFILE_REPLACE_EXISTING)))
537 res = ERR_VCP_IOFAIL;
539 VCP_VirtnodeDelete(lpvn);
541 cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATRENAMEEND, 0, 0, VCP_MsgRef);
545 /***********************************************************************
546 * vcpDefCallbackProc (SETUPX.202)
548 RETERR16 WINAPI vcpDefCallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam,
549 LPARAM lParam, LPARAM lParamRef)
551 static int count = 0;
553 FIXME("(%p, %04x, %04x, %08lx, %08lx) - what to do here ?\n",
554 lpvObj, uMsg, wParam, lParam, lParamRef);
559 /********************* point-and-click stuff from here ***********************/
561 static HWND hDlgCopy = 0;
562 static HKEY hKeyFiles = 0, hKeyRename = 0, hKeyConflict = 0;
563 static char BackupDir[12];
565 static BOOL CALLBACK VCP_UI_FileCopyDlgProc(HWND hWndDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
569 if (iMsg == WM_INITDIALOG)
571 ShowWindow(hWndDlg, SW_SHOWNORMAL);
572 UpdateWindow(hWndDlg);
578 BOOL VCP_UI_GetDialogTemplate(LPCVOID *template32)
580 HANDLE hResInfo, hDlgTmpl32;
582 if (!(hResInfo = FindResourceA(SETUPAPI_hInstance, MAKEINTRESOURCEA(COPYFILEDLGORD), RT_DIALOGA)))
584 if (!(hDlgTmpl32 = LoadResource(SETUPAPI_hInstance, hResInfo )) ||
585 !(*template32 = LockResource( hDlgTmpl32 )))
590 static LRESULT WINAPI
591 VCP_UI_FileCopyWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
593 if (uMsg != WM_CREATE)
594 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
601 FIXME("%04x: unhandled.\n", uMsg);
607 void VCP_UI_RegisterProgressClass(void)
609 static BOOL registered = FALSE;
616 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
617 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
618 wndClass.lpfnWndProc = (WNDPROC)VCP_UI_FileCopyWndProc;
619 wndClass.cbClsExtra = 0;
620 wndClass.cbWndExtra = 0;
621 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
622 wndClass.hbrBackground = (HBRUSH)NULL;
623 wndClass.lpszClassName = "setupx_progress";
625 RegisterClassA (&wndClass);
628 RETERR16 VCP_UI_NodeCompare(LPVIRTNODE vn1, LPVIRTNODE vn2)
631 file1 = vsmGetStringRawName16(vn1->vfsSrc.vhstrFileName);
632 file2 = vsmGetStringRawName16(vn2->vfsSrc.vhstrFileName);
633 return (RETERR16)strcmp(file1, file2);
636 RETERR16 VCP_UI_CopyStart(void)
639 char buf[256]; /* plenty */
643 /* FIXME: should be registered at DLL startup instead */
644 VCP_UI_RegisterProgressClass();
645 if (!(VCP_UI_GetDialogTemplate(&template32)))
648 hDlgCopy = CreateDialogIndirectParamA(SETUPAPI_hInstance, template32, 0,
649 VCP_UI_FileCopyDlgProc, 0);
652 SetDlgItemTextA(hDlgCopy, SOURCESTRORD, "Scanning ...");
653 SetDlgItemTextA(hDlgCopy, DESTSTRORD, "NOT_IMPLEMENTED_YET");
654 strcpy(buf, REG_INSTALLEDFILES);
655 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyFiles))
657 strcat(buf, REGPART_RENAME);
658 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyRename))
660 if (RegCreateKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT, &hKeyConflict))
663 if (!(RegQueryValueExA(hKeyConflict, "Dirty", NULL, 0, (LPBYTE)&dirty, &len)))
665 /* FIXME: what does SETUPX.DLL do in this case ? */
666 MESSAGE("Warning: another program using SETUPX is already running ! Failed.\n");
670 if (RegSetValueExA(hKeyConflict, "Dirty", 0, REG_BINARY, (LPBYTE)&dirty, 1))
673 if (!(RegQueryValueExA(hKeyConflict, "BackupDirectory", NULL, 0, BackupDir, &len)))
674 strcpy(BackupDir, "VCM");
676 /* create C:\WINDOWS\[BackupDir] and set registry key to it */
677 GetWindowsDirectoryA(buf, 256);
679 strcat(buf, BackupDir);
680 if (!(CreateDirectoryA(buf, NULL)))
682 if (RegSetValueExA(hKeyConflict, "BackupDirectory", 0, REG_SZ, (LPBYTE)buf, strlen(buf)+1))
684 RegCloseKey(hKeyConflict);
689 /***********************************************************************
690 * vcpUICallbackProc (SETUPX.213)
692 RETERR16 WINAPI vcpUICallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam,
693 LPARAM lParam, LPARAM lParamRef)
695 static int count = 0;
696 RETERR16 res = VCPN_OK, cbres;
699 FIXME("(%p, %04x, %04x, %08lx, %08lx) - semi-stub\n",
700 lpvObj, uMsg, wParam, lParam, lParamRef);
704 /* unused messages, it seems */
705 case VCPM_DISKPREPINFO:
707 case VCPM_FILENEEDED:
709 case VCPM_NODECREATE:
710 case VCPM_NODEACCEPT:
712 case VCPM_VSTATCLOSESTART:
713 case VCPM_VSTATPATHCHECKSTART:
714 case VCPM_VSTATPATHCHECKEND:
720 case VCPM_NODECOMPARE:
721 res = VCP_UI_NodeCompare((LPVIRTNODE)lpvObj, (LPVIRTNODE)lParam);
725 case VCPM_VSTATWRITE:
726 cbres = VCP_CALLBACK(&vcp_status, VCPM_DISKPREPINFO, 0, 0, VCP_MsgRef);
728 case VCPM_VSTATCLOSEEND:
729 RegCloseKey(hKeyFiles);
730 RegCloseKey(hKeyRename);
731 RegDeleteKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT);
733 case VCPM_VSTATCOPYSTART:
734 res = VCP_UI_CopyStart();
736 case VCPM_VSTATCOPYEND:
737 DestroyWindow(hDlgCopy);
740 FIXME("unhandled msg 0x%04x\n", uMsg);