2 * msiexec.exe implementation
4 * Copyright 2004 Vincent Béron
5 * Copyright 2005 Mike McCormack
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include "wine/debug.h"
28 #include "wine/unicode.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(msiexec
);
32 typedef HRESULT (WINAPI
*DLLREGISTERSERVER
)(void);
33 typedef HRESULT (WINAPI
*DLLUNREGISTERSERVER
)(void);
37 struct string_list
*next
;
41 static const char UsageStr
[] =
43 " Install a product:\n"
44 " msiexec {package|productcode} [property]\n"
45 " msiexec /i {package|productcode} [property]\n"
46 " msiexec /a package [property]\n"
47 " Repair an installation:\n"
48 " msiexec /f[p|o|e|d|c|a|u|m|s|v] {package|productcode}\n"
49 " Uninstall a product:\n"
50 " msiexec /x {package|productcode} [property]\n"
51 " Advertise a product:\n"
52 " msiexec /j[u|m] package [/t transform] [/g languageid]\n"
53 " msiexec {u|m} package [/t transform] [/g languageid]\n"
55 " msiexec /p patchpackage [property]\n"
56 " msiexec /p patchpackage /a package [property]\n"
57 " Modifiers for above operations:\n"
58 " msiexec /l[*][i|w|e|a|r|u|c|m|o|p|v|][+|!] logfile\n"
59 " msiexec /q{|n|b|r|f|n+|b+|b-}\n"
60 " Register a module:\n"
61 " msiexec /y module\n"
62 " Unregister a module:\n"
63 " msiexec /z module\n"
64 " Display usage and copyright:\n"
66 "NOTE: Product code on commandline unimplemented as of yet\n"
68 "Copyright 2004 Vincent Béron\n";
70 static const WCHAR ActionAdmin
[] = {
71 'A','C','T','I','O','N','=','A','D','M','I','N',0 };
72 static const WCHAR RemoveAll
[] = {
73 'R','E','M','O','V','E','=','A','L','L',0 };
75 static const WCHAR InstallRunOnce
[] = {
76 'S','o','f','t','w','a','r','e','\\',
77 'M','i','c','r','o','s','o','f','t','\\',
78 'W','i','n','d','o','w','s','\\',
79 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
80 'I','n','s','t','a','l','l','e','r','\\',
81 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
83 static void ShowUsage(int ExitCode
)
86 ExitProcess(ExitCode
);
89 static BOOL
IsProductCode(LPWSTR str
)
93 if(lstrlenW(str
) != 38)
95 return ( (CLSIDFromString(str
, &ProductCode
) == NOERROR
) );
99 static VOID
StringListAppend(struct string_list
**list
, LPCWSTR str
)
101 struct string_list
*entry
;
104 size
= sizeof *entry
+ lstrlenW(str
) * sizeof (WCHAR
);
105 entry
= HeapAlloc(GetProcessHeap(), 0, size
);
108 WINE_ERR("Out of memory!\n");
111 lstrcpyW(entry
->str
, str
);
115 * Ignoring o(n^2) time complexity to add n strings for simplicity,
116 * add the string to the end of the list to preserve the order.
119 list
= &(*list
)->next
;
123 static LPWSTR
build_properties(struct string_list
*property_list
)
125 struct string_list
*list
;
126 LPWSTR ret
, p
, value
;
133 /* count the space we need */
135 for(list
= property_list
; list
; list
= list
->next
)
136 len
+= lstrlenW(list
->str
) + 3;
138 ret
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
140 /* add a space before each string, and quote the value */
142 for(list
= property_list
; list
; list
= list
->next
)
144 value
= strchrW(list
->str
,'=');
147 len
= value
- list
->str
;
149 memcpy(p
, list
->str
, len
* sizeof(WCHAR
));
153 /* check if the value contains spaces and maybe quote it */
155 needs_quote
= strchrW(value
,' ') ? 1 : 0;
158 len
= lstrlenW(value
);
159 memcpy(p
, value
, len
* sizeof(WCHAR
));
166 WINE_TRACE("properties -> %s\n", wine_dbgstr_w(ret
) );
171 static LPWSTR
build_transforms(struct string_list
*transform_list
)
173 struct string_list
*list
;
177 /* count the space we need */
179 for(list
= transform_list
; list
; list
= list
->next
)
180 len
+= lstrlenW(list
->str
) + 1;
182 ret
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
184 /* add all the transforms with a semicolon between each one */
186 for(list
= transform_list
; list
; list
= list
->next
)
188 len
= lstrlenW(list
->str
);
189 lstrcpynW(p
, list
->str
, len
);
199 static DWORD
msi_atou(LPCWSTR str
)
202 while(*str
>= '0' && *str
<= '9')
211 static LPWSTR
msi_strdup(LPCWSTR str
)
213 DWORD len
= lstrlenW(str
)+1;
214 LPWSTR ret
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
219 /* str1 is the same as str2, ignoring case */
220 static BOOL
msi_strequal(LPCWSTR str1
, LPCSTR str2
)
225 len
= MultiByteToWideChar( CP_ACP
, 0, str2
, -1, NULL
, 0);
228 if( lstrlenW(str1
) != (len
-1) )
230 strW
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
231 MultiByteToWideChar( CP_ACP
, 0, str2
, -1, strW
, len
);
232 ret
= CompareStringW(GetThreadLocale(), NORM_IGNORECASE
, str1
, len
, strW
, len
);
233 HeapFree(GetProcessHeap(), 0, strW
);
234 return (ret
!= CSTR_EQUAL
);
237 /* str2 is at the beginning of str1, ignoring case */
238 static BOOL
msi_strprefix(LPCWSTR str1
, LPCSTR str2
)
243 len
= MultiByteToWideChar( CP_ACP
, 0, str2
, -1, NULL
, 0);
246 if( lstrlenW(str1
) < (len
-1) )
248 strW
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
249 MultiByteToWideChar( CP_ACP
, 0, str2
, -1, strW
, len
);
250 ret
= CompareStringW(GetThreadLocale(), NORM_IGNORECASE
, str1
, len
-1, strW
, len
-1);
251 HeapFree(GetProcessHeap(), 0, strW
);
252 return (ret
!= CSTR_EQUAL
);
255 static VOID
*LoadProc(LPCWSTR DllName
, LPCSTR ProcName
, HMODULE
* DllHandle
)
259 *DllHandle
= LoadLibraryExW(DllName
, NULL
, LOAD_WITH_ALTERED_SEARCH_PATH
);
262 fprintf(stderr
, "Unable to load dll %s\n", wine_dbgstr_w(DllName
));
265 proc
= (VOID
*) GetProcAddress(*DllHandle
, ProcName
);
268 fprintf(stderr
, "Dll %s does not implement function %s\n",
269 wine_dbgstr_w(DllName
), ProcName
);
270 FreeLibrary(*DllHandle
);
277 static DWORD
DoDllRegisterServer(LPCWSTR DllName
)
280 DLLREGISTERSERVER pfDllRegisterServer
= NULL
;
281 HMODULE DllHandle
= NULL
;
283 pfDllRegisterServer
= LoadProc(DllName
, "DllRegisterServer", &DllHandle
);
285 hr
= pfDllRegisterServer();
288 fprintf(stderr
, "Failed to register dll %s\n", wine_dbgstr_w(DllName
));
291 printf("Successfully registered dll %s\n", wine_dbgstr_w(DllName
));
293 FreeLibrary(DllHandle
);
297 static DWORD
DoDllUnregisterServer(LPCWSTR DllName
)
300 DLLUNREGISTERSERVER pfDllUnregisterServer
= NULL
;
301 HMODULE DllHandle
= NULL
;
303 pfDllUnregisterServer
= LoadProc(DllName
, "DllUnregisterServer", &DllHandle
);
305 hr
= pfDllUnregisterServer();
308 fprintf(stderr
, "Failed to unregister dll %s\n", wine_dbgstr_w(DllName
));
311 printf("Successfully unregistered dll %s\n", wine_dbgstr_w(DllName
));
313 FreeLibrary(DllHandle
);
318 * state machine to break up the command line properly
328 static int chomp( WCHAR
*str
)
330 enum chomp_state state
= cs_whitespace
;
332 int count
= 0, ignore
;
334 for( p
= str
, out
= str
; *p
; p
++ )
362 state
= cs_whitespace
;
390 static void process_args( WCHAR
*cmdline
, int *pargc
, WCHAR
***pargv
)
392 WCHAR
**argv
, *p
= msi_strdup(cmdline
);
396 argv
= HeapAlloc(GetProcessHeap(), 0, sizeof (WCHAR
*)*(n
+1));
400 p
+= lstrlenW(p
) + 1;
408 static BOOL
process_args_from_reg( LPWSTR ident
, int *pargc
, WCHAR
***pargv
)
411 HKEY hkey
= 0, hkeyArgs
= 0;
412 DWORD sz
= 0, type
= 0;
416 r
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, InstallRunOnce
, &hkey
);
417 if(r
!= ERROR_SUCCESS
)
419 r
= RegQueryValueExW(hkey
, ident
, 0, &type
, 0, &sz
);
420 if(r
== ERROR_SUCCESS
&& type
== REG_SZ
)
422 buf
= HeapAlloc(GetProcessHeap(), 0, sz
);
423 r
= RegQueryValueExW(hkey
, ident
, 0, &type
, (LPBYTE
)buf
, &sz
);
424 if( r
== ERROR_SUCCESS
)
426 process_args(buf
, pargc
, pargv
);
430 RegCloseKey(hkeyArgs
);
434 int main(int argc
, char **argv
)
437 BOOL FunctionInstall
= FALSE
;
438 BOOL FunctionInstallAdmin
= FALSE
;
439 BOOL FunctionRepair
= FALSE
;
440 BOOL FunctionAdvertise
= FALSE
;
441 BOOL FunctionPatch
= FALSE
;
442 BOOL FunctionDllRegisterServer
= FALSE
;
443 BOOL FunctionDllUnregisterServer
= FALSE
;
444 BOOL FunctionRegServer
= FALSE
;
445 BOOL FunctionUnregServer
= FALSE
;
446 BOOL FunctionUnknown
= FALSE
;
448 LPWSTR PackageName
= NULL
;
449 LPWSTR Properties
= NULL
;
450 struct string_list
*property_list
= NULL
;
452 DWORD RepairMode
= 0;
454 DWORD AdvertiseMode
= 0;
455 LPWSTR Transforms
= NULL
;
456 struct string_list
*transform_list
= NULL
;
460 LPWSTR LogFileName
= NULL
;
461 DWORD LogAttributes
= 0;
463 LPWSTR PatchFileName
= NULL
;
464 INSTALLTYPE InstallType
= INSTALLTYPE_DEFAULT
;
466 INSTALLUILEVEL InstallUILevel
= INSTALLUILEVEL_FULL
;
468 LPWSTR DllName
= NULL
;
470 LPWSTR
*argvW
= NULL
;
472 /* overwrite the command line */
473 process_args( GetCommandLineW(), &argc
, &argvW
);
476 * If the args begin with /@ IDENT then we need to load the real
477 * command line out of the RunOnceEntries key in the registry.
478 * We do that before starting to process the real commandline,
479 * then overwrite the commandline again.
481 if(!msi_strequal(argvW
[1], "/@"))
483 if(!process_args_from_reg( argvW
[2], &argc
, &argvW
))
487 for(i
= 1; i
< argc
; i
++)
489 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
491 if (!msi_strequal(argvW
[i
], "/regserver"))
493 FunctionRegServer
= TRUE
;
495 else if (!msi_strequal(argvW
[i
], "/unregserver") || !msi_strequal(argvW
[i
], "/unregister"))
497 FunctionUnregServer
= TRUE
;
499 else if(!msi_strprefix(argvW
[i
], "/i"))
501 LPWSTR argvWi
= argvW
[i
];
502 FunctionInstall
= TRUE
;
503 if(lstrlenW(argvWi
) > 2)
510 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
513 PackageName
= argvWi
;
515 else if(!msi_strequal(argvW
[i
], "/a"))
517 FunctionInstall
= TRUE
;
518 FunctionInstallAdmin
= TRUE
;
519 InstallType
= INSTALLTYPE_NETWORK_IMAGE
;
523 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
524 PackageName
= argvW
[i
];
525 StringListAppend(&property_list
, ActionAdmin
);
527 else if(!msi_strprefix(argvW
[i
], "/f"))
530 int len
= lstrlenW(argvW
[i
]);
531 FunctionRepair
= TRUE
;
532 for(j
= 2; j
< len
; j
++)
538 RepairMode
|= REINSTALLMODE_FILEMISSING
;
542 RepairMode
|= REINSTALLMODE_FILEOLDERVERSION
;
546 RepairMode
|= REINSTALLMODE_FILEEQUALVERSION
;
550 RepairMode
|= REINSTALLMODE_FILEEXACT
;
554 RepairMode
|= REINSTALLMODE_FILEVERIFY
;
558 RepairMode
|= REINSTALLMODE_FILEREPLACE
;
562 RepairMode
|= REINSTALLMODE_USERDATA
;
566 RepairMode
|= REINSTALLMODE_MACHINEDATA
;
570 RepairMode
|= REINSTALLMODE_SHORTCUT
;
574 RepairMode
|= REINSTALLMODE_PACKAGE
;
577 fprintf(stderr
, "Unknown option \"%c\" in Repair mode\n", argvW
[i
][j
]);
583 RepairMode
= REINSTALLMODE_FILEMISSING
|
584 REINSTALLMODE_FILEEQUALVERSION
|
585 REINSTALLMODE_FILEVERIFY
|
586 REINSTALLMODE_MACHINEDATA
|
587 REINSTALLMODE_SHORTCUT
;
592 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
593 PackageName
= argvW
[i
];
595 else if(!msi_strequal(argvW
[i
], "/x"))
597 FunctionInstall
= TRUE
;
601 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
602 PackageName
= argvW
[i
];
603 StringListAppend(&property_list
, RemoveAll
);
605 else if(!msi_strprefix(argvW
[i
], "/j"))
608 int len
= lstrlenW(argvW
[i
]);
609 FunctionAdvertise
= TRUE
;
610 for(j
= 2; j
< len
; j
++)
616 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
620 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
623 fprintf(stderr
, "Unknown option \"%c\" in Advertise mode\n", argvW
[i
][j
]);
630 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
631 PackageName
= argvW
[i
];
633 else if(!msi_strequal(argvW
[i
], "u"))
635 FunctionAdvertise
= TRUE
;
636 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
640 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
641 PackageName
= argvW
[i
];
643 else if(!msi_strequal(argvW
[i
], "m"))
645 FunctionAdvertise
= TRUE
;
646 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
650 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
651 PackageName
= argvW
[i
];
653 else if(!msi_strequal(argvW
[i
], "/t"))
658 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
659 StringListAppend(&transform_list
, argvW
[i
]);
661 else if(!msi_strprefix(argvW
[i
], "TRANSFORMS="))
663 StringListAppend(&transform_list
, argvW
[i
]+11);
665 else if(!msi_strequal(argvW
[i
], "/g"))
670 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
671 Language
= msi_atou(argvW
[i
]);
673 else if(!msi_strprefix(argvW
[i
], "/l"))
676 int len
= lstrlenW(argvW
[i
]);
677 for(j
= 2; j
< len
; j
++)
683 LogMode
|= INSTALLLOGMODE_INFO
;
687 LogMode
|= INSTALLLOGMODE_WARNING
;
691 LogMode
|= INSTALLLOGMODE_ERROR
;
695 LogMode
|= INSTALLLOGMODE_ACTIONSTART
;
699 LogMode
|= INSTALLLOGMODE_ACTIONDATA
;
703 LogMode
|= INSTALLLOGMODE_USER
;
707 LogMode
|= INSTALLLOGMODE_COMMONDATA
;
711 LogMode
|= INSTALLLOGMODE_FATALEXIT
;
715 LogMode
|= INSTALLLOGMODE_OUTOFDISKSPACE
;
719 LogMode
|= INSTALLLOGMODE_PROPERTYDUMP
;
723 LogMode
|= INSTALLLOGMODE_VERBOSE
;
726 LogMode
= INSTALLLOGMODE_FATALEXIT
|
727 INSTALLLOGMODE_ERROR
|
728 INSTALLLOGMODE_WARNING
|
729 INSTALLLOGMODE_USER
|
730 INSTALLLOGMODE_INFO
|
731 INSTALLLOGMODE_RESOLVESOURCE
|
732 INSTALLLOGMODE_OUTOFDISKSPACE
|
733 INSTALLLOGMODE_ACTIONSTART
|
734 INSTALLLOGMODE_ACTIONDATA
|
735 INSTALLLOGMODE_COMMONDATA
|
736 INSTALLLOGMODE_PROPERTYDUMP
|
737 INSTALLLOGMODE_PROGRESS
|
738 INSTALLLOGMODE_INITIALIZE
|
739 INSTALLLOGMODE_TERMINATE
|
740 INSTALLLOGMODE_SHOWDIALOG
;
743 LogAttributes
|= INSTALLLOGATTRIBUTES_APPEND
;
746 LogAttributes
|= INSTALLLOGATTRIBUTES_FLUSHEACHLINE
;
755 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
756 LogFileName
= argvW
[i
];
757 if(MsiEnableLogW(LogMode
, LogFileName
, LogAttributes
) != ERROR_SUCCESS
)
759 fprintf(stderr
, "Logging in %s (0x%08lx, %lu) failed\n",
760 wine_dbgstr_w(LogFileName
), LogMode
, LogAttributes
);
764 else if(!msi_strequal(argvW
[i
], "/p"))
766 FunctionPatch
= TRUE
;
770 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
771 PatchFileName
= argvW
[i
];
773 else if(!msi_strprefix(argvW
[i
], "/q"))
775 if(lstrlenW(argvW
[i
]) == 2 || !msi_strequal(argvW
[i
]+2, "n"))
777 InstallUILevel
= INSTALLUILEVEL_NONE
;
779 else if(!msi_strequal(argvW
[i
]+2, "b"))
781 InstallUILevel
= INSTALLUILEVEL_BASIC
;
783 else if(!msi_strequal(argvW
[i
]+2, "r"))
785 InstallUILevel
= INSTALLUILEVEL_REDUCED
;
787 else if(!msi_strequal(argvW
[i
]+2, "f"))
789 InstallUILevel
= INSTALLUILEVEL_FULL
|INSTALLUILEVEL_ENDDIALOG
;
791 else if(!msi_strequal(argvW
[i
]+2, "n+"))
793 InstallUILevel
= INSTALLUILEVEL_NONE
|INSTALLUILEVEL_ENDDIALOG
;
795 else if(!msi_strequal(argvW
[i
]+2, "b+"))
797 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_ENDDIALOG
;
799 else if(!msi_strequal(argvW
[i
]+2, "b-"))
801 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_PROGRESSONLY
;
803 else if(!msi_strequal(argvW
[i
]+2, "b+!"))
805 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_ENDDIALOG
;
806 WINE_FIXME("Unknown modifier: !\n");
810 fprintf(stderr
, "Unknown option \"%s\" for UI level\n",
811 wine_dbgstr_w(argvW
[i
]+2));
814 else if(!msi_strequal(argvW
[i
], "/y"))
816 FunctionDllRegisterServer
= TRUE
;
820 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
823 else if(!msi_strequal(argvW
[i
], "/z"))
825 FunctionDllUnregisterServer
= TRUE
;
829 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
832 else if(!msi_strequal(argvW
[i
], "/h") || !msi_strequal(argvW
[i
], "/?"))
836 else if(!msi_strequal(argvW
[i
], "/m"))
838 FunctionUnknown
= TRUE
;
839 WINE_FIXME("Unknown parameter /m\n");
841 else if(!msi_strequal(argvW
[i
], "/D"))
843 FunctionUnknown
= TRUE
;
844 WINE_FIXME("Unknown parameter /D\n");
846 else if(strchrW(argvW
[i
], '='))
848 StringListAppend(&property_list
, argvW
[i
]);
852 FunctionInstall
= TRUE
;
853 PackageName
= argvW
[i
];
858 MsiSetInternalUI(InstallUILevel
, NULL
);
860 Properties
= build_properties( property_list
);
861 Transforms
= build_transforms( transform_list
);
863 if(FunctionInstallAdmin
&& FunctionPatch
)
864 FunctionInstall
= FALSE
;
869 if(IsProductCode(PackageName
))
870 ReturnCode
= MsiConfigureProductExW(PackageName
, 0, INSTALLSTATE_DEFAULT
, Properties
);
872 ReturnCode
= MsiInstallProductW(PackageName
, Properties
);
874 else if(FunctionRepair
)
876 if(IsProductCode(PackageName
))
877 WINE_FIXME("Product code treatment not implemented yet\n");
879 ReturnCode
= MsiReinstallProductW(PackageName
, RepairMode
);
881 else if(FunctionAdvertise
)
883 ReturnCode
= MsiAdvertiseProductW(PackageName
, (LPWSTR
) AdvertiseMode
, Transforms
, Language
);
885 else if(FunctionPatch
)
887 ReturnCode
= MsiApplyPatchW(PatchFileName
, PackageName
, InstallType
, Properties
);
889 else if(FunctionDllRegisterServer
)
891 ReturnCode
= DoDllRegisterServer(DllName
);
893 else if(FunctionDllUnregisterServer
)
895 ReturnCode
= DoDllUnregisterServer(DllName
);
897 else if (FunctionRegServer
)
899 WINE_FIXME( "/regserver not implemented yet, ignoring\n" );
901 else if (FunctionUnregServer
)
903 WINE_FIXME( "/unregserver not implemented yet, ignoring\n" );
905 else if (FunctionUnknown
)
907 WINE_FIXME( "Unknown function, ignoring\n" );