最近看了凉宫春日的消失剧场版,情节很精彩,不过作为一个程序员,我对于影片中的长门有希的“紧急脱出プログラム”很感兴趣,看完电影就决定自己来实现这个小程序。但是没想到,在完成这个程序的时候,碰到了些难点,不过最后还是正常的实现了。现在在这里把这个程序的几个主要方法和大家分享。
首先,长门有希的那些メッセージ并不是一下打印到屏幕上的,而是一个一个字打印出来的。而且,每一段信息后还有停顿,为了实现这种输出方式,我写了如下函数
这个函数的第一个参数是要输出的字符串,第二,第三个参数分别是打印每个字的延时和打印每行字的延时,另外打印完当前字符串后是否换行,用一个布尔值n来确定。
void output(wstring str, int timea, int timeb,bool n) { for (string::size_type i = 0; i < str.size(); i++) { setlocale(LC_ALL ,"chs"); Sleep(timea); wprintf(L"%c",str[i]); } if (n) { printf("\n"); } Sleep(timeb); }
然后是输出那一段メッセージ,由于按照剧情,按下回车后会回到三年前的七夕的二十点三十分之后所以使用了事件处理函数
void printmessage() { wstring s; /**/ s = L" YUKI.N>これをあなたが読んでいる时、\n わたしはわたしではないだろう。"; output(s, 100, 3000,1); s = L" YUKI.N>このメッセージが表示されたということは、\n そこにはあなた、わたし、凉宫ハルヒ、朝比奈みくる、\n 古泉一树が存在しているはずである。"; output(s, 100, 3000,1); s = L" YUKI.N> それが键。\n あなたは解答を见つけ出した。"; output(s, 100, 3000,1); s = L" YUKI.N>これは紧急脱出プログラムである。\n 起动させる场合はエンターキーを、\n そうでない场合はそれ以外のキーを选択せよ。\n 起动させた场合、\n あなたは时空修正の机会を得る。\n ただし成功は保障できない。\n また帰还の保障もできない。"; output(s, 100, 3000,1); s = L" YUKI.N>このプログラムが起动するのは一度きりである。\n 実行ののち、消去される。\n 非実行が选択された场合は起动せずに消去される。\n Ready?"; output(s, 100, 3000,1); /**/ if (_getch()==13) { for (int k=1;k<20;k++) { system("color fc"); system("color 07"); } SYSTEMTIME st; GetLocalTime(&st); st.wYear -= 3; st.wMonth=7; st.wDay=7; st.wHour=20; st.wMinute=30; SetLocalTime(&st); exit(1); } else { s = L" YUKI.N>消去される。"; output(s, 100, 100,0); s = L"。。。。。"; output(s, 500, 3000,0); } }
接下来为了使输出窗口最大化的显示在屏幕上,用模拟按键的方式使输出窗口最大化
void FullScreen() { keybd_event(VK_MENU, 0x38, 0, 0); keybd_event(VK_RETURN, 0x1c, 0, 0); keybd_event(VK_RETURN, 0x1c, KEYEVENTF_KEYUP, 0); keybd_event(VK_MENU, 0x38, KEYEVENTF_KEYUP, 0); }
接下来就是我遇到问题的地方,由于在剧情中,那段メッセージ的最后说到,如果这个这个程序没有被确认,那么程序会删除。这就遇到了一个问题,如何让程序删除自己,其实实现程序自删的方法五花八门。有把程序完全拷贝到内存中,然后退出程序,用存在内存中的程序实现自删的,也有利用WINDOWS 批处理文件实现自删的。我是实用的后者,代码如下,我在关键的地方都加入了注释
void SelfDelete() { static char templ[] =":Repeat\r\n""del \"%s\"\r\n""if exist \"%s\" goto Repeat\r\n""rmdir %s \r\n""del \"%s\"" ; //bat文件主结构,删除程序后,连自己也删除。批处理文件是支持自删除的 static const char tempbatname[] = "_uninsep.bat" ;//批处理文件名 char modulename[MAX_PATH] ; char temppath[MAX_PATH] ; char folder[MAX_PATH] ; GetTempPathA(MAX_PATH, temppath) ; strcat(temppath, tempbatname) ; GetModuleFileNameA(NULL, modulename, MAX_PATH) ; strcpy (folder, modulename) ; char *pb = strrchr(folder, '\\'); if (pb != NULL) *pb = 0 ; //以上这段代码用于取得当前程序所在的路径以及文件名,最后把整个路径加文件名存储到folder变量中,并且取得当前系统的临时文件存放路径,存于temppath中 HANDLE hf ; hf = CreateFileA(temppath, GENERIC_WRITE, 0, NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL) ; if (hf != INVALID_HANDLE_VALUE) { DWORD len ; char *bat ; bat = (char*)alloca(strlen(templ) + strlen(modulename) * 2 + strlen(temppath) + 20) ; sprintf(bat, templ, modulename, modulename, folder, temppath) ; WriteFile(hf, bat, strlen(bat), &len, NULL) ; CloseHandle(hf) ; free(bat); //把之前取得的数据全部组合在一起,创建会删除本程序和自身的BAT文件 ShellExecuteA(NULL, "open", temppath, NULL, NULL, SW_HIDE); //通过ShellExecute执行这个文件,由于实用是窄字符编写,所以用ShellExecuteA } }
然后是主程序
int _tmain(int argc, _TCHAR* argv[]) { FullScreen(); system("color fc"); system("color 8c"); system("color 07"); Sleep(2000); printmessage(); SelfDelete(); exit(1); return 0; }