今天在寫的程序的時(shí)候,遇到字符串比較.
但是字符串可能會(huì)有大小寫的區(qū)別,此時(shí)怎么辦呢?忽略大小寫進(jìn)行比較.
利用wcsicmp就比較好, 它將字符串轉(zhuǎn)換成小寫字符串進(jìn)行比較,這樣就忽略了大小寫的情況.
轉(zhuǎn)自: http://blog.csdn.net/iihero/archive/2009/02/21/3918137.aspx
標(biāo) 題: 贈(zèng)與今年的大學(xué)畢業(yè)生
○胡 適(1932年6月27日)
本文是胡適先生1932年6月27日所作。雖然30年代那個(gè)血雨腥風(fēng)的時(shí)代已經(jīng)過去,現(xiàn)在的時(shí)代已經(jīng)與當(dāng)時(shí)不可同日而語,但是,讀來還是感覺受益匪淺,胡適先生的諄諄教導(dǎo)之情溢于言表。本文中,胡適先生認(rèn)為,大學(xué)生畢業(yè)有三條路可走:繼續(xù)做學(xué)術(shù)研究;尋著相當(dāng)?shù)穆殬I(yè);做官,辦黨,革命。文中分析了大學(xué)畢業(yè)后遇到的“陷阱墮落的方式”,并給出了三個(gè)方子。
這一兩個(gè)星期里,各地的大學(xué)都有畢業(yè)的班次,都有很多的畢業(yè)生離開學(xué)校去開始他們的成人事業(yè)。
學(xué)生的生活是一種享有特殊優(yōu)待的生活,不妨幼稚一點(diǎn),不妨吵吵鬧鬧,社會(huì)都能縱容他們,不肯嚴(yán)格地要他們負(fù)行為的責(zé)任?,F(xiàn)在他們要撐起自己的肩膀來挑他們自己的擔(dān)子了。在這個(gè)國難最緊急的年頭,他們的擔(dān)子真不輕!我們祝他們的成功,同時(shí)也不忍不依據(jù)自己的經(jīng)驗(yàn),贈(zèng)他們幾句送行的贈(zèng)言——雖未必是救命毫毛,也許做個(gè)防身的錦囊罷!
你們畢業(yè)之后,可走的路不出這幾條:絕少數(shù)的人還可以在國內(nèi)或國外的研究院繼續(xù)做學(xué)術(shù)研究;少數(shù)的人可以尋著相當(dāng)?shù)穆殬I(yè);此外還有做官,辦黨,革命三條路;再有就是在家享福或者失業(yè)親居了。
走其余幾條路的人,都不能沒有墮落的危險(xiǎn)。墮落的方式很多,總括起來,約有這兩大類:
第一是容易拋棄學(xué)生時(shí)代求知識(shí)的欲望。你們到了實(shí)際社會(huì)里,往往學(xué)非所用,往往所學(xué)全無用處,往往可以完全用不著學(xué)問,而一樣可以胡亂混飯吃,混官做。在這種環(huán)境里即使向來抱有求知識(shí)學(xué)問的人,也不免心灰意懶,把求知的欲望漸漸冷淡下去。況且學(xué)問是要有相當(dāng)?shù)脑O(shè)備的:書籍,實(shí)驗(yàn)室,師友的切磋指導(dǎo),閑暇的工夫,都不是一個(gè)平常要糊口養(yǎng)家的人能容易辦到的。沒有做學(xué)問的環(huán)境,又誰能怪我們拋棄學(xué)問呢?
第二是容易拋棄學(xué)生時(shí)代理想的人生的追求。少年人初次和冷酷的社會(huì)接觸,容易感覺理想與事實(shí)相去太遠(yuǎn),容易發(fā)生悲觀和失望。多年懷抱的人生理想,改造的熱誠,奮斗的勇氣,到此時(shí)候,好像全不是那么一回事了。渺小的個(gè)人在那強(qiáng)烈的社會(huì)爐火里,往往經(jīng)不起長時(shí)期的烤煉就熔化了,一點(diǎn)高尚的理想不久就幻滅了。抱著改造社會(huì)的夢(mèng)想而來,往往是棄甲拋兵而走,或者做了惡勢(shì)的俘虜。你在那牢獄里,回想那少年氣壯時(shí)代的種種理想主義,好像都成了自誤誤人的迷夢(mèng)!從此以后,你就甘心放棄理想人生的追求,甘心做現(xiàn)在社會(huì)的順民了。要防御這兩方面的墮落,一面要保持我們求知識(shí)的欲望,一面要保持我們對(duì)人生的追求。
有什么好方子呢?依我個(gè)人的觀察和經(jīng)驗(yàn),有三種防身的藥方是值得一試的。
第一個(gè)方子只有一句話:“總得時(shí)時(shí)尋一兩個(gè)值得研究的問題!”問題是知識(shí)學(xué)問的老祖宗:古往今來一切知識(shí)的產(chǎn)生與積聚,都是因?yàn)橐獯饐栴}——要解答實(shí)用上的困難和理論上的疑難。所謂“為知識(shí)而求知識(shí)”,其實(shí)也只是一種好奇心追求某種問題的解答,不過因?yàn)槟欠N問題的性質(zhì)不必是直接應(yīng)用的,人們就覺得這是無所謂的求知識(shí)了。
我們出學(xué)校之后,離開了做學(xué)問的環(huán)境,如果沒有一兩個(gè)值得解答的問題在腦子里盤旋,就很難保持求學(xué)問的熱心。可是,如果你有了一個(gè)真有趣的問題逗你去想它,天天引誘你去解決它,天天對(duì)你挑釁你無可奈何它——這時(shí)候,你就會(huì)同戀愛一個(gè)女子發(fā)了瘋一樣,坐也坐不下,睡也睡不安,沒工夫也得偷出工夫去陪她,沒錢也得縮衣節(jié)食去巴結(jié)她。沒有書,你自會(huì)變賣家私去買書;沒有儀器,你自會(huì)典押衣物去置辦儀器;沒有師友,你自會(huì)不遠(yuǎn)千里去尋師訪友。你只要有疑難問題來逼你時(shí)時(shí)用腦子,你自然會(huì)保持發(fā)展你對(duì)學(xué)問的興趣,即使在最貧乏的知識(shí)中,你也會(huì)慢慢地,聚起一個(gè)小圖書館來,或者設(shè)置起一所小試驗(yàn)室來。所以我說,第一要尋問題。腦子里沒有問題之日,就是你知識(shí)生活壽終正寢之時(shí)!古人說,“待文王而興者,凡民也。若夫豪杰之士,雖無文王猶興。”試想伽利略和牛頓有多少藏書?有多少儀器?他們不過是有問題而已。有了問題而后他們自會(huì)造出儀器來解決他們的問題。沒有問題的人們,關(guān)在圖書館里也不會(huì)用書,鎖在試驗(yàn)室里也不會(huì)有什么發(fā)現(xiàn)。
第二個(gè)方子也只有一句話:“總得多發(fā)展一點(diǎn)非職業(yè)的興趣。”離開學(xué)校之后,大家總是尋個(gè)吃飯的職業(yè)??墒悄銓さ玫穆殬I(yè)未必就是你所學(xué)的,未必是你所心喜的,或者是你所學(xué)的而和你性情不相近的。在這種情況之下,工作往往成了苦工,就感覺不到興趣了。為糊口而做那種非“性之所近而力之所能勉”的工作,就很難保持求知的興趣的生活的理想主義。最好的救濟(jì)方法只有多多發(fā)展職業(yè)以外的正當(dāng)興趣與活動(dòng)。
一個(gè)人應(yīng)該有他的職業(yè),也應(yīng)該有他非職業(yè)的玩藝兒,可以叫作業(yè)余活動(dòng)。往往他的業(yè)余活動(dòng)比他的職業(yè)還更重要,因?yàn)橐粋€(gè)人成就怎樣,往往靠他怎樣利用他的閑暇時(shí)間。他用他的閑暇來打麻將,他就成了個(gè)賭徒;你用你的閑暇來做社會(huì)服務(wù),你也許成個(gè)社會(huì)改革者;或者你用你的閑暇去研究歷史,你也許成個(gè)史學(xué)家。你的閑暇往往定你的終身。英國19世紀(jì)的兩個(gè)哲人,彌兒終身做東印度公司的秘書,然而他的業(yè)余工作使他在哲學(xué)上、經(jīng)濟(jì)學(xué)上、政治思想史上都占一個(gè)很高的位置;斯賓塞是一個(gè)測(cè)量工程師,然而他的業(yè)余工作使他成為前世紀(jì)晚期世界思想界的一個(gè)重鎮(zhèn)。古來成大學(xué)問的人,幾乎沒有一個(gè)不善用他的閑暇時(shí)間的。職業(yè)不容易適合我們的性情,我們要想生活不苦痛不墮落,只有多方發(fā)展。
有了這種心愛的玩藝兒,你就做六個(gè)鐘頭抹桌子工作也不會(huì)感覺煩悶了。因?yàn)槟阒?,抹了六個(gè)鐘頭的桌子之后,你可以回家做你的化學(xué)研究,或畫完你的大幅山水,或?qū)懩愕男≌f戲曲,或繼續(xù)你的歷史考據(jù),或做你的社會(huì)改革事業(yè)。你有了這種稱心如意的活動(dòng),生活就不枯寂了,精神也就不會(huì)煩悶了。
第三個(gè)方子也只有一句話:“你得有一點(diǎn)信心。”我們生當(dāng)這個(gè)不幸的時(shí)代,眼中所見,耳中所聞,無非是叫我們悲觀失望的。特別是在這個(gè)年頭畢業(yè)的你們,眼見自己的國家民族沉淪到這步田地,眼看世界只是強(qiáng)權(quán)的世界,望極天邊好像看不見一線的光明——在這個(gè)年頭不發(fā)狂自殺,已算是萬幸了,怎么還能夠保持一點(diǎn)內(nèi)心的鎮(zhèn)定和理想的信任呢?我要對(duì)你們說:這時(shí)候正是我們要培養(yǎng)我們的信心的時(shí)候!只要我們有信心,我們還有救。
古人說:“信心可以移山。”又說:“只要功夫深,生鐵磨成繡花針。”你不信嗎?當(dāng)拿破侖的軍隊(duì)征服普魯士,占據(jù)柏林的時(shí)候,有一位教授叫作費(fèi)希特的,天天在講堂勸他的國人要有信心,要信仰他們的民族是有世界的特殊使命的,是必定要復(fù)興的。費(fèi)希特死的時(shí)候,誰也不能預(yù)料德意志統(tǒng)一帝國何時(shí)可以實(shí)現(xiàn),然而不滿50年,新的統(tǒng)一的德意志帝國居然實(shí)現(xiàn)了。
一個(gè)國家的強(qiáng)弱盛衰,都不是偶然的,都不能逃出因果的鐵律的。我們今日所受的苦痛和恥辱,都只是過去種種惡因種下的惡果。我們要收獲將來的善果,必須努力種現(xiàn)在新因。一粒一粒地種,必有滿倉滿屋的收,這是我們今日應(yīng)有的信心。我們要深信:今日的失敗,都由于過去的不努力。我們要深信:今日的努力,必定有將來的大收成。
佛典里有一句話:“福不唐捐。”唐捐就是白白地丟了。我們也應(yīng)該說:“功不唐捐!”沒有一點(diǎn)努力是會(huì)白白地丟了的。在我們看不見想不到的時(shí)候,在我們看不見的方向,你瞧!你下的種子早已生根發(fā)葉開花結(jié)果了!你不信嗎?法國被普魯士打敗之后,割了兩省地,賠了50萬萬法郎的賠款。這時(shí)候有一位刻苦的科學(xué)家巴斯德終日埋頭在他的化學(xué)試驗(yàn)室里做他的化學(xué)試驗(yàn)和微菌學(xué)研究。他是一個(gè)最愛國的人,然而他深信只有科學(xué)可以救國。他用一生的精力證明了三個(gè)科學(xué)問題:(1)每一種發(fā)酵作用都是由于一種微菌的發(fā)展;(2)每一種傳染病都是一種微菌在生物體內(nèi)的發(fā)展;(3)傳染病的微菌,在特殊的培養(yǎng)之下可以減輕毒力,使他們從病菌變成防病的藥苗。
這三個(gè)問題在表面上似乎都和救國大事業(yè)沒有多大關(guān)系。然而從第一個(gè)問題的證明,巴斯德定出做醋釀酒的新法,使全國的酒醋業(yè)每年減除極大的損失。從第二個(gè)問題的證明,巴斯德教全國的蠶絲業(yè)怎樣選種防病,教全國的畜牧農(nóng)家怎樣防止牛羊瘟疫,又教全世界怎樣注重消毒以減少外科手術(shù)的死亡率。從第三個(gè)問題的證明,巴斯德發(fā)明了牲畜的脾熱瘟的療治藥苗,每年替法國農(nóng)家減除了2000萬法郎的大損失;又發(fā)明了瘋狗咬毒的治療法,救濟(jì)了無數(shù)的生命。所以英國的科學(xué)家赫胥黎在皇家學(xué)會(huì)里稱頌巴斯德的功績道:“法國給了德國50萬萬法郎的賠款,巴斯德先生一個(gè)人研究科學(xué)的成就足夠還清這一筆賠款了。”巴斯德對(duì)于科學(xué)有絕大的信心,所以他在國家蒙奇辱大難的時(shí)候,終不肯拋棄他的顯微鏡與試驗(yàn)室。他絕不想他在顯微鏡底下能償還50萬萬法郎的賠款,然而在他看不見想不到的時(shí)候,他已收獲了科學(xué)救國的奇跡。
朋友們,在你最悲觀失望的時(shí)候,那正是你必須鼓起堅(jiān)強(qiáng)的信心的時(shí)候。你要深信:天下沒有白費(fèi)的努力。成功不必在我,而功力必不唐捐。
(摘自《胡適文存》第4集第4卷《胡適教育論著選》,人民教育出版社)
方子不錯(cuò)!
總得時(shí)時(shí)尋一兩個(gè)值得研究的問題!
總得多發(fā)展一點(diǎn)非職業(yè)的興趣。
你得有一點(diǎn)信心。
如何釋放內(nèi)存?
這里,我不是簡單的new后要delete.是對(duì)于系統(tǒng)而言,我怎么做到釋放內(nèi)存呢?讓系統(tǒng)的可用內(nèi)存變大.從網(wǎng)上也看到了許多釋放內(nèi)存的軟件,很驚異它們是如何做到的呢?
有人建議我申請(qǐng)一大塊控件,系統(tǒng)不夠分配了,會(huì)引起它自己去整理內(nèi)存.試了一下,似乎效果不是很好.
還有人建議,這么用,向所有窗口發(fā)送一個(gè)WM_HIBERNAT消息.
PostMessage(HWND_BROADCAST, WM_HIBERNATE, 0, 0);
似乎效果也不是很好.
有沒有很好的方法處理這個(gè)問題呢?
大家知道的話,指點(diǎn)一下啊!
萬分感謝!
過年前,從china-pub買的。一直也沒有安下心來讀??唇衲暾夜ぷ鞯木硾r,也不得不抓把緊了。也愿與c++博客的各位朋友分享我的學(xué)習(xí)心得。
步入主題。
這一章開篇介紹了windows函數(shù)的幾種返回值:VOID,BOOL,HANDLE,PVOID,LONG/DWORD。讓我們明白,僅僅通過返回值,我們是不能清楚函數(shù)調(diào)用為什么會(huì)失敗的。
windows內(nèi)部,函數(shù)檢測(cè)到錯(cuò)誤會(huì)采用什么機(jī)制呢?它是采用“線程本地存儲(chǔ)區(qū)”的機(jī)制來講相應(yīng)的錯(cuò)誤代碼與“主調(diào)線程”關(guān)聯(lián)到一起。它可以使不同的線程能獨(dú)立運(yùn)行,不會(huì)出現(xiàn)相互干擾對(duì)方的錯(cuò)誤代碼的情況。
函數(shù)返回的時(shí)候,其返回值會(huì)指出已發(fā)生的一個(gè)錯(cuò)誤。
我們查看具體是什么錯(cuò)誤,在相應(yīng)的函數(shù)執(zhí)行完成后調(diào)用GetLastError()即可。
windows中,錯(cuò)誤有三種表示:
一個(gè)消息ID(如ERROR_PATH_NOT_FOUND)
消息文本(如the system cannot find the path specified)
一個(gè)編號(hào)(盡量避免使用)
調(diào)試程序的時(shí)候,我們可以配置watch窗口,讓它始終顯示線程的上一個(gè)錯(cuò)誤代碼和錯(cuò)誤的文本描述。如$err,hr。hr是要顯示錯(cuò)誤代碼的消息文本。不過我在windows mobile的環(huán)境下沒有成功,沒有弄清楚為什么。
那么我們?cè)趺丛谧约旱某绦蛑酗@示消息文本呢?文章介紹了利用FormatMessage函數(shù)。這里我也介紹一下這個(gè)函數(shù)的用法:
(下面的介紹摘自:http://www.shnenglu.com/bidepan2023/archive/2008/02/03/42433.html)
DWORD FormatMessage(
DWORD dwFlags,
LPCVOID lpSource,
DWORD dwMessageId,
DWORD dwLanguageId,
LPTSTR lpBuffer,
DWORD nSize,
va_list* Arguments
);
dwFlags:
# FORMAT_MESSAGE_ALLOCATE_BUFFER // 此函數(shù)會(huì)分配內(nèi)存以包含描述字串。
# FORMAT_MESSAGE_FROM_SYSTEM, // 在系統(tǒng)的id映射表中尋找描述字串
# FORMAT_MESSAGE_FROM_HMODULE // 在其他資源模塊中尋找描述字串
# FORMAT_MESSAGE_FROM_STRING // 消息ID是個(gè)字串,不是個(gè)DWORD
#FORMAT_MESSAGE_IGNORE_INSERTS // 允許我們獲得含有%占位符的消息,不傳遞這個(gè)標(biāo)志,就必須在Arguments參數(shù)中提供這些占位符的信息
通常為:FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
lpSource:
# 指定了FORMAT_MESSAGE_FROM_HMODULE的話,此參數(shù)表示模塊的HANDLE
# 指定了FORMAT_MESSAGE_FROM_STRING的話,此參數(shù)表示id字串
通常為:NULL
dwMessageId:
消息ID;如果指定FORMAT_MESSAGE_FROM_STRING,將被忽略。
dwLanguageId:
消息描述所用的語言
通常為:0表示自動(dòng)選擇
lpBuffer:
#如果未指定FORMAT_MESSAGE_ALLOCATE_BUFFER,則為自己提供的緩沖區(qū)
#否則為系統(tǒng)LocalAlloc分配,需要被用戶LocalFree
nSize:
#如果未指定FORMAT_MESSAGE_ALLOCATE_BUFFER,則為自己提供的緩沖區(qū)大小
#否則為系統(tǒng)LocalAlloc分配之最小緩沖區(qū)大小
Arguments:
通常不使用
例子:
void ShowError()


{
DWORD dwError = GetLastError();

HLOCAL hlocal = NULL;

// Use the default system locale since we look for Windows messages.
// Note: this MAKELANGID combination has 0 as value
DWORD systemLocale = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);

BOOL fOk = FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, dwError, systemLocale,
(PTSTR) &hlocal, 0, NULL);


if (!fOk)
{
// Is it a network-related error?
HMODULE hDll = LoadLibraryEx(TEXT("netmsg.dll"), NULL,
DONT_RESOLVE_DLL_REFERENCES);


if (hDll != NULL)
{
fOk = FormatMessage(
FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_ALLOCATE_BUFFER,
hDll, dwError, systemLocale,
(PTSTR) &hlocal, 0, NULL);
FreeLibrary(hDll);
}
}

if (fOk && (hlocal != NULL))

{
OutputDebugString((PCTSTR) LocalLock(hlocal));
LocalFree(hlocal);
}
}
這個(gè)是書中的例子的代碼,我只是將它歸結(jié)為了一個(gè)函數(shù)ErrorShow。這樣我們?cè)谝粋€(gè)函數(shù)的后面調(diào)用,直接可以知道錯(cuò)誤的原因。不過環(huán)境我是在smart device 的DEBUG環(huán)境下調(diào)時(shí)的,OutputDebugString會(huì)輸出相應(yīng)的字符串。
這個(gè)例子中同時(shí)展示了FormatMessage的兩種用法。觀察一下第二個(gè)參數(shù)就明白了。
visual studio 也提供了一個(gè)查詢錯(cuò)誤的小工具,為Error Lookup。通過以上的示例,我們就知道其相應(yīng)的工作原理呢。
這本書的源碼的下載地址:http://wintellect.com/Books.aspx
大家如果對(duì)windows 編程感興趣的話,不妨下來看看。
最近在調(diào)研文件相關(guān)的東西,如MD5值.但是文件有可能很大,所以我們不能一次讀出文件.有人建議用文件映射.查了一下文件映射的內(nèi)容.記錄下來.
摘自:http://www.yesky.com/405/1756405.shtml
摘要: 本文通過內(nèi)存映射文件的使用來對(duì)大尺寸文件進(jìn)行訪問操作,同時(shí)也對(duì)內(nèi)存映射文件的相關(guān)概念和一般編程過程作了較為詳細(xì)的介紹。
關(guān)鍵詞: 內(nèi)存映射文件;大文件處理;分配粒度
引言 文件操作是應(yīng)用
程序最為基本的功能之一,Win32 API和MFC均提供有支持文件處理的函數(shù)和類,常用的有Win32 API的CreateFile()、WriteFile()、ReadFile()和MFC提供的CFile類等。一般來說,以上這些函數(shù)可以滿足大多數(shù)場合的要求,但是對(duì)于某些特殊應(yīng)用領(lǐng)域所需要的動(dòng)輒幾十GB、幾百GB、乃至幾TB的海量存儲(chǔ),再以通常的文件處理方法進(jìn)行處理顯然是行不通的。目前,對(duì)于上述這種大文件的操作一般是以內(nèi)存映射文件的方式來加以處理的,本文下面將針對(duì)這種Windows核心編程技術(shù)展開討論。
內(nèi)存映射文件概述 內(nèi)存文件映射也是Windows的一種內(nèi)存管理方法,提供了一個(gè)統(tǒng)一的內(nèi)存管理特征,使應(yīng)用程序可以通過內(nèi)存指針對(duì)磁盤上的文件進(jìn)行訪問,其過程就如同對(duì)加載了文件的內(nèi)存的訪問。通過文件映射這種使磁盤文件的全部或部分內(nèi)容與進(jìn)程虛擬地址空間的某個(gè)區(qū)域建立映射關(guān)聯(lián)的能力,可以直接對(duì)被映射的文件進(jìn)行訪問,而不必執(zhí)行文件I/O操作也無需對(duì)文件內(nèi)容進(jìn)行緩沖處理。內(nèi)存文件映射的這種特性是非常適合于用來管理大尺寸文件的。
在使用內(nèi)存映射文件進(jìn)行I/O處理時(shí),系統(tǒng)對(duì)數(shù)據(jù)的傳輸按頁面來進(jìn)行。至于內(nèi)部的所有內(nèi)存頁面則是由虛擬內(nèi)存管理器來負(fù)責(zé)管理,由其來決定內(nèi)存頁面何時(shí)被分頁到磁盤,哪些頁面應(yīng)該被釋放以便為其它進(jìn)程提供空閑空間,以及每個(gè)進(jìn)程可以擁有超出實(shí)際分配物理內(nèi)存之外的多少個(gè)頁面空間等等。由于虛擬內(nèi)存管理器是以一種統(tǒng)一的方式來處理所有磁盤I/O的(以頁面為單位對(duì)內(nèi)存數(shù)據(jù)進(jìn)行讀寫),因此這種優(yōu)化使其有能力以足夠快的速度來處理內(nèi)存操作。
使用內(nèi)存映射文件時(shí)所進(jìn)行的任何實(shí)際I/O交互都是在內(nèi)存中進(jìn)行并以標(biāo)準(zhǔn)的內(nèi)存地址形式來訪問。磁盤的周期性分頁也是由
操作系統(tǒng)在后臺(tái)隱蔽實(shí)現(xiàn)的,對(duì)應(yīng)用程序而言是完全透明的。內(nèi)存映射文件的這種特性在進(jìn)行大文件的磁盤事務(wù)操作時(shí)將獲得很高的效益。
需要說明的是,在系統(tǒng)的正常的分頁操作過程中,內(nèi)存映射文件并非一成不變的,它將被定期更新。如果系統(tǒng)要使用的頁面目前正被某個(gè)內(nèi)存映射文件所占用,系統(tǒng)將釋放此頁面,如果頁面數(shù)據(jù)尚未保存,系統(tǒng)將在釋放頁面之前自動(dòng)完成頁面數(shù)據(jù)到磁盤的寫入。
對(duì)于使用頁虛擬存儲(chǔ)管理的Windows操作系統(tǒng),內(nèi)存映射文件是其內(nèi)部已有的內(nèi)存管理組件的一個(gè)擴(kuò)充。由可執(zhí)行代碼頁面和數(shù)據(jù)頁面組成的應(yīng)用程序可根據(jù)需要由操作系統(tǒng)來將這些頁面換進(jìn)或換出內(nèi)存。如果內(nèi)存中的某個(gè)頁面不再需要,操作系統(tǒng)將撤消此頁面原擁用者對(duì)它的控制權(quán),并釋放該頁面以供其它進(jìn)程使用。只有在該頁面再次成為需求頁面時(shí),才會(huì)從磁盤上的可執(zhí)行文件重新讀入內(nèi)存。同樣地,當(dāng)一個(gè)進(jìn)程初始化啟動(dòng)時(shí),內(nèi)存的頁面將用來存儲(chǔ)該應(yīng)用程序的靜態(tài)、動(dòng)態(tài)數(shù)據(jù),一旦對(duì)它們的操作被提交,這些頁面也將被備份至系統(tǒng)的頁面文件,這與可執(zhí)行文件被用來備份執(zhí)行代碼頁面的過程是很類似的。圖1展示了代碼頁面和數(shù)據(jù)頁面在磁盤存儲(chǔ)器上的備份過程:

圖1 進(jìn)程的代碼頁、數(shù)據(jù)頁在磁盤存儲(chǔ)器上的備份
顯然,如果可以采取同一種方式來處理代碼和數(shù)據(jù)頁面,無疑將會(huì)提高程序的執(zhí)行效率,而內(nèi)存映射文件的使用恰恰可以滿足此需求。
對(duì)大文件的管理
內(nèi)存映射文件對(duì)象在關(guān)閉對(duì)象之前并沒有必要撤銷內(nèi)存映射文件的所有視圖。在對(duì)象被釋放之前,所有的臟頁面將自動(dòng)寫入磁盤。通過CloseHandle()關(guān)閉內(nèi)存映射文件對(duì)象,只是釋放該對(duì)象,如果內(nèi)存映射文件代表的是磁盤文件,那么還需要調(diào)用標(biāo)準(zhǔn)文件I/O函數(shù)來將其關(guān)閉。在處理大文件處理時(shí),內(nèi)存映射文件將表示出卓越的優(yōu)勢(shì),只需要消耗極少的物理資源,對(duì)系統(tǒng)的影響微乎其微。下面先給出內(nèi)存映射文件的一般編程流程框圖:

圖2 使用內(nèi)存映射文件的一般流程
而在某些特殊行業(yè),經(jīng)常要面對(duì)十幾GB乃至幾十GB容量的巨型文件,而一個(gè)32位進(jìn)程所擁有的虛擬地址空間只有232 = 4GB,顯然不能一次將文件映像全部映射進(jìn)來。對(duì)于這種情況只能依次將大文件的各個(gè)部分映射到進(jìn)程中的一個(gè)較小的地址空間。這需要對(duì)上面的一般流程進(jìn)行適當(dāng)?shù)母模?br>
1)映射文件開頭的映像。
2)對(duì)該映像進(jìn)行訪問。
3)取消此映像
4)映射一個(gè)從文件中的一個(gè)更深的位移開始的新映像。
5)重復(fù)步驟2,直到訪問完全部的文件數(shù)據(jù)。
下面給出一段根據(jù)此描述而寫出的對(duì)大于4GB的文件的處理代碼:
// 選擇文件 CFileDialog fileDlg(TRUE, "*.txt", "*.txt", NULL, "文本文件 (*.txt)|*.txt||", this); fileDlg.m_ofn.Flags |= OFN_FILEMUSTEXIST; fileDlg.m_ofn.lpstrTitle = "通過內(nèi)存映射文件讀取數(shù)據(jù)"; if (fileDlg.DoModal() == IDOK) { // 創(chuàng)建文件對(duì)象 HANDLE hFile = CreateFile(fileDlg.GetPathName(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { TRACE("創(chuàng)建文件對(duì)象失敗,錯(cuò)誤代碼:%d\r\n", GetLastError()); return; } // 創(chuàng)建文件映射對(duì)象 HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL); if (hFileMap == NULL) { TRACE("創(chuàng)建文件映射對(duì)象失敗,錯(cuò)誤代碼:%d\r\n", GetLastError()); return; } // 得到系統(tǒng)分配粒度 SYSTEM_INFO SysInfo; GetSystemInfo(&SysInfo); DWORD dwGran = SysInfo.dwAllocationGranularity; // 得到文件尺寸 DWORD dwFileSizeHigh; __int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh); qwFileSize |= (((__int64)dwFileSizeHigh) << 32); // 關(guān)閉文件對(duì)象 CloseHandle(hFile); // 偏移地址 __int64 qwFileOffset = 0; // 塊大小 DWORD dwBlockBytes = 1000 * dwGran; if (qwFileSize < 1000 * dwGran) dwBlockBytes = (DWORD)qwFileSize; while (qwFileOffset > 0) { // 映射視圖 LPBYTE lpbMapAddress = (LPBYTE)MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS, (DWORD)(qwFileOffset >> 32), (DWORD)(qwFileOffset & 0xFFFFFFFF), dwBlockBytes); if (lpbMapAddress == NULL) { TRACE("映射文件映射失敗,錯(cuò)誤代碼:%d\r\n", GetLastError()); return; } // 對(duì)映射的視圖進(jìn)行訪問 for(DWORD i = 0; i < dwBlockBytes; i++) BYTE temp = *(lpbMapAddress + i); // 撤消文件映像 UnmapViewOfFile(lpbMapAddress); // 修正參數(shù) qwFileOffset += dwBlockBytes; qwFileSize -= dwBlockBytes; } // 關(guān)閉文件映射對(duì)象句柄 CloseHandle(hFileMap); AfxMessageBox("成功完成對(duì)文件的訪問"); } |
在本例中,首先通過GetFileSize()得到被處理文件長度(64位)的高32位和低32位值。然后在映射過程中設(shè)定每次映射的塊大小為1000倍的分配粒度,如果文件長度小于1000倍的分配粒度時(shí)則將塊大小設(shè)置為文件的實(shí)際長度。在處理過程中由映射、訪問、撤消映射構(gòu)成了一個(gè)循環(huán)處理。其中,每處理完一個(gè)文件塊后都通過關(guān)閉文件映射對(duì)象來對(duì)每個(gè)文件塊進(jìn)行整理。CreateFileMapping()、MapViewOfFile()等函數(shù)是專門用來進(jìn)行內(nèi)存文件映射處理用的。
下面分別對(duì)這些關(guān)鍵函數(shù)進(jìn)行說明:
1)CreateFile():CreateFile()函數(shù)是一個(gè)用途非常廣泛的函數(shù), 在這里的用法并沒有什么特殊的地方,但有幾點(diǎn)需要注意:一是訪問模式參數(shù)dwDesiredAccess。該參數(shù)設(shè)置了對(duì)文件內(nèi)核對(duì)象的訪問類型,其允許設(shè)置的權(quán)限可以為讀權(quán)限GENERIC_READ、寫權(quán)限GENERIC_WRITE、讀寫權(quán)限GENERIC_READ | GENERIC_WRITE和設(shè)備查詢權(quán)限0。在使用映射文件時(shí),只能打開那些具有可讀訪問權(quán)限的文件,即只能應(yīng)用GENERIC_READ和GENERIC_READ | GENERIC_WRITE這兩種組合;另一點(diǎn)需要注意的是共享模式參數(shù)dwShareMode。該參數(shù)定義了對(duì)文件內(nèi)核對(duì)象的共享方式,其可能的設(shè)置為FILE_SHARE_READ、FILE_SHARE_WRITE和0,并可對(duì)其組合使用。其中,設(shè)置為0時(shí)不允許共享對(duì)象;FILE_SHARE_READ和FILE_SHARE_WRITE分別為在要求只讀、只寫訪問的情況下才允許對(duì)象的共享。
由于通過內(nèi)存映射文件可以在多個(gè)進(jìn)程間共享數(shù)據(jù),因此在進(jìn)行這種應(yīng)用時(shí)應(yīng)當(dāng)考慮dwShareMode參數(shù)設(shè)置對(duì)運(yùn)行結(jié)果的影響。
2)CreateFileMapping():該函數(shù)的作用是創(chuàng)建一個(gè)文件映射內(nèi)核對(duì)象,以告知系統(tǒng)文件映射對(duì)象需要多大的物理存儲(chǔ)器。創(chuàng)建內(nèi)存映射文件對(duì)象對(duì)系統(tǒng)資源幾乎沒有什么影響,也不會(huì)影響進(jìn)程的虛擬地址空間。除了需要用來表示該對(duì)象的內(nèi)部資源之外通常并不用為其分配虛擬內(nèi)存,但是如果內(nèi)存映射文件對(duì)象是作共享內(nèi)存之用的話,就要在創(chuàng)建對(duì)象時(shí)由系統(tǒng)為內(nèi)存映射文件的使用在系統(tǒng)頁文件中保留足夠的空間。
函數(shù)第一個(gè)參數(shù)hFile為標(biāo)識(shí)要映射到進(jìn)程的地址空間的文件的句柄。雖然由于內(nèi)存映射文件的物理存儲(chǔ)器是來自于磁盤上的文件,而非系統(tǒng)的頁文件,使創(chuàng)建內(nèi)存映射文件就像保留一個(gè)地址空間區(qū)域并將物理存儲(chǔ)器提交給該區(qū)域一樣。第二個(gè)參數(shù)為指向文件映射內(nèi)核對(duì)象的SECURITY_ATTRIBUTES結(jié)構(gòu)的指針,由此來決定子進(jìn)程能否繼承得到返回的句柄。通常為其傳遞NULL值,以默認(rèn)的
安全屬性來禁止返回句柄的被繼承。
接下來的參數(shù)用于文件被映射后設(shè)定文件映像的保護(hù)屬性。其可能的取值為PAGE_READONLY、PAGE_READWRITE和PAGE_WRITECOPY。雖然在創(chuàng)建文件映射對(duì)象時(shí),系統(tǒng)并不為其保留地址空間區(qū)域,也不將文件的存儲(chǔ)器映射到該區(qū)域。但是,在系統(tǒng)將存儲(chǔ)器映射到進(jìn)程的地址空間中去時(shí),系統(tǒng)必須確切知道應(yīng)賦予物理存儲(chǔ)器頁面的保護(hù)屬性。在設(shè)置保護(hù)屬性時(shí),必須與用CreateFile()函數(shù)打開文件時(shí)所指定的訪問標(biāo)識(shí)相匹配,否則將導(dǎo)致CreateFileMapping()的執(zhí)行失敗。因此這里設(shè)置PAGE_READWRITE屬性。除了上述三個(gè)頁面保護(hù)屬性外,還有4個(gè)區(qū)(Section)保護(hù)屬性也可以一起組合使用:
區(qū)保護(hù)屬性 |
說明 |
SEC_COMMIT |
為區(qū)中的所有頁面在內(nèi)存中或磁盤頁面文件中分配物理存儲(chǔ)器 |
SEC_IMAGE |
告知系統(tǒng),映射的文件是一個(gè)可移植的EXE文件映像 |
SEC_NOCACHE |
告知系統(tǒng),未將文件的任何內(nèi)存映射文件放入高速緩存,多供硬件設(shè)備驅(qū)動(dòng)程序開發(fā)人員使用 |
SEC_RESERVE |
對(duì)一個(gè)區(qū)的所有頁面進(jìn)行保留而不分配物理存儲(chǔ)器 |
后面的兩個(gè)參數(shù)指定了要?jiǎng)?chuàng)建的文件映射對(duì)象的最大字節(jié)數(shù)的高32位值和低32位值,實(shí)際也就設(shè)定了文件的最大字節(jié)數(shù)(最大可以處理16EB的文件)。這兩個(gè)參數(shù)可以滿足確保文件映射對(duì)象能夠得到足夠的物理存儲(chǔ)器這一基本條件。在參數(shù)設(shè)置的大小小于文件實(shí)際大小時(shí),系統(tǒng)將從文件映射指定的字節(jié)數(shù)。這里將其設(shè)置為0,將使所創(chuàng)建的文件映射對(duì)象將為文件的當(dāng)前大小,以上兩種情況均無法改變文件的大小。如果設(shè)置的參數(shù)大于文件的實(shí)際大小,系統(tǒng)將會(huì)在CreateFileMapping()函數(shù)返回前擴(kuò)展該文件。需要指出的是,文件映射對(duì)象的大小是靜態(tài)的,一旦創(chuàng)建完畢后將無法更改。如果設(shè)置的文件映射對(duì)象尺寸偏小將導(dǎo)致無法對(duì)文件進(jìn)行全面的訪問。
在本節(jié)開始也曾提到過,創(chuàng)建文件映射對(duì)象是不需要花費(fèi)什么系統(tǒng)資源的,因此遵循"寧多勿缺"的原則,一般應(yīng)將文件映射對(duì)象的大小設(shè)置為文件大小的相同值。函數(shù)最后的參數(shù)將可以為映射對(duì)象命名。如果想打開一個(gè)已存在的文件映射對(duì)象,該對(duì)象必須要命名。對(duì)該名字字符串的要求僅限于未被其它對(duì)象使用過的名字即可。
CreateFileMapping()在成功執(zhí)行后將返回一個(gè)指向文件映射對(duì)象的句柄。如果對(duì)一個(gè)已經(jīng)存在的文件映射對(duì)象調(diào)用了CreateFileMapping()函數(shù),進(jìn)程將得到一個(gè)指向現(xiàn)有映射對(duì)象的句柄。通過調(diào)用GetLastError()可以得到返回值ERROR_ALREADY_EXIST,由此可以判斷當(dāng)前得到的內(nèi)存映射對(duì)象句柄是新創(chuàng)建的還是打開已經(jīng)存在的。如果系統(tǒng)無法創(chuàng)建文件映射對(duì)象,將導(dǎo)致CreateFileMapping()的執(zhí)行失敗,返回N U L L句柄值。
3)MapViewOfFile():當(dāng)創(chuàng)建了一個(gè)內(nèi)存映射文件對(duì)象并得到其有效句柄后,該句柄即可用來在進(jìn)程的虛擬地址空間中映射文件的一個(gè)映像。在內(nèi)存映射文件對(duì)象已經(jīng)存在的情況下,映像可被任意映射或取消映射。在文件映像被映射時(shí),仍然必須由系統(tǒng)來為文件的數(shù)據(jù)保留一個(gè)地址空間區(qū)域,并將文件的數(shù)據(jù)作為映射到該區(qū)域的物理存儲(chǔ)器進(jìn)行提交。在進(jìn)程的地址空間中,一個(gè)足夠大的連續(xù)地址空間(通常足以覆蓋整個(gè)文件映像)將被指定給此文件映像。盡管如此,內(nèi)存的物理頁面還是根據(jù)在實(shí)際使用中的需求而進(jìn)行分配的。真正分配一個(gè)對(duì)應(yīng)于內(nèi)存映射文件映像頁面的物理內(nèi)存頁面是在發(fā)生該頁的缺頁中斷時(shí)進(jìn)行的,這將在第一次讀寫內(nèi)存頁面中的任一地址時(shí)自動(dòng)完成。MapViewOfFile()即負(fù)責(zé)映射內(nèi)存映射文件的一個(gè)映像,
函數(shù)的第一個(gè)參數(shù)為CreateFileMapping()所返回的內(nèi)存映射文件對(duì)象句柄,第二個(gè)參數(shù)指定了對(duì)文件映像的訪問類型,可能取值有FILE_MAP_WRITE、FILE_MAP_READ、FILE_MAP_ALL_ACCESS和FILE_MAP_COPY等幾種,具體的設(shè)置要根據(jù)文件映射對(duì)象允許的保護(hù)模式而定。根據(jù)前面代碼的設(shè)置,這里應(yīng)該使用FILE_MAP_ALL_ACCESS參數(shù)。這種機(jī)制為對(duì)象的創(chuàng)建者提供了對(duì)映射此對(duì)象的方式進(jìn)行控制的能力。接下來的2個(gè)參數(shù)分別指定了內(nèi)存映射文件的64位偏移地址的低32位和高32位地址,該地址是從內(nèi)存映射文件頭位置到映像開始位置的距離。最后的參數(shù)指定了視圖的大小,如果設(shè)置為0,前面的偏移地址將被忽略,系統(tǒng)將會(huì)把整個(gè)文件映射為一個(gè)映像。MapViewOfFile()如果成功執(zhí)行,將返回一個(gè)指向文件映像在進(jìn)程的地址空間中的起始地址的指針。如果失敗,則返回NULL。在進(jìn)程中,可以為同一個(gè)文件映射對(duì)象創(chuàng)建多個(gè)文件映像,這些映像可以在系統(tǒng)中共存和重疊,也可以與對(duì)應(yīng)的文件映射對(duì)象大小不相一致,但不能大于文件映射對(duì)象的大小。
4)UnmapViewOfFile():當(dāng)不再需要保留映射到進(jìn)程地址空間區(qū)域中的文件映像數(shù)據(jù)時(shí),可通過調(diào)用UnmapViewOfFile()函數(shù)將其釋放。該函數(shù)結(jié)構(gòu)非常簡單,只需要提供映像在進(jìn)程中的起始地址(區(qū)域的基地址)作為參數(shù)即可。該函數(shù)的輸入?yún)?shù)為調(diào)用MapViewOfFile()時(shí)所返回的指向文件映像在進(jìn)程的地址空間中的起始地址的指針。在調(diào)用MapViewOfFile()后,必須確保在進(jìn)程退出之前能夠執(zhí)行UnmapViewOfFile()函數(shù),否則在進(jìn)程終止之后先前保留的區(qū)域?qū)⒌貌坏结尫牛词乖俅螁?dòng)進(jìn)程重復(fù)調(diào)用MapViewOfFile()系統(tǒng)也總是在進(jìn)程的地址空間中保留一個(gè)新的區(qū)域,而此前保留的所有區(qū)域?qū)⒌貌坏结尫拧?br>
一種比較特殊的情況是,對(duì)同一個(gè)內(nèi)存映射文件映射了兩個(gè)相同的映像的撤消。前面曾經(jīng)提到過,對(duì)于同一個(gè)內(nèi)存映射文件可以有多個(gè)映像,這些映像也可以重疊,因此這種情況的存在是合法的。對(duì)于這種情況,雖然從表面看上去在單進(jìn)程的地址空間內(nèi)是不可能存在兩個(gè)基地址完全相同的映像的,這將導(dǎo)致無法對(duì)這它們的區(qū)分。但是事實(shí)上,由MapViewOfFile()所返回得到的基地址只是文件映像在進(jìn)程地址空間中的起始基地址,因此在映射同一內(nèi)存映射文件的兩個(gè)相同映像時(shí)將會(huì)產(chǎn)生對(duì)內(nèi)存映射文件同一部分的兩個(gè)不同基地址的相同映像,可以用同樣的方法調(diào)用UnmapViewOfFile()將其從進(jìn)程的地址空間中予以撤消。
5)CloseHandle(): 與Win32的大多數(shù)對(duì)象一樣,在使用完畢之后總是要通過CloseHandle()函數(shù)將已打開的內(nèi)核對(duì)象關(guān)閉。如果忘記關(guān)閉對(duì)象,在程序繼續(xù)運(yùn)行時(shí)將會(huì)出現(xiàn)資源泄漏。雖然在程序退出運(yùn)行時(shí),操作系統(tǒng)會(huì)自動(dòng)關(guān)閉在進(jìn)程中已經(jīng)打開但未關(guān)閉的任何對(duì)象。但是在進(jìn)程的運(yùn)行過程中,勢(shì)必會(huì)積累過多的資源句柄。因此在不再需要使用對(duì)象的時(shí)候通過CloseHandle()將其予以關(guān)閉是有意義的。
小結(jié) 本文對(duì)內(nèi)存映射文件在大文件處理中的應(yīng)用作了較為詳細(xì)的闡述。經(jīng)實(shí)際測(cè)試,內(nèi)存映射文件在處理大數(shù)據(jù)量文件時(shí)表現(xiàn)出了良好的性能,比通常使用CFile類和ReadFile()和WriteFile()等函數(shù)的文件處理方式具有明顯的優(yōu)勢(shì)。本文所述程序代碼在Windows 2000 Professional下由Microsoft Visual C++ 6.0編譯通過。
今天的任務(wù)是要保存一個(gè)文件。平??磩e人怎么寫,自己還只是看,沒有動(dòng)手去寫過,對(duì)各個(gè)API相應(yīng)的參數(shù)不是很了解。今天在運(yùn)用的時(shí)候,還真是遇見了一些問題。
我們先來說說問題:
第一個(gè)問題:使用WriteFile的時(shí)候,我直接將寬字符串寫進(jìn)了文件,文件顯示如大家所想,摻雜了很多亂碼。但是很有規(guī)則。所以我很快就明白了這需要將寬字符串轉(zhuǎn)換成ASCII碼。
第二個(gè)問題:就是我將文件打開后,又進(jìn)行了寫文件的操作,此時(shí)失敗。所以對(duì)這種情況,還沒有想出辦法,是由于CreateFile的參數(shù)的某些限制么?
由于這兩個(gè)問題,所以我也好好看了一下SDK文檔。
我們先來看一下CreateFile和WriteFile的原型和參數(shù)介紹:
HANDLE CreateFile(
LPCTSTR lpFileName, // 文件名
DWORD dwDesiredAccess, // 訪問方式
DWORD dwShareMode, // 共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 設(shè)為NULL
DWORD dwCreationDisposition, /// 創(chuàng)建方式
DWORD dwFlagsAndAttributes, // 屬性
HANDLE hTemplateFile
);
BOOL WriteFile(
HANDLE hFile, // 文件句柄
LPCVOID lpBuffer, // 包含寫向文件的數(shù)據(jù)
DWORD nNumberOfBytesToWrite, // 數(shù)據(jù)包含的字符串的個(gè)數(shù)
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
);
第一次我寫的程序很簡單
BOOL WriteOwnFile(TCHAR* pFileName, TCHAR* pBuffer, DWORD dwLen)
{
HANDLE hFile = CreateFile(pFileName,
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (INVALID_HANDLE_VALUE != hFile)
{
DWORD dwSize = 0;
WriteFile(hFile, pBuffer, dwLen, &dwSize, NULL );
CloseHandle(hFile);
return TRUE;
}
return FALSE;
}
這樣是完成了,但是寫出來的文件是亂碼。所以沒有進(jìn)行字符的轉(zhuǎn)換,我們需要將pBuffer進(jìn)行轉(zhuǎn)換。這就要用到了WideCharToMultiByte.如何用呢?
首先我的方法比較笨,我是這么用的:
char* pchBuffer = new char[dwLen+1];
WideCharToMultiByte(CP_ACP, NULL, pBuffer, -1, pchBuffer, dwLen+1, NULL, FALSE );
WriteFile(hFile, pBuffer, dwLen+1, &dwSize, NULL );
Delete[] pchBuffer;
此時(shí)注意,我在WriteFile中用了dwLen+1。結(jié)果就是在文件的末尾出現(xiàn)了亂碼,正好多一個(gè)亂碼出來。所以WriteFile中nNumberOfBytesToWrite是寫的字符串的數(shù)目,是不包括’\0’的。
這個(gè)方法笨,是因?yàn)槲覀兊暮瘮?shù)可以縮減為兩個(gè)參數(shù)。是因?yàn)槿缦逻@么寫時(shí),dwLen是所要轉(zhuǎn)換的字符串的個(gè)數(shù),此時(shí)轉(zhuǎn)換的字符串是包括’\0’的。
DWORD dwLen = WideCharToMultiByte(CP_ACP, NULL, pBuffer, -1, NULL, NULL, NULL, FALSE );
所以我們?cè)賮砜匆幌赂膶懸院蟮拇a
BOOL WriteOwnFile(TCHAR* pFileName, TCHAR* pBuffer)
{
HANDLE hFile = CreateFile(pFileName,
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (INVALID_HANDLE_VALUE != hFile)
{
DWORD dwSize = 0;
DWORD dwLen = WideCharToMultiByte(CP_ACP, NULL, pBuffer, -1, NULL, NULL, NULL, FALSE );
char* pchBuffer = new char[dwLen];
WideCharToMultiByte(CP_ACP, NULL, pBuffer, -1, pchBuffer, dwLen, NULL, FALSE );
WriteFile(hFile, pBuffer, dwLen+1, &dwSize, NULL );
delete[] pchBuffer;
CloseHandle(hFile);
return TRUE;
}
return FALSE;
}
這樣感覺代碼好看多了。
對(duì)于第二個(gè)問題,文件打開的時(shí)候文件創(chuàng)建失敗,還沒有想好辦法解決。我在想是不是我的某些認(rèn)知存在問題,文件打開的時(shí)候,是否可以用CreateFile來打開呢?
今天完成了一個(gè)任務(wù),就是在mobile上如何監(jiān)控文件的操作。這個(gè)SDK中有相應(yīng)的例子,為FileChangeNotif。
如何實(shí)現(xiàn)文件監(jiān)控?
首先要在窗口注冊(cè),這個(gè)要用到SHChangeNotifyRegister,這個(gè)函數(shù)的主要功能就是列舉一個(gè)窗口來接收change notifications.
在這個(gè)注冊(cè)的窗口中,響應(yīng)WM_FILECHANGEINFO這個(gè)消息,來進(jìn)行我們響應(yīng)的操作。
如何我們不想監(jiān)控了,則可以使用SHChangeNotifyDeregister,來移除相應(yīng)的注冊(cè)窗口。
這樣我們就可以實(shí)現(xiàn)對(duì)一個(gè)文件夾內(nèi)文件的生成,刪除,改名等等操作的監(jiān)控。
下面我們?cè)倬唧w來談?wù)劽恳徊饺绾尾僮鳌?/span>
1、SHChangeNotifyRegister的運(yùn)用
SHChangeNotifyRegister的原型為
BOOL WINAPI SHChangeNotifyRegister(
HWND hwnd,
SHCHANGENOTIFYENTRY * pshcne
);
其中,hwnd,為接收change notification的窗口;
pshcne是一個(gè)指向SHCHANGENOTIFYENTRY結(jié)構(gòu)的指針,它用來指明窗口接收的change notification的類型.如果設(shè)為NULL,窗口將接收all file system, network 和 media類型的notifications.
SHCHANGENOTIFYENTRY是什么樣的一個(gè)結(jié)構(gòu),我們看一下它的定義
typedef struct tagSHCHANGENOTIFYENTRY {
DWORD dwEventMask;
LPTSTR pszWatchDir;
BOOL fRecursive;
} SHCHANGENOTIFYENTRY;
dwEventMask 指定發(fā)生什么時(shí)間來發(fā)送notification 消息
pszWatchDir 指定監(jiān)控路徑,該值為NULL的情況下,是監(jiān)控所有的文件。
fRecursive指定是否只監(jiān)控指定路徑還是監(jiān)控指定路徑及其子文件夾。
知道了這些,我們不妨寫一個(gè)這樣的函數(shù),來啟動(dòng)文件監(jiān)控。
代碼如下:
BOOL StartFileMonitor(HWND hWnd, LPTSTR lpFilePath)
{
SHCHANGENOTIFYENTRY schneNotifyEntry;
schneNotifyEntry.dwEventMask = SHCNE_ALLEVENTS;
schneNotifyEntry.pszWatchDir = lpFilePath;
schneNotifyEntry.fRecursive = TRUE;
return SHChangeNotifyRegister(hWnd, &schneNotifyEntry);
}
2、如何處理WM_FILECHANGEINFO消息
WM_FILECHANGEINFO 中的參數(shù)lParam,指向FILECHANGENOTIFY,含有相關(guān)的數(shù)據(jù)。所以我們?cè)谑盏皆撓⒑螅茸鞯囊徊坎僮骶褪?/span>
FILECHANGENOTIFY *lpfcn = (FILECHANGENOTIFY*)lParam;
FILECHANGENOTIFY的結(jié)構(gòu)為:
typedef struct tagFILECHANGENOTIFY {
DWORD dwRefCount;
FILECHANGEINFO fci;
} FILECHANGENOTIFY;
我們主要用到了其中的fci參數(shù)。
FILECHANGEINFO的結(jié)構(gòu)為:
struct _FILECHANGEINFO {
DWORD cbSize;
LONG wEventId;
ULONG uFlags;
DWORD dwItem1;
DWORD dwItem2;
DWORD dwAttributes;
FILETIME ftModified;
ULONG nFileSize;
} FILECHANGEINFO, *LPFILECHANGEINFO;
dwEventId 與SHCHANGENOTIFYENTRY結(jié)構(gòu)中的dwEventMask對(duì)應(yīng)。
dwItem1,dwItem2是事件依賴的值,里面包括了我們需要的文件的完整路徑。如果是進(jìn)行創(chuàng)建文件的操作,則dwItem1是創(chuàng)建后文件的完整路徑,如果是對(duì)文件進(jìn)行重新命名操作的話,則dwItem2是修改后文件的完整路徑。此處對(duì)其他參數(shù)不做介紹,大家需要的話,可以查看一下。
我們做完相應(yīng)的操作后,要知道釋放,此時(shí)要用到SHChangeNotifyFree。這個(gè)用起來就簡單很多,如SHChangeNotifyFree(lpfcn)。
下面給大家一小段示例代碼,如下
case WM_FILECHANGEINFO:
{
FILECHANGENOTIFY *lpfcn;
FILECHANGEINFO *lpfci;
lpfcn = (FILECHANGENOTIFY *)lParam;
if (NULL == lpfcn)
{
break;
}
// see if the pointer to the file change info structure
lpfci = &(lpfcn->fci);
if (NULL == lpfci)
{
break;
}
else
{
switch (lpfci->wEventId)
{
case SHCNE_RENAME:
{
//……
}
break;
}
}
SHChangeNotifyFree(lpfcn);
}
break;
3、如何停止文件監(jiān)控
停止文件監(jiān)控比較簡單,只要使該窗口不接收WM_FILECHANGEINFO消息即可。使用SHChangeNotifyDeregister(hWnd)即可。
以上是我今天學(xué)習(xí)的一些總結(jié),此外需要注意的一個(gè)小地方,在mobile上,把一個(gè)文件從一個(gè)文件夾拷到另一個(gè)文件夾,此時(shí)響應(yīng)的事件是SHCNE_CREATE,二從電腦上拷貝一個(gè)文件到mobile上,響應(yīng)的消息為SHCNE_RENAME。我注意到從電腦上拷貝的話,mobile會(huì)先生成一個(gè)Temp文件夾內(nèi)生成一個(gè)臨時(shí)文件,然后再在我們指定的文件夾內(nèi)生成一個(gè)文件。這個(gè)機(jī)制我還不是很清楚為什么。
今天在加數(shù)據(jù)庫的相關(guān)操作時(shí),遇到了一些問題,提示
error C3861: 'CeMountDBVolEx': identifier not found
error C3861: 'CeMountDBVolEx': identifier not found
error C3861: 'CeCreateDatabaseWithProps': identifier not found
error C3861: 'CeCreateSession': identifier not found
error C3861: 'CeOpenDatabaseInSession': identifier not found
我在.cpp文件的開頭加入了
#define EDB
#include <windows.h>
#include <windbase.h>
但是錯(cuò)誤還依然存在
從網(wǎng)上搜索了一些方法
在博文《mobile數(shù)據(jù)庫遇到的問題》
http://blog.sina.com.cn/s/blog_4c5ad0740100cvxg.html
它里面建議使用
extern "C"
{
#include <windbase_edb.h>
}
但是使用后,問題變成了lnk的錯(cuò)誤
error LNK2019: unresolved external symbol
有人在論壇里建議
#include Windbase_edb.h
也是同樣的問題
最后,我問了一下我的同事
他建議我在
stdafx.h 頭文件中添加
#define EDB
#include <windows.h>
#include <windbase.h>
這樣的確解決了問題。
先粘過來,備以后細(xì)讀
鏈接地址:http://www.bsdlover.cn/index.php?action/viewnews/itemid/1611/page/1/php/1
進(jìn)程間通信有以下方法
Using named objects
Waiting for multiple objects
Waiting in a message loop
Using mutex objects
Using semaphore objects
Using event objects
Using critical section objects
Using timer queues
Using waitable timer objects
進(jìn)程間的通訊實(shí)現(xiàn)(IPC)的11種方法
進(jìn)程通常被定義為一個(gè)正在運(yùn)行的程序的實(shí)例,它由兩個(gè)部分組成:
一個(gè)是操作系統(tǒng)用來管理進(jìn)程的內(nèi)核對(duì)象。內(nèi)核對(duì)象也是系統(tǒng)用來存放關(guān)于進(jìn)程的統(tǒng)計(jì)信息的地方
另一個(gè)是地址空間,它包含所有的可執(zhí)行模塊或DLL模塊的代碼和數(shù)據(jù)。它還包含動(dòng)態(tài)分配的空間。如線程堆棧和堆分配空間。每個(gè)進(jìn)程被賦予它自己的虛擬地址空間,當(dāng)進(jìn)程中的一個(gè)線程正在運(yùn)行時(shí),該線程可以訪問只屬于它的進(jìn)程的內(nèi)存。屬于其它進(jìn)程的內(nèi)存則是隱藏的,并不能被正在運(yùn)行的線程訪問。
為了能在兩個(gè)進(jìn)程之間進(jìn)行通訊,由以下幾種方法可供參考:
在16位時(shí)代常使用的方式,CWnd中提供支持
1。窗口消息 標(biāo)準(zhǔn)的Windows消息以及專用的WM_COPYDATA消息 SENDMESSAGE()接收端必須有一個(gè)窗口
2。使用共享內(nèi)存方式(Shared Memory)
a.設(shè)定一塊共享內(nèi)存區(qū)域
HANDLE CreateFileMapping(HANDLE,LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCSTR)
產(chǎn)生一個(gè)file-mapping核心對(duì)象
LPVOID MapViewOfFile(
HANDLE hFileMappingObject,
DWORD dwDesiredAcess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
DWORD dwNumberOfBytesToMap
);
得到共享內(nèi)存的指針
b.找出共享內(nèi)存
決定這塊內(nèi)存要以點(diǎn)對(duì)點(diǎn)(peer to peer)的形式呈現(xiàn)
每個(gè)進(jìn)程都必須有相同的能力,產(chǎn)生共享內(nèi)存并將它初始化。每個(gè)進(jìn)程
都應(yīng)該調(diào)用CreateFileMapping(),然后調(diào)用GetLastError().如果傳回的錯(cuò)誤代碼是ERROR_ALREADY_EXISTS,那么進(jìn)程就可以假設(shè)這一共享內(nèi)存區(qū) 域已經(jīng)被別的進(jìn)程打開并初始化了,否則該進(jìn)程就可以合理的認(rèn)為自己 排在第一位,并接下來將共享內(nèi)存初始化。還是要使用client/server架構(gòu)中只有server進(jìn)程才應(yīng)該產(chǎn)生并初始化共享內(nèi)存。所有的進(jìn)程都應(yīng)該使用
HANDLE OpenFileMapping(DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName);
再調(diào)用MapViewOfFile(),取得共享內(nèi)存的指針
c.同步處理(Mutex)
d.清理(Cleaning up) BOOL UnmapViewOfFile(LPCVOID lpBaseAddress);
CloseHandle()
3。動(dòng)態(tài)數(shù)據(jù)交換(DDE)通過維護(hù)全局分配內(nèi)存使的應(yīng)用程序間傳遞成為可能
其方式是再一塊全局內(nèi)存中手工放置大量的數(shù)據(jù),然后使用窗口消息傳遞內(nèi)存 指針.這是16位WIN時(shí)代使用的方式,因?yàn)樵赪IN32下已經(jīng)沒有全局和局部內(nèi)存 了,現(xiàn)在的內(nèi)存只有一種就是虛存。
4。消息管道(Message Pipe)
用于設(shè)置應(yīng)用程序間的一條永久通訊通道,通過該通道可以象自己的應(yīng)用程序
訪問一個(gè)平面文件一樣讀寫數(shù)據(jù)。
名管道(Anonymous Pipes)
單向流動(dòng),并且只能夠在同一電腦上的各個(gè)進(jìn)程之間流動(dòng)。
命名管道(Named Pipes)
雙向,跨網(wǎng)絡(luò),任何進(jìn)程都可以輕易的抓住,放進(jìn)管道的數(shù)據(jù)有固定的格式,而使用ReadFile()只能讀取該大小的倍數(shù)??梢员皇褂糜贗/O Completion Ports
5郵件槽(Mailslots)
廣播式通信,在32系統(tǒng)中提供的新方法,可以在不同主機(jī)間交換數(shù)據(jù),在 WIN9X下只支持郵件槽客戶
6Windows套接字(Windows Socket)
它具備消息管道所有的功能,但遵守一套通信標(biāo)準(zhǔn)使的不同操作系統(tǒng)之上的應(yīng) 用程序之間可以互相通信。
7Internet通信 它讓應(yīng)用程序從Internet地址上載或下載文件
8。RPC:遠(yuǎn)程過程調(diào)用,很少使用,因其與UNIX的RPC不兼容。
9。串行/并行通信(Serial/Parallel Communication)
它允許應(yīng)用程序通過串行或并行端口與其他的應(yīng)用程序通信
10。COM/DCOM
通過COM系統(tǒng)的代理存根方式進(jìn)行進(jìn)程間數(shù)據(jù)交換,但只能夠表現(xiàn)在對(duì)接口 函數(shù)的調(diào)用時(shí)傳送數(shù)據(jù),通過DCOM可以在不同主機(jī)間傳送數(shù)據(jù)。
今天在與同事討論如何進(jìn)行進(jìn)程間的通訊,在網(wǎng)上查找了一些內(nèi)容,貼出來以備查找.
鏈接地址:
http://www.cnblogs.com/henryzc/archive/2005/11/08/271920.html
1、引言
在Windows程序中,各個(gè)進(jìn)程之間常常需要交換數(shù)據(jù),進(jìn)行數(shù)據(jù)通訊。WIN32 API提供了許多函數(shù)使我們能夠方便高效的進(jìn)行進(jìn)程間的通訊,通過這些函數(shù)我們可以控制不同進(jìn)程間的數(shù)據(jù)交換,就如同在WIN16中對(duì)本地進(jìn)程進(jìn)行讀寫操作一樣。
典型的WIN16兩進(jìn)程可以通過共享內(nèi)存來進(jìn)行數(shù)據(jù)交換:(1)進(jìn)程A將GlobalAlloc(GMEM_SHARE...)API分配一定長度的內(nèi)存;(2)進(jìn)程A將GlobalAlloc函數(shù)返回的句柄傳遞給進(jìn)程B(通過一個(gè)登錄消息);(3)進(jìn)程B對(duì)這個(gè)句柄調(diào)用GlobalLock函數(shù),并利用GlobalLock函數(shù)返回的指針訪問數(shù)據(jù)。這種方法在WIN32中可能失敗,這是因?yàn)镚lobalLock函數(shù)返回指向的是進(jìn)程A的內(nèi)存,由于進(jìn)程使用的是虛擬地址而非實(shí)際物理地址,因此這一指針僅與A進(jìn)程有關(guān),而于B進(jìn)程無關(guān)。
本文探討了幾種WIN32下進(jìn)程之間通訊的幾種實(shí)現(xiàn)方法,讀者可以使用不同的方法以達(dá)到程序運(yùn)行高效可靠的目的。
2、Windows95中進(jìn)程的內(nèi)存空間管理
WIN32進(jìn)程間通訊與Windows95的內(nèi)存管理有密切關(guān)系,理解Windows95的內(nèi)存管理對(duì)我們?nèi)缦碌某绦蛟O(shè)計(jì)將會(huì)有很大的幫助,下面我們討論以下Windows95中進(jìn)程的內(nèi)存空間管理。
在WIN16下,所有Windows應(yīng)用程序共享單一地址,任何進(jìn)程都能夠?qū)@一空間中屬于共享單一的地址空間,任何進(jìn)程都能夠?qū)@一空間中屬于其他進(jìn)程的內(nèi)存進(jìn)行讀寫操作,甚至可以存取操作系統(tǒng)本身的數(shù)據(jù),這樣就可能破壞其他程序的數(shù)據(jù)段代碼。
在WIN32下,每個(gè)進(jìn)程都有自己的地址空間,一個(gè)WIN32進(jìn)程不能存取另一個(gè)地址的私有數(shù)據(jù),兩個(gè)進(jìn)程可以用具有相同值的指針尋址,但所讀寫的只是它們各自的數(shù)據(jù),這樣就減少了進(jìn)程之間的相互干擾。另一方面,每個(gè)WIN32進(jìn)程擁有4GB的地址空間,但并不代表它真正擁有4GB的實(shí)際物理內(nèi)存,而只是操作系統(tǒng)利用CPU的內(nèi)存分配功能提供的虛擬地址空間。在一般情況下,絕大多數(shù)虛擬地址并沒有物理內(nèi)存于它對(duì)應(yīng),在真正可以使用這些地址空間之前,還要由操作系統(tǒng)提供實(shí)際的物理內(nèi)存(這個(gè)過程叫"提交"commit)。在不同的情況下,系統(tǒng)提交的物理內(nèi)存是不同的,可能是RAM,也可能是硬盤模擬的虛擬內(nèi)存。
3、WIN32中進(jìn)程間的通訊
在Windows 95中,為實(shí)現(xiàn)進(jìn)程間平等的數(shù)據(jù)交換,用戶可以有如下幾種選擇:
* 使用內(nèi)存映射文件
* 通過共享內(nèi)存DLL共享內(nèi)存
* 向另一進(jìn)程發(fā)送WM_COPYDATA消息
* 調(diào)用ReadProcessMemory以及WriteProcessMemory函數(shù),用戶可以發(fā)送由GlobalLock(GMEM_SHARE,...)函數(shù)調(diào)用提取的句柄、GlobalLock函數(shù)返回的指針以及VirtualAlloc函數(shù)返回的指針。
3.1、利用內(nèi)存映射文件實(shí)現(xiàn)WIN32進(jìn)程間的通訊
Windows95中的內(nèi)存映射文件的機(jī)制為我們高效地操作文件提供了一種途徑,它允許我們?cè)赪IN32進(jìn)程中保留一段內(nèi)存區(qū)域,把目標(biāo)文件映射到這段虛擬內(nèi)存中。在程序?qū)崿F(xiàn)中必須考慮各進(jìn)程之間的同步。具體實(shí)現(xiàn)步驟如下:
首先我們?cè)诎l(fā)送數(shù)據(jù)的進(jìn)程中需要通過調(diào)用內(nèi)存映射API函數(shù)CreateFileMapping創(chuàng)建一個(gè)有名的共享內(nèi)存:
HANDLE CreateFileMapping(
HANDLE hFile, // 映射文件的句柄,
//設(shè)為0xFFFFFFFF以創(chuàng)建一個(gè)進(jìn)程間共享的對(duì)象
LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // 安全屬性
DWORD flProtect, // 保護(hù)方式
DWORD dwMaximumSizeHigh, //對(duì)象的大小
DWORD dwMaximumSizeLow,
LPCTSTR lpName // 必須為映射文件命名
);
與虛擬內(nèi)存類似,保護(hù)方式可以是PAGE_READONLY或是PAGE_READWRITE。如果多進(jìn)程都對(duì)同一共享內(nèi)存進(jìn)行寫訪問,則必須保持相互間同步。映射文件還可以指定PAGE_WRITECOPY標(biāo)志,可以保證其原始數(shù)據(jù)不會(huì)遭到破壞,同時(shí)允許其他進(jìn)程在必要時(shí)自由的操作數(shù)據(jù)的拷貝。
在創(chuàng)建文件映射對(duì)象后使用可以調(diào)用MapViewOfFile函數(shù)映射到本進(jìn)程的地址空間內(nèi)。
下面說明創(chuàng)建一個(gè)名為MySharedMem的長度為4096字節(jié)的有名映射文件:
HANDLE hMySharedMapFile=CreateFileMapping((HANDLE)0xFFFFFFFF),
NULL,PAGE_READWRITE,0,0x1000,"MySharedMem");
并映射緩存區(qū)視圖:
LPSTR pszMySharedMapView=(LPSTR)MapViewOfFile(hMySharedMapFile,
FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
其他進(jìn)程訪問共享對(duì)象,需要獲得對(duì)象名并調(diào)用OpenFileMapping函數(shù)。
HANDLE hMySharedMapFile=OpenFileMapping(FILE_MAP_WRITE,
FALSE,"MySharedMem");
一旦其他進(jìn)程獲得映射對(duì)象的句柄,可以象創(chuàng)建進(jìn)程那樣調(diào)用MapViewOfFile函數(shù)來映射對(duì)象視圖。用戶可以使用該對(duì)象視圖來進(jìn)行數(shù)據(jù)讀寫操作,以達(dá)到數(shù)據(jù)通訊的目的。
當(dāng)用戶進(jìn)程結(jié)束使用共享內(nèi)存后,調(diào)用UnmapViewOfFile函數(shù)以取消其地址空間內(nèi)的視圖:
if (!UnmapViewOfFile(pszMySharedMapView))
{ AfxMessageBox("could not unmap view of file"); }
3.2、利用共享內(nèi)存DLL
共享數(shù)據(jù)DLL允許進(jìn)程以類似于Windows 3.1 DLL共享數(shù)據(jù)的方式訪問讀寫數(shù)據(jù),多個(gè)進(jìn)程都可以對(duì)該共享數(shù)據(jù)DLL進(jìn)行數(shù)據(jù)操作,達(dá)到共享數(shù)據(jù)的目的。在WIN32中為建立共享內(nèi)存,必須執(zhí)行以下步驟:
首先創(chuàng)建一個(gè)有名的數(shù)據(jù)區(qū)。這在Visual C++中是使用data_seg pragma宏。使用data_seg pragma宏必須注意數(shù)據(jù)的初始化:
#pragma data_seg("MYSEC")
char MySharedData[4096]={0};
#pragma data_seg()
然后在用戶的DEF文件中為有名的數(shù)據(jù)區(qū)設(shè)定共享屬性。
LIBRARY TEST
DATA READ WRITE
SECTIONS
.MYSEC READ WRITE SHARED
這樣每個(gè)附屬于DLL的進(jìn)程都將接受到屬于自己的數(shù)據(jù)拷貝,一個(gè)進(jìn)程的數(shù)據(jù)變化并不會(huì)反映到其他進(jìn)程的數(shù)據(jù)中。
在DEF文件中適當(dāng)?shù)剌敵鰯?shù)據(jù)。以下的DEF文件項(xiàng)說明了如何以常數(shù)變量的形式輸出MySharedData。
EXPORTS
MySharedData CONSTANT
最后在應(yīng)用程序(進(jìn)程)按外部變量引用共享數(shù)據(jù)。
extern _export"C"{char * MySharedData[];}
進(jìn)程中使用該變量應(yīng)注意間接引用。
m_pStatic=(CEdit*)GetDlgItem(IDC_SHARED);
m_pStatic->GetLine(0,*MySharedData,80);
3.3、用于傳輸只讀數(shù)據(jù)的WM_COPYDATA
傳輸只讀數(shù)據(jù)可以使用Win32中的WM_COPYDATA消息。該消息的主要目的是允許在進(jìn)程間傳遞只讀數(shù)據(jù)。 Windows95在通過WM_COPYDATA消息傳遞期間,不提供繼承同步方式。SDK文檔推薦用戶使用SendMessage函數(shù),接受方在數(shù)據(jù)拷貝完成前不返回,這樣發(fā)送方就不可能刪除和修改數(shù)據(jù):
SendMessage(hwnd,WM_COPYDATA,wParam,lParam);
其中wParam設(shè)置為包含數(shù)據(jù)的窗口的句柄。lParam指向一個(gè)COPYDATASTRUCT的結(jié)構(gòu):
typedef struct tagCOPYDATASTRUCT{
DWORD dwData;//用戶定義數(shù)據(jù)
DWORD cbData;//數(shù)據(jù)大小
PVOID lpData;//指向數(shù)據(jù)的指針
}COPYDATASTRUCT;
該結(jié)構(gòu)用來定義用戶數(shù)據(jù)。
3.4、直接調(diào)用ReadProcessMemory和WriteProcessMemory函數(shù)實(shí)現(xiàn)進(jìn)程間通訊
通過調(diào)用ReadProcessMemory以及WriteProcessMemory函數(shù)用戶可以按類似與Windows3.1的方法實(shí)現(xiàn)進(jìn)程間通訊,在發(fā)送進(jìn)程中分配一塊內(nèi)存存放數(shù)據(jù),可以調(diào)用GlobalAlloc或者VirtualAlloc函數(shù)實(shí)現(xiàn):
pApp->m_hGlobalHandle=GlobalAlloc(GMEM_SHARE,1024);
可以得到指針地址:
pApp->mpszGlobalHandlePtr=(LPSTR)GlobalLock
(pApp->m_hGlobalHandle);
在接收進(jìn)程中要用到用戶希望影響的進(jìn)程的打開句柄。為了讀寫另一進(jìn)程,應(yīng)按如下方式調(diào)用OpenProcess函數(shù):
HANDLE hTargetProcess=OpenProcess(
STANDARD_RIGHTS_REQUIRED|
PROCESS_VM_REDA|
PROCESS_VM_WRITE|
PROCESS_VM_OPERATION,//訪問權(quán)限
FALSE,//繼承關(guān)系
dwProcessID);//進(jìn)程ID
為保證OpenProcess函數(shù)調(diào)用成功,用戶所影響的進(jìn)程必須由上述標(biāo)志創(chuàng)建。
一旦用戶獲得一個(gè)進(jìn)程的有效句柄,就可以調(diào)用ReadProcessMemory函數(shù)讀取該進(jìn)程的內(nèi)存:
BOOL ReadProcessMemory(
HANDLE hProcess, // 進(jìn)程指針
LPCVOID lpBaseAddress, // 數(shù)據(jù)塊的首地址
LPVOID lpBuffer, // 讀取數(shù)據(jù)所需緩沖區(qū)
DWORD cbRead, // 要讀取的字節(jié)數(shù)
LPDWORD lpNumberOfBytesRead
);
使用同樣的句柄也可以寫入該進(jìn)程的內(nèi)存:
BOOL WriteProcessMemory(
HANDLE hProcess, // 進(jìn)程指針
LPVOID lpBaseAddress, // 要寫入的首地址
LPVOID lpBuffer, // 緩沖區(qū)地址
DWORD cbWrite, // 要寫的字節(jié)數(shù)
LPDWORD lpNumberOfBytesWritten
);
如下所示是讀寫另一進(jìn)程的共享內(nèi)存中的數(shù)據(jù):
ReadProcessMemory((HANDLE)hTargetProcess,
(LPSTR)lpsz,m_strGlobal.GetBuffer(_MAX_FIELD),
_MAX_FIELD,&cb);
WriteProcessMemory((HANDLE)hTargetProcess,
(LPSTR)lpsz,(LPSTR)STARS,
m_strGlobal.GetLength(),&cb);
4、進(jìn)程之間的消息發(fā)送與接收
在實(shí)際應(yīng)用中進(jìn)程之間需要發(fā)送和接收Windows消息來通知進(jìn)程間相互通訊,發(fā)送方發(fā)送通訊的消息以通知接收方,接收方在收到發(fā)送方的消息后就可以對(duì)內(nèi)存進(jìn)行讀寫操作。
我們?cè)诔绦蛟O(shè)計(jì)中采用Windows注冊(cè)消息進(jìn)行消息傳遞,首先在發(fā)送進(jìn)程初始化過程中進(jìn)行消息注冊(cè):
m_nMsgMapped=::RegisterWindowsMessage("Mapped");
m_nMsgHandle=::RegisterWindowsMessage("Handle");
m_nMsgShared=::RegisterWindowsMessage("Shared");
在程序運(yùn)行中向接收進(jìn)程發(fā)送消息:
CWnd* pWndRecv=FindWindow(lpClassName,"Receive");
pWndRecv->SendMessage(m_MsgMapped,0,0);
pWndRecv->SendMessage(m_nMsgHandle,
(UINT)GetCurrentProcessID(),(LONG)pApp->m_hGlobalHandle);
pWndRecv->SendMessage(m_nMsgShared,0,0);
可以按如下方式發(fā)送WM_COPYDATA消息:
static COPYDATASTRUCT cds;//用戶存放數(shù)據(jù)
pWnd->SendMessage(WM_COPYDATA,NULL,(LONG)&cds);
接收方進(jìn)程初始化也必須進(jìn)行消息注冊(cè):
UNIT CRecvApp:: m_nMsgMapped=::RegisterWindowsMessage("Mapped");
UNIT CRecvApp::m_nMsgHandle=::RegisterWindowsMessage("Handle");
UNIT CRecvApp::m_nMsgShared=::RegisterWindowsMessage("Shared");
同時(shí)映射消息函數(shù)如下:
ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgMapped,OnRegMsgMapped)
ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgHandle,OnRegMsgHandle)
ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgShared,OnRegMsgShared)
在這些消息函數(shù)我們就可以采用上述技術(shù)實(shí)現(xiàn)接收進(jìn)程中數(shù)據(jù)的讀寫操作了。
5、結(jié)束語
從以上分析中我們可以看出Windows95的內(nèi)存管理與Windows 3.x相比有很多的不同,對(duì)進(jìn)程之間的通訊有較為嚴(yán)格的限制。這就確保了任何故障程序無法意外地寫入用戶的地址空間,而用戶則可根據(jù)實(shí)際情況靈活地進(jìn)行進(jìn)程間的數(shù)據(jù)通訊,從這一點(diǎn)上來講Windows95增強(qiáng)應(yīng)用程序的強(qiáng)壯性。
參考文獻(xiàn):
1、 David J.Kruglinski, Visual C++技術(shù)內(nèi)幕, 北京:清華大學(xué)出版社,1995.
2、 Microsoft Co. Visual C++ 5.0 On Line Help.