From 11cd25ed6fe974a5d673683c76a8ffbcfd38185e Mon Sep 17 00:00:00 2001 From: Aleksey Bragin Date: Thu, 10 Jan 2008 13:33:25 +0000 Subject: [PATCH] Christoph Brill - Sync msiexec to Wine-20080105. See issue #2948 for more details. svn path=/trunk/; revision=31701 --- reactos/base/system/msiexec/msiexec.c | 164 +++++++++++++------ reactos/base/system/msiexec/msiexec.ico | Bin 0 -> 766 bytes reactos/base/system/msiexec/msiexec.rbuild | 2 + reactos/base/system/msiexec/rsrc.rc | 26 +++ reactos/base/system/msiexec/service.c | 174 +++++++++++++++++++++ reactos/base/system/msiexec/version.rc | 2 +- reactos/media/doc/README.WINE | 2 +- 7 files changed, 322 insertions(+), 48 deletions(-) create mode 100644 reactos/base/system/msiexec/msiexec.ico create mode 100644 reactos/base/system/msiexec/rsrc.rc create mode 100644 reactos/base/system/msiexec/service.c diff --git a/reactos/base/system/msiexec/msiexec.c b/reactos/base/system/msiexec/msiexec.c index 6342d851872..5e9afd93bc0 100644 --- a/reactos/base/system/msiexec/msiexec.c +++ b/reactos/base/system/msiexec/msiexec.c @@ -16,9 +16,11 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#define WIN32_LEAN_AND_MEAN + #include #include #include @@ -32,6 +34,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(msiexec); typedef HRESULT (WINAPI *DLLREGISTERSERVER)(void); typedef HRESULT (WINAPI *DLLUNREGISTERSERVER)(void); +DWORD DoService(void); + struct string_list { struct string_list *next; @@ -205,7 +209,7 @@ static DWORD msi_atou(LPCWSTR str) ret += (*str - '0'); str++; } - return 0; + return ret; } static LPWSTR msi_strdup(LPCWSTR str) @@ -224,32 +228,52 @@ static BOOL msi_strequal(LPCWSTR str1, LPCSTR str2) len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0); if( !len ) - return TRUE; + return FALSE; if( lstrlenW(str1) != (len-1) ) - return TRUE; + return FALSE; strW = (WCHAR*) HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len); MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len); ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len, strW, len); HeapFree(GetProcessHeap(), 0, strW); - return (ret != CSTR_EQUAL); + return (ret == CSTR_EQUAL); +} + +/* prefix is hyphen or dash, and str1 is the same as str2, ignoring case */ +static BOOL msi_option_equal(LPCWSTR str1, LPCSTR str2) +{ + if (str1[0] != '/' && str1[0] != '-') + return FALSE; + + /* skip over the hyphen or slash */ + return msi_strequal(str1 + 1, str2); } /* str2 is at the beginning of str1, ignoring case */ static BOOL msi_strprefix(LPCWSTR str1, LPCSTR str2) { - int len, ret; + DWORD len, ret; LPWSTR strW; len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0); if( !len ) - return TRUE; + return FALSE; if( lstrlenW(str1) < (len-1) ) - return TRUE; + return FALSE; strW = (WCHAR*) HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len); MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len); ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len-1, strW, len-1); HeapFree(GetProcessHeap(), 0, strW); - return (ret != CSTR_EQUAL); + return (ret == CSTR_EQUAL); +} + +/* prefix is hyphen or dash, and str2 is at the beginning of str1, ignoring case */ +static BOOL msi_option_prefix(LPCWSTR str1, LPCSTR str2) +{ + if (str1[0] != '/' && str1[0] != '-') + return FALSE; + + /* skip over the hyphen or slash */ + return msi_strprefix(str1 + 1, str2); } static VOID *LoadProc(LPCWSTR DllName, LPCSTR ProcName, HMODULE* DllHandle) @@ -314,6 +338,43 @@ static DWORD DoDllUnregisterServer(LPCWSTR DllName) return 0; } +static DWORD DoRegServer(void) +{ + SC_HANDLE scm, service; + CHAR path[MAX_PATH+12]; + DWORD ret = 0; + + scm = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CREATE_SERVICE); + if (!scm) + { + fprintf(stderr, "Failed to open the service control manager.\n"); + return 1; + } + + GetSystemDirectory(path, MAX_PATH); + lstrcatA(path, "\\msiexec.exe /V"); + + service = CreateServiceA(scm, "MSIServer", "MSIServer", GENERIC_ALL, + SERVICE_WIN32_SHARE_PROCESS, SERVICE_DEMAND_START, + SERVICE_ERROR_NORMAL, path, NULL, NULL, + NULL, NULL, NULL); + + if (service) CloseServiceHandle(service); + else if (GetLastError() != ERROR_SERVICE_EXISTS) + { + fprintf(stderr, "Failed to create MSI service\n"); + ret = 1; + } + CloseServiceHandle(scm); + return ret; +} + +static INT DoEmbedding( LPWSTR key ) +{ + printf("Remote custom actions are not supported yet\n"); + return 1; +} + /* * state machine to break up the command line properly */ @@ -426,6 +487,7 @@ static BOOL process_args_from_reg( LPWSTR ident, int *pargc, WCHAR ***pargv ) process_args(buf, pargc, pargv); ret = TRUE; } + HeapFree(GetProcessHeap(), 0, buf); } RegCloseKey(hkeyArgs); return ret; @@ -443,6 +505,7 @@ int main(int argc, char **argv) BOOL FunctionDllUnregisterServer = FALSE; BOOL FunctionRegServer = FALSE; BOOL FunctionUnregServer = FALSE; + BOOL FunctionServer = FALSE; BOOL FunctionUnknown = FALSE; LPWSTR PackageName = NULL; @@ -451,7 +514,7 @@ int main(int argc, char **argv) DWORD RepairMode = 0; - DWORD AdvertiseMode = 0; + DWORD_PTR AdvertiseMode = 0; struct string_list *transform_list = NULL; LANGID Language = 0; @@ -477,25 +540,28 @@ int main(int argc, char **argv) * We do that before starting to process the real commandline, * then overwrite the commandline again. */ - if(!msi_strequal(argvW[1], "/@")) + if(argc>1 && msi_option_equal(argvW[1], "@")) { if(!process_args_from_reg( argvW[2], &argc, &argvW )) return 1; } + if (argc == 3 && msi_option_equal(argvW[1], "Embedding")) + return DoEmbedding( argvW[2] ); + for(i = 1; i < argc; i++) { WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i])); - if (!msi_strequal(argvW[i], "/regserver")) + if (msi_option_equal(argvW[i], "regserver")) { FunctionRegServer = TRUE; } - else if (!msi_strequal(argvW[i], "/unregserver") || !msi_strequal(argvW[i], "/unregister")) + else if (msi_option_equal(argvW[i], "unregserver") || msi_option_equal(argvW[i], "unregister")) { FunctionUnregServer = TRUE; } - else if(!msi_strprefix(argvW[i], "/i")) + else if(msi_option_prefix(argvW[i], "i")) { LPWSTR argvWi = argvW[i]; FunctionInstall = TRUE; @@ -511,7 +577,7 @@ int main(int argc, char **argv) } PackageName = argvWi; } - else if(!msi_strequal(argvW[i], "/a")) + else if(msi_option_equal(argvW[i], "a")) { FunctionInstall = TRUE; FunctionInstallAdmin = TRUE; @@ -523,7 +589,7 @@ int main(int argc, char **argv) PackageName = argvW[i]; StringListAppend(&property_list, ActionAdmin); } - else if(!msi_strprefix(argvW[i], "/f")) + else if(msi_option_prefix(argvW[i], "f")) { int j; int len = lstrlenW(argvW[i]); @@ -591,17 +657,21 @@ int main(int argc, char **argv) WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i])); PackageName = argvW[i]; } - else if(!msi_strequal(argvW[i], "/x")) + else if(msi_option_prefix(argvW[i], "x")) { FunctionInstall = TRUE; + PackageName = argvW[i]+2; + if (!PackageName[0]) + { i++; if(i >= argc) ShowUsage(1); - WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i])); PackageName = argvW[i]; + } + WINE_TRACE("PackageName = %s\n", wine_dbgstr_w(PackageName)); StringListAppend(&property_list, RemoveAll); } - else if(!msi_strprefix(argvW[i], "/j")) + else if(msi_option_prefix(argvW[i], "j")) { int j; int len = lstrlenW(argvW[i]); @@ -629,7 +699,7 @@ int main(int argc, char **argv) WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i])); PackageName = argvW[i]; } - else if(!msi_strequal(argvW[i], "u")) + else if(msi_strequal(argvW[i], "u")) { FunctionAdvertise = TRUE; AdvertiseMode = ADVERTISEFLAGS_USERASSIGN; @@ -639,7 +709,7 @@ int main(int argc, char **argv) WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i])); PackageName = argvW[i]; } - else if(!msi_strequal(argvW[i], "m")) + else if(msi_strequal(argvW[i], "m")) { FunctionAdvertise = TRUE; AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN; @@ -649,7 +719,7 @@ int main(int argc, char **argv) WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i])); PackageName = argvW[i]; } - else if(!msi_strequal(argvW[i], "/t")) + else if(msi_option_equal(argvW[i], "t")) { i++; if(i >= argc) @@ -657,7 +727,7 @@ int main(int argc, char **argv) WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i])); StringListAppend(&transform_list, argvW[i]); } - else if(!msi_strequal(argvW[i], "/g")) + else if(msi_option_equal(argvW[i], "g")) { i++; if(i >= argc) @@ -665,7 +735,7 @@ int main(int argc, char **argv) WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i])); Language = msi_atou(argvW[i]); } - else if(!msi_strprefix(argvW[i], "/l")) + else if(msi_option_prefix(argvW[i], "l")) { int j; int len = lstrlenW(argvW[i]); @@ -756,7 +826,7 @@ int main(int argc, char **argv) ExitProcess(1); } } - else if(!msi_strequal(argvW[i], "/p")) + else if(msi_option_equal(argvW[i], "p")) { FunctionPatch = TRUE; i++; @@ -765,37 +835,38 @@ int main(int argc, char **argv) WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i])); PatchFileName = argvW[i]; } - else if(!msi_strprefix(argvW[i], "/q")) + else if(msi_option_prefix(argvW[i], "q")) { - if(lstrlenW(argvW[i]) == 2 || !msi_strequal(argvW[i]+2, "n")) + if(lstrlenW(argvW[i]) == 2 || msi_strequal(argvW[i]+2, "n") || + msi_strequal(argvW[i] + 2, "uiet")) { InstallUILevel = INSTALLUILEVEL_NONE; } - else if(!msi_strequal(argvW[i]+2, "b")) + else if(msi_strequal(argvW[i]+2, "b")) { InstallUILevel = INSTALLUILEVEL_BASIC; } - else if(!msi_strequal(argvW[i]+2, "r")) + else if(msi_strequal(argvW[i]+2, "r")) { InstallUILevel = INSTALLUILEVEL_REDUCED; } - else if(!msi_strequal(argvW[i]+2, "f")) + else if(msi_strequal(argvW[i]+2, "f")) { InstallUILevel = (INSTALLUILEVEL) (INSTALLUILEVEL_FULL|INSTALLUILEVEL_ENDDIALOG); } - else if(!msi_strequal(argvW[i]+2, "n+")) + else if(msi_strequal(argvW[i]+2, "n+")) { InstallUILevel = (INSTALLUILEVEL) (INSTALLUILEVEL_NONE|INSTALLUILEVEL_ENDDIALOG); } - else if(!msi_strequal(argvW[i]+2, "b+")) + else if(msi_strequal(argvW[i]+2, "b+")) { InstallUILevel = (INSTALLUILEVEL) (INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG); } - else if(!msi_strequal(argvW[i]+2, "b-")) + else if(msi_strequal(argvW[i]+2, "b-")) { InstallUILevel = (INSTALLUILEVEL) (INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY); } - else if(!msi_strequal(argvW[i]+2, "b+!")) + else if(msi_strequal(argvW[i]+2, "b+!")) { InstallUILevel = (INSTALLUILEVEL) (INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG); WINE_FIXME("Unknown modifier: !\n"); @@ -806,7 +877,7 @@ int main(int argc, char **argv) wine_dbgstr_w(argvW[i]+2)); } } - else if(!msi_strequal(argvW[i], "/y")) + else if(msi_option_equal(argvW[i], "y")) { FunctionDllRegisterServer = TRUE; i++; @@ -815,7 +886,7 @@ int main(int argc, char **argv) WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i])); DllName = argvW[i]; } - else if(!msi_strequal(argvW[i], "/z")) + else if(msi_option_equal(argvW[i], "z")) { FunctionDllUnregisterServer = TRUE; i++; @@ -824,29 +895,26 @@ int main(int argc, char **argv) WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i])); DllName = argvW[i]; } - else if(!msi_strequal(argvW[i], "/h") || !msi_strequal(argvW[i], "/?")) + else if(msi_option_equal(argvW[i], "h") || msi_option_equal(argvW[i], "?")) { ShowUsage(0); } - else if(!msi_strequal(argvW[i], "/m")) + else if(msi_option_equal(argvW[i], "m")) { FunctionUnknown = TRUE; WINE_FIXME("Unknown parameter /m\n"); } - else if(!msi_strequal(argvW[i], "/D")) + else if(msi_option_equal(argvW[i], "D")) { FunctionUnknown = TRUE; WINE_FIXME("Unknown parameter /D\n"); } - else if(strchrW(argvW[i], '=')) + else if (msi_option_equal(argvW[i], "V")) { - StringListAppend(&property_list, argvW[i]); + FunctionServer = TRUE; } else - { - FunctionInstall = TRUE; - PackageName = argvW[i]; - } + StringListAppend(&property_list, argvW[i]); } /* start the GUI */ @@ -891,12 +959,16 @@ int main(int argc, char **argv) } else if (FunctionRegServer) { - WINE_FIXME( "/regserver not implemented yet, ignoring\n" ); + ReturnCode = DoRegServer(); } else if (FunctionUnregServer) { WINE_FIXME( "/unregserver not implemented yet, ignoring\n" ); } + else if (FunctionServer) + { + ReturnCode = DoService(); + } else if (FunctionUnknown) { WINE_FIXME( "Unknown function, ignoring\n" ); diff --git a/reactos/base/system/msiexec/msiexec.ico b/reactos/base/system/msiexec/msiexec.ico new file mode 100644 index 0000000000000000000000000000000000000000..f6bea3c9bea64ee84909a4bf29e2d252d52ed57b GIT binary patch literal 766 zcmb_ZF>Zt~5FBSCQRJk9CM}dHk+wmixcn@Z_)sK1z*D#`Wy&i~x0%Hlfs`&;Gq%Sw z_O1;O*qWw<$JZ0^%JGe8h<763nLQAx{T`7bl^D~&o>NTI#L)#?KA8E8VDlyK{v%{K zoT7T5(zoPsTU%t=JW1BkO&%|c(&=ZSkPZ;`S?6{^CiLj2WTC27zv{@XRaKQ~L3L1< zvq45xU3fBx_S*0H(CM)7^+YB}{*{Yo^yAB%q3c^x!#S6%ZSdFKJ#RQ4Ih=p5ejl9g zsPnzdzjdU0?;qw~)fU&9HPrSX?kwPjf=Ok=j9~N00h@sj7UZ}P^^G^U1I2^jJ;bhr S#e(2XCLTOp%;qgsp8No9&Wy(Z literal 0 HcmV?d00001 diff --git a/reactos/base/system/msiexec/msiexec.rbuild b/reactos/base/system/msiexec/msiexec.rbuild index 4c5a7f0297a..a2141404bfe 100644 --- a/reactos/base/system/msiexec/msiexec.rbuild +++ b/reactos/base/system/msiexec/msiexec.rbuild @@ -15,5 +15,7 @@ ole32 msi msiexec.c + rsrc.rc + service.c version.rc diff --git a/reactos/base/system/msiexec/rsrc.rc b/reactos/base/system/msiexec/rsrc.rc new file mode 100644 index 00000000000..01e43befe19 --- /dev/null +++ b/reactos/base/system/msiexec/rsrc.rc @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2006 Mike McCormack + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include "version.rc" + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +/* @makedep: msiexec.ico */ +1 ICON DISCARDABLE msiexec.ico diff --git a/reactos/base/system/msiexec/service.c b/reactos/base/system/msiexec/service.c new file mode 100644 index 00000000000..64618b852f4 --- /dev/null +++ b/reactos/base/system/msiexec/service.c @@ -0,0 +1,174 @@ +/* + * msiexec.exe implementation + * + * Copyright 2007 Google (James Hawkins) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define WIN32_LEAN_AND_MEAN + +#include +#include + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msiexec); + +static SERVICE_STATUS_HANDLE hstatus; + +static HANDLE thread; +static HANDLE kill_event; + +void KillService(void) +{ + WINE_TRACE("Killing service\n"); + SetEvent(kill_event); +} + +static BOOL UpdateSCMStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, + DWORD dwServiceSpecificExitCode) +{ + SERVICE_STATUS status; + + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + status.dwCurrentState = dwCurrentState; + + if (dwCurrentState == SERVICE_START_PENDING) + status.dwControlsAccepted = 0; + else + { + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_PAUSE_CONTINUE | + SERVICE_ACCEPT_SHUTDOWN; + } + + if (dwServiceSpecificExitCode == 0) + { + status.dwWin32ExitCode = dwWin32ExitCode; + } + else + { + status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + } + + status.dwServiceSpecificExitCode = dwServiceSpecificExitCode; + status.dwCheckPoint = 0; + status.dwWaitHint = 0; + + if (!SetServiceStatus(hstatus, &status)) + { + fprintf(stderr, "Failed to set service status\n"); + KillService(); + return FALSE; + } + + return TRUE; +} + +static void WINAPI ServiceCtrlHandler(DWORD code) +{ + WINE_TRACE("%d\n", code); + + switch (code) + { + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + UpdateSCMStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); + KillService(); + return; + default: + fprintf(stderr, "Unhandled service control code: %d\n", code); + break; + } + + UpdateSCMStatus(SERVICE_RUNNING, NO_ERROR, 0); +} + +static DWORD WINAPI ServiceExecutionThread(LPVOID param) +{ + while (TRUE) + { + /* do nothing */ + } + + return 0; +} + +static BOOL StartServiceThread(void) +{ + DWORD id; + + thread = CreateThread(0, 0, ServiceExecutionThread, 0, 0, &id); + if (!thread) + { + fprintf(stderr, "Failed to create thread\n"); + return FALSE; + } + + return TRUE; +} + +static void WINAPI ServiceMain(DWORD argc, LPSTR *argv) +{ + hstatus = RegisterServiceCtrlHandlerA("MSIServer", ServiceCtrlHandler); + if (!hstatus) + { + fprintf(stderr, "Failed to register service ctrl handler\n"); + return; + } + + UpdateSCMStatus(SERVICE_START_PENDING, NO_ERROR, 0); + + kill_event = CreateEvent(0, TRUE, FALSE, 0); + if (!kill_event) + { + fprintf(stderr, "Failed to create event\n"); + KillService(); + return; + } + + if (!StartServiceThread()) + { + KillService(); + return; + } + + UpdateSCMStatus(SERVICE_RUNNING, NO_ERROR, 0); + + WaitForSingleObject(kill_event, INFINITE); + KillService(); +} + +DWORD DoService(void) +{ + char service_name[] = "MSIServer"; + + const SERVICE_TABLE_ENTRY service[] = + { + {service_name, ServiceMain}, + {NULL, NULL}, + }; + + WINE_TRACE("Starting MSIServer service\n"); + + if (!StartServiceCtrlDispatcher(service)) + { + fprintf(stderr, "Failed to start MSIServer service\n"); + return 1; + } + + return 0; +} diff --git a/reactos/base/system/msiexec/version.rc b/reactos/base/system/msiexec/version.rc index 60ae673af7e..077759431e2 100644 --- a/reactos/base/system/msiexec/version.rc +++ b/reactos/base/system/msiexec/version.rc @@ -13,7 +13,7 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #define WINE_FILEDESCRIPTION_STR "Wine Installer" diff --git a/reactos/media/doc/README.WINE b/reactos/media/doc/README.WINE index f779655d90b..7009b13093e 100644 --- a/reactos/media/doc/README.WINE +++ b/reactos/media/doc/README.WINE @@ -103,7 +103,7 @@ ReactOS shares the following programs with Winehq. reactos/base/applications/games/winemine # Out of sync reactos/base/applications/regedit # Out of sync reactos/base/system/expand # Out of sync -reactos/base/system/msiexec # Synced to Wine-0_9_3 +reactos/base/system/msiexec # Synced to Wine-20080105 In addition the following libs, dlls and source files are mostly based on code ported from Winehq CVS. If you are looking to update something in these files