Fix update procedure on Windows

by letting c4group wait for the Clonk process to terminate
Armin Burgmeier 2010-12-26 20:49:04 +01:00
parent b471c22204
commit f50972a9de
4 changed files with 71 additions and 13 deletions

View File

@ -42,8 +42,29 @@ bool C4Group_CopyEntry(C4Group *pFrom, C4Group *pTo, const char *strItemName)
return true;
}
bool C4Group_ApplyUpdate(C4Group &hGroup)
bool C4Group_ApplyUpdate(C4Group &hGroup, unsigned long ParentProcessID)
{
// Wait for parent process to terminate (so we can safely replace the executable)
#ifdef _WIN32
if(ParentProcessID)
{
HANDLE ParentProcess = OpenProcess(SYNCHRONIZE, FALSE, ParentProcessID);
if(ParentProcess)
{
// If we couldn't find a handle then either
// a) the process terminated already, which is great.
// b) OpenProcess() failed, which is not so great. But let's still try to do
// the update.
printf("Waiting for parent process to terminate...");
DWORD res = WaitForSingleObject(ParentProcess, 10000);
if(res == WAIT_TIMEOUT)
fprintf(stderr, "Parent process did not terminate after 10 seconds. Continuing...");
}
}
#else
// We could use waitpid on Unix, but we don't need that functionality there anyway...
#endif
// Process object update group (GRPUP_Entries.txt found)
C4UpdatePackage Upd;
if (hGroup.FindEntry(C4CFN_UpdateEntries))
@ -129,7 +150,7 @@ bool C4Group_ApplyUpdate(C4Group &hGroup)
while (hGroup.FindNextEntry("*.c4u", strEntry))
if (hChild.OpenAsChild(&hGroup, strEntry))
{
bool ok = C4Group_ApplyUpdate(hChild);
bool ok = C4Group_ApplyUpdate(hChild, 0);
hChild.Close();
// Failure on child update
if (!ok) return false;

View File

@ -72,6 +72,6 @@ protected:
void WriteLog(const char *strMsg, ...) GNUC_FORMAT_ATTRIBUTE_O;
};
bool C4Group_ApplyUpdate(C4Group &hGroup);
bool C4Group_ApplyUpdate(C4Group &hGroup, unsigned long ParentProcessID);
#endif

View File

@ -349,11 +349,29 @@ bool ProcessGroup(const char *FilenamePar)
// Apply an update
case 'y':
Log("Applying update...");
if (C4Group_ApplyUpdate(hGroup))
{ if (argv[iArg][2]=='d') fDeleteGroup = true; }
else
fprintf(stderr,"Update failed.\n");
{
Log("Applying update...");
bool success = false;
unsigned long pid = 0;
bool have_pid = false;
if(iArg + 1 < argc)
{
errno = 0;
pid = strtoul(argv[iArg+1], NULL, 10);
if(errno == 0)
have_pid = true;
else
pid = 0;
}
if(C4Group_ApplyUpdate(hGroup, pid))
{ if (argv[iArg][2]=='d') fDeleteGroup = true; }
else
fprintf(stderr,"Update failed.\n");
if(have_pid) ++iArg;
}
break;
#ifdef _DEBUG
case 'z':
@ -578,7 +596,7 @@ int main(int argc, char *argv[])
printf(" -p Pack -u Unpack -x Explode\n");
printf(" -k Print maker\n");
printf(" -g [source] [target] [title] Make update\n");
printf(" -y Apply update\n");
printf(" -y [ppid] Apply update (waiting for ppid to terminate first)\n");
printf("\n");
printf("Options: -v Verbose -r Recursive\n");
printf(" -i Register shell -u Unregister shell\n");

View File

@ -189,7 +189,7 @@ bool C4UpdateDlg::ApplyUpdate(const char *strUpdateFile, bool fDeleteUpdate, C4G
strUpdateProg += ".exe";
#endif
// Determine name of local extract of update program
StdStrBuf strUpdateProgEx; strUpdateProgEx.Copy(strUpdateProg);
StdStrBuf strUpdateProgEx; strUpdateProgEx.Copy(Config.AtExePath(strUpdateProg.getData()));
// Windows Vista/7: rename update program to setup.exe for UAC elevation and in temp path
if (IsWindowsWithUAC()) strUpdateProgEx.Copy(Config.AtTempPath("setup.exe"));
// Extract update program (the update should be applied using the new version)
@ -198,6 +198,8 @@ bool C4UpdateDlg::ApplyUpdate(const char *strUpdateFile, bool fDeleteUpdate, C4G
if (!UpdateGroup.Open(strUpdateFile)) return false;
// Look for update program at top level
if (!UpdateGroup.ExtractEntry(strUpdateProg.getData(), strUpdateProgEx.getData()))
return false;
#if 0
// ASK: What is this? Why should an update program not be found at the top
// level? This seems obsolete. - Newton
// Not found: look for an engine update pack one level down
@ -208,15 +210,32 @@ bool C4UpdateDlg::ApplyUpdate(const char *strUpdateFile, bool fDeleteUpdate, C4G
SubGroup.ExtractEntry(strUpdateProg.getData(), strUpdateProgEx.getData());
SubGroup.Close();
}
#endif
UpdateGroup.Close();
// Execute update program
Log(LoadResStr("IDS_PRC_LAUNCHINGUPDATE"));
succeeded = true;
#ifdef _WIN32
// Notice: even if the update program and update group are in the temp path, they must be executed in our working directory
StdStrBuf strUpdateArgs; strUpdateArgs.Format("\"%s\" /p -w \"" C4ENGINECAPTION "\" -w \"" C4EDITORCAPTION "\" -w 2000 %s", strUpdateFile, fDeleteUpdate ? "-yd" : "-y");
int iError = (intptr_t)ShellExecute(NULL, "open", strUpdateProgEx.getData(), strUpdateArgs.getData(), Config.General.ExePath, SW_SHOW);
if (iError <= 32) return false;
DWORD ProcessID = GetCurrentProcessId();
StdStrBuf strUpdateArgs, strTitle;
strUpdateArgs.Format("\"%s\" \"%s\" %s %lu", strUpdateProgEx.getData(), strUpdateFile, fDeleteUpdate ? "-yd" : "-y", (unsigned long)ProcessID);
STARTUPINFO startupInfo;
startupInfo.cb = sizeof(startupInfo);
startupInfo.lpReserved = NULL;
startupInfo.lpDesktop = NULL;
startupInfo.lpTitle = "Updating OpenClonk...";
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
startupInfo.wShowWindow = SW_SHOW;
startupInfo.cbReserved2 = 0;
startupInfo.lpReserved2 = NULL;
PROCESS_INFORMATION procInfo;
BOOL success = CreateProcess(strUpdateProgEx.getData(), strUpdateArgs.getMData(), NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, Config.General.ExePath, &startupInfo, &procInfo);
if(!success) return false;
//int iError = (intptr_t)ShellExecute(NULL, "open", strUpdateProgEx.getData(), strUpdateArgs.getData(), Config.General.ExePath, SW_SHOW);
//if (iError <= 32) return false;
// must quit ourselves for update program to work
if (succeeded) Application.Quit();
#else