• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

FFFTPのソースコードです。


Commit MetaInfo

Revision6794c0ad103fd74579a3afc0911fef11e89db271 (tree)
Zeit2014-06-08 00:23:41
Autors_kawamoto <s_kawamoto@user...>
Commiters_kawamoto

Log Message

Add routines to check for software updates.

Ändern Zusammenfassung

Diff

Binary files a/FFFTP_Eng_Release/FFFTP.exe and b/FFFTP_Eng_Release/FFFTP.exe differ
Binary files a/Release/FFFTP.exe and b/Release/FFFTP.exe differ
--- a/common.h
+++ b/common.h
@@ -78,6 +78,10 @@
7878 #endif
7979 #define VER_NUM 1990 /* 設定バージョン */
8080 #define PROGRAM_VERSION_NUM 1990 /* バージョン */
81+// ソフトウェア自動更新
82+// リリースバージョンはリリース予定年(10進数4桁)+月(2桁)+日(2桁)+通し番号(0スタート2桁)とする
83+// 2014年7月31日中の30個目のリリースは2014073129
84+#define RELEASE_VERSION_NUM 2014061500 /* リリースバージョン */
8185
8286
8387 // SourceForge.JPによるフォーク
--- a/main.c
+++ b/main.c
@@ -372,13 +372,16 @@ int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLi
372372 {
373373 if(!StartUpdateProcessAsAdministrator(lpszCmdLine, " --restart"))
374374 {
375- ApplyUpdates(UpdateDir);
375+ if(ApplyUpdates(UpdateDir))
376+ MessageBox(NULL, MSGJPN359, "FFFTP", MB_OK);
377+ else
378+ MessageBox(NULL, MSGJPN360, "FFFTP", MB_OK);
376379 }
377380 return 0;
378381 }
379382 else if(GetTokenAfterOption(lpszCmdLine, UpdateDir, "--software-cleanup", "--software-cleanup"))
380383 {
381- // TODO: ダウンロードした更新ファイルを削除
384+ CleanupUpdates(UpdateDir);
382385 }
383386
384387 // マルチコアCPUの特定環境下でファイル通信中にクラッシュするバグ対策
--- a/mbswrapper.c
+++ b/mbswrapper.c
@@ -2438,6 +2438,30 @@ END_ROUTINE
24382438 return r;
24392439 }
24402440
2441+BOOL CreateDirectoryM(LPCSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
2442+{
2443+ BOOL r = FALSE;
2444+ wchar_t* pw0 = NULL;
2445+START_ROUTINE
2446+ pw0 = DuplicateMtoW(lpPathName, -1);
2447+ r = CreateDirectoryW(pw0, lpSecurityAttributes);
2448+END_ROUTINE
2449+ FreeDuplicatedString(pw0);
2450+ return r;
2451+}
2452+
2453+BOOL RemoveDirectoryM(LPCSTR lpPathName)
2454+{
2455+ BOOL r = FALSE;
2456+ wchar_t* pw0 = NULL;
2457+START_ROUTINE
2458+ pw0 = DuplicateMtoW(lpPathName, -1);
2459+ r = RemoveDirectoryW(pw0);
2460+END_ROUTINE
2461+ FreeDuplicatedString(pw0);
2462+ return r;
2463+}
2464+
24412465 int mkdirM(const char * _Path)
24422466 {
24432467 int r = -1;
--- a/mbswrapper.h
+++ b/mbswrapper.h
@@ -182,6 +182,12 @@ BOOL MoveFileM(LPCSTR lpExistingFileName, LPCSTR lpNewFileName);
182182 #undef DeleteFile
183183 #define DeleteFile DeleteFileM
184184 BOOL DeleteFileM(LPCSTR lpFileName);
185+#undef CreateDirectory
186+#define CreateDirectory CreateDirectoryM
187+BOOL CreateDirectoryM(LPCSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes);
188+#undef RemoveDirectory
189+#define RemoveDirectory RemoveDirectoryM
190+BOOL RemoveDirectoryM(LPCSTR lpPathName);
185191 #undef mkdir
186192 #define mkdir _mkdirM
187193 int mkdirM(const char * _Path);
--- a/mesg-eng.h
+++ b/mesg-eng.h
@@ -357,6 +357,8 @@
357357 #define MSGJPN356 _Tu8("Move to &parent folder...", "Move to &parent folder...")
358358 #define MSGJPN357 _Tu8("XML file\0*.xml\0All file\0*\0", "XML file\0*.xml\0All file\0*\0")
359359 #define MSGJPN358 _Tu8("Failed to export the settings.\nPlease change saving path or format.", "Failed to export the settings.\nPlease change saving path or format.")
360+#define MSGJPN359 _Tu8("Software update has been completed.", "Software update has been completed.")
361+#define MSGJPN360 _Tu8("Failed to update the software.\nPlease get the latest version from our web site and update it manually.", "Failed to update the software.\nPlease get the latest version from our web site and update it manually.")
360362 #if defined(HAVE_TANDEM)
361363 #define MSGJPN2000 _Tu8("NonStop Server", "NonStop Server")
362364 #define MSGJPN2001 _Tu8("OSS<->GUARDIAN Switch(&O)", "OSS<->GUARDIAN Switch(&O)")
--- a/mesg-jpn.h
+++ b/mesg-jpn.h
@@ -357,6 +357,8 @@
357357 #define MSGJPN356 _Tu8("一つ上のフォルダへ移動(&P)...", "\xE4\xB8\x80\xE3\x81\xA4\xE4\xB8\x8A\xE3\x81\xAE\xE3\x83\x95\xE3\x82\xA9\xE3\x83\xAB\xE3\x83\x80\xE3\x81\xB8\xE7\xA7\xBB\xE5\x8B\x95(&P)...")
358358 #define MSGJPN357 _Tu8("XMLファイル\0*.xml\0全てのファイル\0*\0", "XML\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB\0*.xml\0\xE5\x85\xA8\xE3\x81\xA6\xE3\x81\xAE\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB\0*\0")
359359 #define MSGJPN358 _Tu8("設定のエクスポートに失敗しました.\n保存する場所や形式を変更してください.", "\xE8\xA8\xAD\xE5\xAE\x9A\xE3\x81\xAE\xE3\x82\xA8\xE3\x82\xAF\xE3\x82\xB9\xE3\x83\x9D\xE3\x83\xBC\xE3\x83\x88\xE3\x81\xAB\xE5\xA4\xB1\xE6\x95\x97\xE3\x81\x97\xE3\x81\xBE\xE3\x81\x97\xE3\x81\x9F.\n\xE4\xBF\x9D\xE5\xAD\x98\xE3\x81\x99\xE3\x82\x8B\xE5\xA0\xB4\xE6\x89\x80\xE3\x82\x84\xE5\xBD\xA2\xE5\xBC\x8F\xE3\x82\x92\xE5\xA4\x89\xE6\x9B\xB4\xE3\x81\x97\xE3\x81\xA6\xE3\x81\x8F\xE3\x81\xA0\xE3\x81\x95\xE3\x81\x84.")
360+#define MSGJPN359 _Tu8("ソフトウェアの更新が完了しました.", "\xE3\x82\xBD\xE3\x83\x95\xE3\x83\x88\xE3\x82\xA6\xE3\x82\xA7\xE3\x82\xA2\xE3\x81\xAE\xE6\x9B\xB4\xE6\x96\xB0\xE3\x81\x8C\xE5\xAE\x8C\xE4\xBA\x86\xE3\x81\x97\xE3\x81\xBE\xE3\x81\x97\xE3\x81\x9F.")
361+#define MSGJPN360 _Tu8("ソフトウェアの更新に失敗しました.\nWebサイトから最新版を入手して手動で更新してください.", "\xE3\x82\xBD\xE3\x83\x95\xE3\x83\x88\xE3\x82\xA6\xE3\x82\xA7\xE3\x82\xA2\xE3\x81\xAE\xE6\x9B\xB4\xE6\x96\xB0\xE3\x81\xAB\xE5\xA4\xB1\xE6\x95\x97\xE3\x81\x97\xE3\x81\xBE\xE3\x81\x97\xE3\x81\x9F.\nWeb\xE3\x82\xB5\xE3\x82\xA4\xE3\x83\x88\xE3\x81\x8B\xE3\x82\x89\xE6\x9C\x80\xE6\x96\xB0\xE7\x89\x88\xE3\x82\x92\xE5\x85\xA5\xE6\x89\x8B\xE3\x81\x97\xE3\x81\xA6\xE6\x89\x8B\xE5\x8B\x95\xE3\x81\xA7\xE6\x9B\xB4\xE6\x96\xB0\xE3\x81\x97\xE3\x81\xA6\xE3\x81\x8F\xE3\x81\xA0\xE3\x81\x95\xE3\x81\x84.")
360362 #if defined(HAVE_TANDEM)
361363 #define MSGJPN2000 _Tu8("NonStop Server", "NonStop Server")
362364 #define MSGJPN2001 _Tu8("OSS<->GUARDIAN 切り替え(&O)", "OSS<->GUARDIAN \xE5\x88\x87\xE3\x82\x8A\xE6\x9B\xBF\xE3\x81\x88(&O)")
--- a/updater.c
+++ b/updater.c
@@ -1,6 +1,7 @@
11 // updater.c
22 // Copyright (C) 2014 Suguru Kawamoto
33 // ソフトウェア自動更新
4+// コードの再利用のため表記はwchar_t型だが実体はchar型でUTF-8
45
56 #include <tchar.h>
67 #include <ws2tcpip.h>
@@ -21,7 +22,26 @@ typedef struct
2122 BYTE ListHash[64];
2223 } UPDATE_HASH;
2324
24-BOOL DownloadFileViaHTTP(void* pOut, DWORD Length, DWORD* pLength, LPCWSTR UserAgent, LPCWSTR ServerName, LPCWSTR ObjectName)
25+#define UPDATE_LIST_FILE_FLAG_DIRECTORY 0x00000001
26+
27+typedef struct
28+{
29+ DWORD Flags;
30+ CHAR SrcPath[128];
31+ BYTE SrcHash[64];
32+ CHAR DstPath[128];
33+ FILETIME Timestamp;
34+} UPDATE_LIST_FILE;
35+
36+typedef struct
37+{
38+ DWORD Version;
39+ CHAR VersionString[32];
40+ DWORD FileCount;
41+ UPDATE_LIST_FILE File[1];
42+} UPDATE_LIST;
43+
44+BOOL ReadFileViaHTTPW(void* pOut, DWORD Length, DWORD* pLength, LPCWSTR UserAgent, LPCWSTR ServerName, LPCWSTR ObjectName)
2545 {
2646 BOOL bResult;
2747 HINTERNET hSession;
@@ -57,17 +77,99 @@ BOOL DownloadFileViaHTTP(void* pOut, DWORD Length, DWORD* pLength, LPCWSTR UserA
5777 return bResult;
5878 }
5979
80+BOOL ReadFileViaHTTP(void* pOut, DWORD Length, DWORD* pLength, LPCSTR UserAgent, LPCSTR ServerName, LPCSTR ObjectName)
81+{
82+ BOOL r;
83+ wchar_t* pw0;
84+ wchar_t* pw1;
85+ wchar_t* pw2;
86+ pw0 = DuplicateMtoW(UserAgent, -1);
87+ pw1 = DuplicateMtoW(ServerName, -1);
88+ pw2 = DuplicateMtoW(ObjectName, -1);
89+ r = ReadFileViaHTTPW(pOut, Length, pLength, pw0, pw1, pw2);
90+ FreeDuplicatedString(pw0);
91+ FreeDuplicatedString(pw1);
92+ FreeDuplicatedString(pw2);
93+ return r;
94+}
95+
96+BOOL SaveMemoryToFileWithTimestamp(LPCTSTR FileName, void* pData, DWORD Size, FILETIME* pTimestamp)
97+{
98+ BOOL bResult;
99+ HANDLE hFile;
100+ bResult = FALSE;
101+ if((hFile = CreateFile(FileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
102+ {
103+ if(WriteFile(hFile, pData, Size, &Size, NULL))
104+ {
105+ if(SetFileTime(hFile, NULL, NULL, pTimestamp))
106+ bResult = TRUE;
107+ }
108+ CloseHandle(hFile);
109+ }
110+ return bResult;
111+}
112+
113+BOOL CopyAllFilesInDirectory(LPCTSTR From, LPCTSTR To)
114+{
115+ BOOL bResult;
116+ TCHAR* pFrom;
117+ TCHAR* pTo;
118+ SHFILEOPSTRUCT fop;
119+ bResult = FALSE;
120+ if(pFrom = (TCHAR*)malloc(sizeof(TCHAR) * (_tcslen(From) + _tcslen(_T("\\*")) + 2)))
121+ {
122+ _tcscpy(pFrom, From);
123+ _tcsncpy(pFrom + _tcslen(pFrom), _T("\\*"), _tcslen(_T("\\*")) + 2);
124+ if(pTo = (TCHAR*)malloc(sizeof(TCHAR) * (_tcslen(To) + 2)))
125+ {
126+ _tcsncpy(pTo, To, _tcslen(To) + 2);
127+ memset(&fop, 0, sizeof(SHFILEOPSTRUCT));
128+ fop.wFunc = FO_COPY;
129+ fop.pFrom = pFrom;
130+ fop.pTo = pTo;
131+ fop.fFlags = FOF_NO_UI;
132+ if(SHFileOperation(&fop) == 0)
133+ bResult = TRUE;
134+ free(pTo);
135+ }
136+ free(pFrom);
137+ }
138+ return bResult;
139+}
140+
141+BOOL DeleteDirectoryAndContents(LPCTSTR Path)
142+{
143+ BOOL bResult;
144+ TCHAR* pFrom;
145+ SHFILEOPSTRUCT fop;
146+ bResult = FALSE;
147+ if(pFrom = (TCHAR*)malloc(sizeof(TCHAR) * (_tcslen(Path) + 2)))
148+ {
149+ _tcsncpy(pFrom, Path, _tcslen(Path) + 2);
150+ memset(&fop, 0, sizeof(SHFILEOPSTRUCT));
151+ fop.wFunc = FO_DELETE;
152+ fop.pFrom = pFrom;
153+ fop.fFlags = FOF_NO_UI;
154+ if(SHFileOperation(&fop) == 0)
155+ bResult = TRUE;
156+ free(pFrom);
157+ }
158+ return bResult;
159+}
160+
60161 // FFFTPの更新情報を確認
61-BOOL CheckForUpdates(BOOL bDownload, LPCTSTR DownloadDir)
162+BOOL CheckForUpdates(BOOL bDownload, LPCTSTR DownloadDir, DWORD* pVersion, LPTSTR pVersionString)
62163 {
63164 BOOL bResult;
64165 DWORD Length;
65- BYTE Buf1[4096];
166+ BYTE Buf1[65536];
66167 BYTE Buf2[1024];
67168 UPDATE_HASH UpdateHash;
68169 BYTE Hash[64];
170+ UPDATE_LIST* pUpdateList;
69171 bResult = FALSE;
70- if(DownloadFileViaHTTP(&Buf1, sizeof(Buf1), &Length, HTTP_USER_AGENT, UPDATE_SERVER, UPDATE_HASH_PATH))
172+ if(ReadFileViaHTTP(&Buf1, sizeof(Buf1), &Length, HTTP_USER_AGENT, UPDATE_SERVER, UPDATE_HASH_PATH))
71173 {
72174 if(DecryptSignature(UPDATE_RSA_PUBLIC_KEY, &Buf1, Length, &Buf2, sizeof(Buf2), &Length))
73175 {
@@ -76,15 +178,23 @@ BOOL CheckForUpdates(BOOL bDownload, LPCTSTR DownloadDir)
76178 memcpy(&UpdateHash, &Buf2, sizeof(UPDATE_HASH));
77179 if(memcmp(&UpdateHash.Signature, UPDATE_SIGNATURE, 64) == 0)
78180 {
79- if(DownloadFileViaHTTP(&Buf1, sizeof(Buf1), &Length, HTTP_USER_AGENT, UPDATE_SERVER, UPDATE_LIST_PATH))
181+ if(ReadFileViaHTTP(&Buf1, sizeof(Buf1), &Length, HTTP_USER_AGENT, UPDATE_SERVER, UPDATE_LIST_PATH))
80182 {
81183 GetHashSHA512(&Buf1, Length, &Hash);
82184 if(memcmp(&Hash, &UpdateHash.ListHash, 64) == 0)
83185 {
84- // TODO: 更新情報を解析
85- bResult = TRUE;
86- if(bDownload)
87- bResult = PrepareUpdates(&Buf1, Length, DownloadDir);
186+ if(Length >= sizeof(UPDATE_LIST))
187+ {
188+ bResult = TRUE;
189+ pUpdateList = (UPDATE_LIST*)&Buf1;
190+ if(pUpdateList->Version > *pVersion)
191+ {
192+ *pVersion = pUpdateList->Version;
193+ _tcscpy(pVersionString, pUpdateList->VersionString);
194+ }
195+ if(bDownload)
196+ bResult = PrepareUpdates(&Buf1, Length, DownloadDir);
197+ }
88198 }
89199 }
90200 }
@@ -98,9 +208,58 @@ BOOL CheckForUpdates(BOOL bDownload, LPCTSTR DownloadDir)
98208 BOOL PrepareUpdates(void* pList, DWORD ListLength, LPCTSTR DownloadDir)
99209 {
100210 BOOL bResult;
211+ UPDATE_LIST* pUpdateList;
212+ void* pBuf;
213+ DWORD i;
214+ BOOL b;
215+ DWORD Length;
216+ BYTE Hash[64];
217+ TCHAR Path[MAX_PATH];
101218 bResult = FALSE;
102- // TODO: 更新情報を解析
103- // TODO: 更新するファイルをダウンロード
219+ if(ListLength >= sizeof(UPDATE_LIST))
220+ {
221+ pUpdateList = (UPDATE_LIST*)pList;
222+ if((pUpdateList->FileCount - 1) * sizeof(UPDATE_LIST_FILE) + sizeof(UPDATE_LIST) >= ListLength)
223+ {
224+ bResult = TRUE;
225+ DeleteDirectoryAndContents(DownloadDir);
226+ CreateDirectory(DownloadDir, NULL);
227+ pBuf = malloc(16777216);
228+ for(i = 0; i < pUpdateList->FileCount; i++)
229+ {
230+ b = FALSE;
231+ if(pUpdateList->File[i].Flags & UPDATE_LIST_FILE_FLAG_DIRECTORY)
232+ {
233+ _tcscpy(Path, DownloadDir);
234+ _tcscat(Path, _T("\\"));
235+ _tcscat(Path, pUpdateList->File[i].DstPath);
236+ if(CreateDirectory(Path, NULL))
237+ b = TRUE;
238+ }
239+ if(strlen(pUpdateList->File[i].SrcPath) > 0)
240+ {
241+ if(ReadFileViaHTTP(pBuf, 16777216, &Length, HTTP_USER_AGENT, UPDATE_SERVER, pUpdateList->File[i].SrcPath))
242+ {
243+ GetHashSHA512(pBuf, Length, &Hash);
244+ if(memcmp(&Hash, &pUpdateList->File[i].SrcHash, 64) == 0)
245+ {
246+ _tcscpy(Path, DownloadDir);
247+ _tcscat(Path, _T("\\"));
248+ _tcscat(Path, pUpdateList->File[i].DstPath);
249+ if(SaveMemoryToFileWithTimestamp(Path, pBuf, Length, &pUpdateList->File[i].Timestamp))
250+ b = TRUE;
251+ }
252+ }
253+ }
254+ if(!b)
255+ {
256+ bResult = FALSE;
257+ break;
258+ }
259+ }
260+ free(pBuf);
261+ }
262+ }
104263 return bResult;
105264 }
106265
@@ -108,8 +267,40 @@ BOOL PrepareUpdates(void* pList, DWORD ListLength, LPCTSTR DownloadDir)
108267 BOOL ApplyUpdates(LPCTSTR DestinationDir)
109268 {
110269 BOOL bResult;
270+ TCHAR Source[MAX_PATH];
271+ TCHAR Backup[MAX_PATH];
272+ TCHAR* p;
111273 bResult = FALSE;
112- // TODO:
274+ if(GetModuleFileName(NULL, Source, MAX_PATH) > 0)
275+ {
276+ if(p = _tcsrchr(Source, _T('\\')))
277+ *p = _T('\0');
278+ _tcscpy(Backup, Source);
279+ _tcscat(Backup, _T("\\updatebackup"));
280+ DeleteDirectoryAndContents(Backup);
281+ if(CopyAllFilesInDirectory(DestinationDir, Backup))
282+ {
283+ if(CopyAllFilesInDirectory(Source, DestinationDir))
284+ {
285+ bResult = TRUE;
286+ _tcscpy(Backup, DestinationDir);
287+ _tcscat(Backup, _T("\\updatebackup"));
288+ DeleteDirectoryAndContents(Backup);
289+ }
290+ else
291+ CopyAllFilesInDirectory(Backup, DestinationDir);
292+ }
293+ }
294+ return bResult;
295+}
296+
297+// 更新するファイルをダウンロード
298+BOOL CleanupUpdates(LPCTSTR DownloadDir)
299+{
300+ BOOL bResult;
301+ bResult = FALSE;
302+ if(DeleteDirectoryAndContents(DownloadDir))
303+ bResult = TRUE;
113304 return bResult;
114305 }
115306
--- a/updater.h
+++ b/updater.h
@@ -7,10 +7,15 @@
77
88 #include <windows.h>
99
10-#define HTTP_USER_AGENT L"Mozilla/4.0"
11-#define UPDATE_SERVER L"ffftp.sourceforge.jp"
12-#define UPDATE_HASH_PATH L"/update/hash"
13-#define UPDATE_LIST_PATH L"/update/list"
10+#define HTTP_USER_AGENT "Mozilla/4.0"
11+#define UPDATE_SERVER "ffftp.sourceforge.jp"
12+#if defined(_M_IX86)
13+#define UPDATE_HASH_PATH "/update/hash"
14+#define UPDATE_LIST_PATH "/update/list"
15+#elif defined(_M_AMD64)
16+#define UPDATE_HASH_PATH "/update/amd64/hash"
17+#define UPDATE_LIST_PATH "/update/amd64/list"
18+#endif
1419 #define UPDATE_RSA_PUBLIC_KEY \
1520 "-----BEGIN PUBLIC KEY-----\n" \
1621 "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsVo13yricPHxkQypqiMy\n" \
@@ -28,9 +33,10 @@
2833 "-----END PUBLIC KEY-----\n"
2934 #define UPDATE_SIGNATURE "\x4C\x2A\x8E\x57\xAB\x75\x0C\xB5\xDA\x5F\xFE\xB9\x57\x9A\x1B\xA2\x7A\x61\x32\xF8\xFA\x4B\x61\xE2\xBA\x20\x9C\x37\xD5\x0A\xDC\x94\x10\x4D\x02\x30\x9B\xCD\x01\x9B\xB8\x73\x1E\xDB\xFD\xD7\x45\xCA\xE0\x8E\xF9\xB0\x1F\xB4\x0D\xD8\xFB\xE8\x41\x48\xE7\xF5\xE8\x64"
3035
31-BOOL CheckForUpdates(BOOL bDownload, LPCTSTR DownloadDir);
36+BOOL CheckForUpdates(BOOL bDownload, LPCTSTR DownloadDir, DWORD* pVersion, LPTSTR pVersionString);
3237 BOOL PrepareUpdates(void* pList, DWORD ListLength, LPCTSTR DownloadDir);
3338 BOOL ApplyUpdates(LPCTSTR DestinationDir);
39+BOOL CleanupUpdates(LPCTSTR DownloadDir);
3440 BOOL StartUpdateProcess(LPCTSTR CommandLine, LPCTSTR Keyword);
3541 BOOL StartUpdateProcessAsAdministrator(LPCTSTR CommandLine, LPCTSTR Keyword);
3642