c++를 활용해 자동 업데이트 프로그램을 만들어야 하는 일이 생겨 구현해보고자 한다.
필요로 하는 프로그램은 다음과 같다.
업데이트 해야하는 프로그램이 웹 서버 혹은 로컬 저장소에 업로드 되어있는 상황이라고 가정
기존 프로그램의 버전과 서버 혹은 저장소에 있는 동일한 프로그램의 버전을 비교하여 상위 버전인 경우 기존 프로그램을 상위 버전의 프로그램으로 덮어 씌운 뒤 프로그램을 재실행 한다.
C++는 주로 사용하는 언어가 아니기 때문에 처음부터 구현하지는 않고 구현되어 있는 자료를 참고하여 구현해보려고 한다.
https://www.codeproject.com/Articles/1205548/An-efficient-way-for-automatic-updating
첫 번째 링크의 Micahel Haepahrati 라는 분이 작성하신 코드와 두 번째 링크의 블로거 분께서 작성해주신 코드 분석 내용을 참고하였다.
중요하다고 생각되는 부분의 코드만 참고하고 다뤄보았고 상세 코드의 경우 첫 번째 링크에서 다운받을 수 있다.
int _tmain(int argc, _TCHAR* argv[])
{
AutoUpdate au;
au.CheckForUpdates();
system("pause");
return 0;
}
프로그램의 실행 파일이다.
가장 중요한 역할을 하는 AutoUpdate 클래스를 생성하고 클래스 내부 메서드인 CheckForupdates()를 실행해준다.
AutoUpdate::AutoUpdate()
{
TCHAR szPath[MAX_PATH];
// 프로그램 실행 시 경로를 불러온다.
if (!GetModuleFileName(NULL, szPath, MAX_PATH))
{ // 경로 탐색 실패 시 오류 메시지 반환
wprintf(L"Can't find module file name (%s)\n", GetLastError());
return;
}
// 경로와 이름 별도 저장
SetSelfFullPath(szPath);
SetSelfFileName(GetFileNameFromPath(szPath));
// 프로그램 버전 확인
SG_Version ver; // 구조체 변수
if (SG_GetVersion(szPath, &ver))
{
CString ModifiedFileName = szPath;
AddNextVersionToFileName(ModifiedFileName, ver);
}
//
// 버전 업데이트 메서드
ReplaceTempVersion();
}
AutoUpdate 클래스의 생성자이다. GetModuleFileName이라는 C++ 내부 라이브러리를 통해 현재 실행중인 프로그램의 경로를 불러온다.
불러온 경로를 프로그램의 이름과 분리하여 저장해준 뒤 프로그램의 버전을 확인한다.
버전의 경우 자체적으로 구현한 SG_Version이라는 구조체에 저장하게 되는데
네자리의 정수 형태로 구성되어 있다.
BOOL AutoUpdate::ReplaceTempVersion()
{
int tries = 5;
// 임시 파일 여부 확인, 파일 이름 앞 U 여부로 판단
if (m_SelfFileName.Left(3) == L"U")
{
tempversion = true;
wprintf(L"We are running a temp version\n");
retry:;
// 기존 파일 삭제
BOOL result = DeleteFile(m_SelfFileName.Mid(3));
if (result)
{
wprintf(L"File '%s' deleted\n", m_SelfFileName.Mid(3));
// 임시 파일을 복사하여 기존 파일명으로 변경
BOOL result2 = CopyFile(m_SelfFileName, m_SelfFileName.Mid(3), FALSE);
if (result2)
{
wprintf(L"File '%s' copied to '%s'\n", m_SelfFileName, m_SelfFileName.Mid(3));
if (SG_Run(m_SelfFileName.Mid(3).GetBuffer()))
{
wprintf(L"Terminated %s\n",m_SelfFileName);
_exit(0);
}
}
}
else
{
if (--tries) goto retry;
wprintf(L"'original version' ('%s') can't be deleted or doesn't exists\n", m_SelfFileName.Mid(3));
}
}
else
{
tempversion = false;
wprintf(L"We are running the normal version\n");
retry2:;
BOOL result = DeleteFile(L"_U_"+ m_SelfFileName); // 임시 파일 삭제
if (result)
{
wprintf(L"temp File '%s' deleted\n", L"_U_" + m_SelfFileName);
}
else
{
if (--tries) goto retry2;
wprintf(L"temp File '%s' can't be deleted or doesn't exist\n", L"_U_" + m_SelfFileName);
}
}
return TRUE;
}
생성자 종료지점에서 실행하게 되는 메서드인 ReplaceTempVersion()이다.
우선 현재 파일의 임시 파일 여부를 확인한다. 업데이트 파일을 다운받아오는 상황에서 해당 파일이 현재 실행 파일이 아닌 임시 파일임을 알아볼 수 있도록 파일 이름 앞에 U를 붙여 저장하게 된다.
실행된 파일이 임시 파일인 경우 기존의 파일을 삭제한 뒤 업데이트 파일인 임시 파일을 기존 파일명으로 변경한 뒤 저장해준다.
실행된 파일이 임시 파일이 아닌 경우 임시 파일을 삭제하게 된다.
BOOL AutoUpdate::CheckForUpdates(void)
{
if (tempversion) return TRUE; // We don't check for updates if we are running a temp version
MyCallback pCallback;
// 업데이트 파일명은 U + 기존 파일명으로 설정
CString ExeName = L"U" + m_SelfFileName;
// 업데이트 파일 다운로드 경로
CString URL = m_DownloadLink + m_NextVersion;
wprintf(L"Next version will be %s\n", m_NextVersion);
if (m_NextVersion == L"") return FALSE;
wprintf(L"Looking for updates at %s\n", URL);
// 캐시 초기화
DeleteUrlCacheEntry(URL);
HRESULT hr = 0;
hr = URLDownloadToFile(
NULL, // A pointer to the controlling IUnknown interface (not needed here)
URL,
ExeName,0, // Reserved. Must be set to 0.
&pCallback); // status callback interface (not needed for basic use)
if (SUCCEEDED(hr))
{
// Check if the version string matches the file name on the server
SG_Version ver;
if (SG_GetVersion(ExeName.GetBuffer(), &ver))
{
if (SG_VersionMatch(m_NextVersion.GetBuffer(), &ver) == FALSE)
{
wprintf(L"Version string doesn't match actual version\n");
return FALSE;
}
}
wprintf(L"Downloaded file '%s' which is a newer version. Result = %u\n", m_NextVersion, hr);
if (SG_Run(ExeName.GetBuffer()))
{
wprintf(L"Successfully started the temp version (%s)\n", ExeName);
_exit(0);
}
else
{
wprintf(L"Couldn't start the temp version (%s)\n", ExeName);
}
}
else
wprintf(L"No new version (%s) on the server\n", m_NextVersion);
return (hr)?TRUE:FALSE;
}
실행 파일에서 실행하게 되는 AutoUpdate 클래스의 메서드인 CheckForUpdates 메서드이다.
업데이트 할 파일의 경로를 저장한 뒤 URLDownLoadToFile() 라는 내부 라이브러리 함수를 통해 파일을 다운받게 된다.
업데이트 할 파일의 다운로드가 완료되면 해당 파일의 버전을 확인한 뒤 해당 파일을 실행하게 된다.
업데이트 할 파일을 웹 서버를 통해 다운받은 뒤 기존 실행중인 프로그램을 대체하는 방식의 프로그램이다.
해당 프로그램을 조금 수정하여 웹 서버가 아닌 로컬 저장소를 통해 업데이트 파일을 저장받도록 수정하려고 한다. 또한 프로그램에서 이해가 되지 않는 부분들이 존재하여 새롭게 이해하게 되는 부분이 있으면 코드를 좀 더 수정해보려고 한다.
안녕하세요! 분석글 잘 읽었습니다:) 다름이아니라 분석하신 동일한 소스를 사용해서 자동업데이트를 구현하려하는데 사용법을 잘 모르겠어서요..도움을 받을 수 있을지 여쭈어봅니다ㅜㅜ