1
typedef ULONG (WINAPI *PFNNtUnmapViewOfSection)( IN HANDLE ProcessHandle,IN PVOID BaseAddress );
2
3
BOOL UnmapViewOfModule ( DWORD dwProcessId, LPVOID lpBaseAddr )
4


{
5
HMODULE hModule = GetModuleHandle ( L"ntdll.dll" ) ;
6
if ( hModule == NULL )
7
hModule = LoadLibrary ( L"ntdll.dll" ) ;
8
9
PFNNtUnmapViewOfSection pfnNtUnmapViewOfSection = (PFNNtUnmapViewOfSection)GetProcAddress ( hModule, "NtUnmapViewOfSection" ) ;
10
11
HANDLE hProcess = OpenProcess ( PROCESS_ALL_ACCESS, TRUE, dwProcessId ) ;
12
ULONG ret = pfnNtUnmapViewOfSection ( hProcess, lpBaseAddr ) ;
13
CloseHandle ( hProcess ) ;
14
return ret ? FALSE : TRUE;
15
}
16
17
posted @
2009-02-21 22:32 幽幽 閱讀(475) |
評論 (0) |
編輯 收藏
本文轉自:
http://hi.baidu.com/csuhkx/blog/item/267418d3614cf9013bf3cf55.html這篇文章是翻譯MSDN上一篇叫《Heap: Pleasures and Pains》的文章的
Murali R. Krishnan
Microsoft Corporation
1999 年 2 月
摘要: 討論常見的堆性能問題以及如何防范它們。(共 9 頁)
前言
您是否是動態分配的 C/C++ 對象忠實且幸運的用戶?您是否在模塊間的往返通信中頻繁地使用了“自動化”?您的程序是否因堆分配而運行起來很慢?不僅僅您遇到這樣的問題。幾乎所有項目遲早都會遇到堆問題。大家都想說,“我的代碼真正好,只是堆太慢”。那只是部分正確。更深入理解堆及其用法、以及會發生什么問題,是很有用的。
什么是堆?
(如果您已經知道什么是堆,可以跳到“什么是常見的堆性能問題?”部分)
在程序中,使用堆來動態分配和釋放對象。在下列情況下,調用堆操作:
- 事先不知道程序所需對象的數量和大小。
- 對象太大而不適合堆棧分配程序。
堆使用了在運行時分配給代碼和堆棧的內存之外的部分內存。下圖給出了堆分配程序的不同層。

GlobalAlloc/GlobalFree:Microsoft Win32 堆調用,這些調用直接與每個進程的默認堆進行對話。
LocalAlloc/LocalFree:Win32 堆調用(為了與 Microsoft Windows NT 兼容),這些調用直接與每個進程的默認堆進行對話。
COM 的 IMalloc 分配程序(或 CoTaskMemAlloc / CoTaskMemFree):函數使用每個進程的默認堆。自動化程序使用“組件對象模型 (COM)”的分配程序,而申請的程序使用每個進程堆。
C/C++ 運行時 (CRT) 分配程序:提供了 malloc() 和 free() 以及 new 和 delete 操作符。如 Microsoft Visual Basic 和 Java 等語言也提供了新的操作符并使用垃圾收集來代替堆。CRT 創建自己的私有堆,駐留在 Win32 堆的頂部。
Windows NT 中,Win32 堆是 Windows NT 運行時分配程序周圍的薄層。所有 API 轉發它們的請求給 NTDLL。
Windows NT 運行時分配程序提供 Windows NT 內的核心堆分配程序。它由具有 128 個大小從 8 到 1,024 字節的空閑列表的前端分配程序組成。后端分配程序使用虛擬內存來保留和提交頁。
在圖表的底部是“虛擬內存分配程序”,操作系統使用它來保留和提交頁。所有分配程序使用虛擬內存進行數據的存取。
分配和釋放塊不就那么簡單嗎?為何花費這么長時間?
堆實現的注意事項
傳統上,操作系統和運行時庫是與堆的實現共存的。在一個進程的開始,操作系統創建一個默認堆,叫做“進程堆”。如果沒有其他堆可使用,則塊的分配使用“進程堆”。語言運行時也能在進程內創建單獨的堆。(例如,C 運行時創建它自己的堆。)除這些專用的堆外,應用程序或許多已載入的動態鏈接庫 (DLL) 之一可以創建和使用單獨的堆。Win32 提供一整套 API 來創建和使用私有堆。有關堆函數(英文)的詳盡指導,請參見 MSDN。
當應用程序或 DLL 創建私有堆時,這些堆存在于進程空間,并且在進程內是可訪問的。從給定堆分配的數據將在同一個堆上釋放。(不能從一個堆分配而在另一個堆釋放。)
在所有虛擬內存系統中,堆駐留在操作系統的“虛擬內存管理器”的頂部。語言運行時堆也駐留在虛擬內存頂部。某些情況下,這些堆是操作系統堆中的層,而語言運行時堆則通過大塊的分配來執行自己的內存管理。不使用操作系統堆,而使用虛擬內存函數更利于堆的分配和塊的使用。
典型的堆實現由前、后端分配程序組成。前端分配程序維持固定大小塊的空閑列表。對于一次分配調用,堆嘗試從前端列表找到一個自由塊。如果失敗,堆被迫從后端(保留和提交虛擬內存)分配一個大塊來滿足請求。通用的實現有每塊分配的開銷,這將耗費執行周期,也減少了可使用的存儲空間。
Knowledge Base 文章 Q10758,“用 calloc() 和 malloc() 管理內存” (搜索文章編號), 包含了有關這些主題的更多背景知識。另外,有關堆實現和設計的詳細討論也可在下列著作中找到:“Dynamic Storage Allocation: A Survey and Critical Review”,作者 Paul R. Wilson、Mark S. Johnstone、Michael Neely 和 David Boles;“International Workshop on Memory Management”, 作者 Kinross, Scotland, UK, 1995 年 9 月(http://www.cs.utexas.edu/users/oops/papers.html)(英文)。
Windows NT 的實現(Windows NT 版本 4.0 和更新版本) 使用了 127 個大小從 8 到 1,024 字節的 8 字節對齊塊空閑列表和一個“大塊”列表。“大塊”列表(空閑列表[0]) 保存大于 1,024 字節的塊。空閑列表容納了用雙向鏈表鏈接在一起的對象。默認情況下,“進程堆”執行收集操作。(收集是將相鄰空閑塊合并成一個大塊的操作。)收集耗費了額外的周期,但減少了堆塊的內部碎片。
單一全局鎖保護堆,防止多線程式的使用。(請參見“Server Performance and Scalability Killers”中的第一個注意事項, George Reilly 所著,在 “MSDN Online Web Workshop”上(站點:http://msdn.microsoft.com/workshop/server/iis/tencom.asp(英文)。)單一全局鎖本質上是用來保護堆數據結構,防止跨多線程的隨機存取。若堆操作太頻繁,單一全局鎖會對性能有不利的影響。
什么是常見的堆性能問題?
以下是您使用堆時會遇到的最常見問題:
競爭是在分配和釋放操作中導致速度減慢的問題。理想情況下,希望使用沒有競爭和快速分配/釋放的堆。可惜,現在還沒有這樣的通用堆,也許將來會有。
在所有的服務器系統中(如 IIS、MSProxy、DatabaseStacks、網絡服務器、 Exchange 和其他), 堆鎖定實在是個大瓶頸。處理器數越多,競爭就越會惡化。
盡量減少堆的使用
現在您明白使用堆時存在的問題了,難道您不想擁有能解決這些問題的超級魔棒嗎?我可希望有。但沒有魔法能使堆運行加快—因此不要期望在產品出貨之前的最后一星期能夠大為改觀。如果提前規劃堆策略,情況將會大大好轉。調整使用堆的方法,減少對堆的操作是提高性能的良方。
如何減少使用堆操作?通過利用數據結構內的位置可減少堆操作的次數。請考慮下列實例:
struct ObjectA {
// objectA 的數據
}
struct ObjectB {
// objectB 的數據
}
// 同時使用 objectA 和 objectB
//
// 使用指針
//
struct ObjectB {
struct ObjectA * pObjA;
// objectB 的數據
}
//
// 使用嵌入
//
struct ObjectB {
struct ObjectA pObjA;
// objectB 的數據
}
//
// 集合 – 在另一對象內使用 objectA 和 objectB
//
struct ObjectX {
struct ObjectA objA;
struct ObjectB objB;
}
- 避免使用指針關聯兩個數據結構。如果使用指針關聯兩個數據結構,前面實例中的對象 A 和 B 將被分別分配和釋放。這會增加額外開銷—我們要避免這種做法。
- 把帶指針的子對象嵌入父對象。當對象中有指針時,則意味著對象中有動態元素(百分之八十)和沒有引用的新位置。嵌入增加了位置從而減少了進一步分配/釋放的需求。這將提高應用程序的性能。
- 合并小對象形成大對象(聚合)。聚合減少分配和釋放的塊的數量。如果有幾個開發者,各自開發設計的不同部分,則最終會有許多小對象需要合并。集成的挑戰就是要找到正確的聚合邊界。
- 內聯緩沖區能夠滿足百分之八十的需要(aka 80-20 規則)。個別情況下,需要內存緩沖區來保存字符串/二進制數據,但事先不知道總字節數。估計并內聯一個大小能滿足百分之八十需要的緩沖區。對剩余的百分之二十,可以分配一個新的緩沖區和指向這個緩沖區的指針。這樣,就減少分配和釋放調用并增加數據的位置空間,從根本上提高代碼的性能。
- 在塊中分配對象(塊化)。塊化是以組的方式一次分配多個對象的方法。如果對列表的項連續跟蹤,例如對一個 {名稱,值} 對的列表,有兩種選擇:選擇一是為每一個“名稱-值”對分配一個節點;選擇二是分配一個能容納(如五個)“名稱-值”對的結構。例如,一般情況下,如果存儲四對,就可減少節點的數量,如果需要額外的空間數量,則使用附加的鏈表指針。
塊化是友好的處理器高速緩存,特別是對于 L1-高速緩存,因為它提供了增加的位置 —不用說對于塊分配,很多數據塊會在同一個虛擬頁中。
- 正確使用 _amblksiz。C 運行時 (CRT) 有它的自定義前端分配程序,該分配程序從后端(Win32 堆)分配大小為 _amblksiz 的塊。將 _amblksiz 設置為較高的值能潛在地減少對后端的調用次數。這只對廣泛使用 CRT 的程序適用。
使用上述技術將獲得的好處會因對象類型、大小及工作量而有所不同。但總能在性能和可升縮性方面有所收獲。另一方面,代碼會有點特殊,但如果經過深思熟慮,代碼還是很容易管理的。
其他提高性能的技術
下面是一些提高速度的技術:
- 使用 Windows NT5 堆
由于幾個同事的努力和辛勤工作,1998 年初 Microsoft Windows(R) 2000 中有了幾個重大改進:
- 改進了堆代碼內的鎖定。堆代碼對每堆一個鎖。全局鎖保護堆數據結構,防止多線程式的使用。但不幸的是,在高通信量的情況下,堆仍受困于全局鎖,導致高競爭和低性能。Windows 2000 中,鎖內代碼的臨界區將競爭的可能性減到最小,從而提高了可伸縮性。
- 使用 “Lookaside”列表。堆數據結構對塊的所有空閑項使用了大小在 8 到 1,024 字節(以 8-字節遞增)的快速高速緩存。快速高速緩存最初保護在全局鎖內。現在,使用 lookaside 列表來訪問這些快速高速緩存空閑列表。這些列表不要求鎖定,而是使用 64 位的互鎖操作,因此提高了性能。
- 內部數據結構算法也得到改進。
這些改進避免了對分配高速緩存的需求,但不排除其他的優化。使用 Windows NT5 堆評估您的代碼;它對小于 1,024 字節 (1 KB) 的塊(來自前端分配程序的塊)是最佳的。GlobalAlloc() 和 LocalAlloc() 建立在同一堆上,是存取每個進程堆的通用機制。如果希望獲得高的局部性能,則使用 Heap(R) API 來存取每個進程堆,或為分配操作創建自己的堆。如果需要對大塊操作,也可以直接使用 VirtualAlloc() / VirtualFree() 操作。
上述改進已在 Windows 2000 beta 2 和 Windows NT 4.0 SP4 中使用。改進后,堆鎖的競爭率顯著降低。這使所有 Win32 堆的直接用戶受益。CRT 堆建立于 Win32 堆的頂部,但它使用自己的小塊堆,因而不能從 Windows NT 改進中受益。(Visual C++ 版本 6.0 也有改進的堆分配程序。)
- 使用分配高速緩存
分配高速緩存允許高速緩存分配的塊,以便將來重用。這能夠減少對進程堆(或全局堆)的分配/釋放調用的次數,也允許最大限度的重用曾經分配的塊。另外,分配高速緩存允許收集統計信息,以便較好地理解對象在較高層次上的使用。
典型地,自定義堆分配程序在進程堆的頂部實現。自定義堆分配程序與系統堆的行為很相似。主要的差別是它在進程堆的頂部為分配的對象提供高速緩存。高速緩存設計成一套固定大小(如 32 字節、64 字節、128 字節等)。這一個很好的策略,但這種自定義堆分配程序丟失與分配和釋放的對象相關的“語義信息”。
與自定義堆分配程序相反,“分配高速緩存”作為每類分配高速緩存來實現。除能夠提供自定義堆分配程序的所有好處之外,它們還能夠保留大量語義信息。每個分配高速緩存處理程序與一個目標二進制對象關聯。它能夠使用一套參數進行初始化,這些參數表示并發級別、對象大小和保持在空閑列表中的元素的數量等。分配高速緩存處理程序對象維持自己的私有空閑實體池(不超過指定的閥值)并使用私有保護鎖。合在一起,分配高速緩存和私有鎖減少了與主系統堆的通信量,因而提供了增加的并發、最大限度的重用和較高的可伸縮性。
需要使用清理程序來定期檢查所有分配高速緩存處理程序的活動情況并回收未用的資源。如果發現沒有活動,將釋放分配對象的池,從而提高性能。
可以審核每個分配/釋放活動。第一級信息包括對象、分配和釋放調用的總數。通過查看它們的統計信息可以得出各個對象之間的語義關系。利用以上介紹的許多技術之一,這種關系可以用來減少內存分配。
分配高速緩存也起到了調試助手的作用,幫助您跟蹤沒有完全清除的對象數量。通過查看動態堆棧返回蹤跡和除沒有清除的對象之外的簽名,甚至能夠找到確切的失敗的調用者。
- MP 堆
MP 堆是對多處理器友好的分布式分配的程序包,在 Win32 SDK(Windows NT 4.0 和更新版本)中可以得到。最初由 JVert 實現,此處堆抽象建立在 Win32 堆程序包的頂部。MP 堆創建多個 Win32 堆,并試圖將分配調用分布到不同堆,以減少在所有單一鎖上的競爭。
本程序包是好的步驟 —一種改進的 MP-友好的自定義堆分配程序。但是,它不提供語義信息和缺乏統計功能。通常將 MP 堆作為 SDK 庫來使用。如果使用這個 SDK 創建可重用組件,您將大大受益。但是,如果在每個 DLL 中建立這個 SDK 庫,將增加工作設置。
- 重新思考算法和數據結構
要在多處理器機器上伸縮,則算法、實現、數據結構和硬件必須動態伸縮。請看最經常分配和釋放的數據結構。試問,“我能用不同的數據結構完成此工作嗎?”例如,如果在應用程序初始化時加載了只讀項的列表,這個列表不必是線性鏈接的列表。如果是動態分配的數組就非常好。動態分配的數組將減少內存中的堆塊和碎片,從而增強性能。
減少需要的小對象的數量減少堆分配程序的負載。例如,我們在服務器的關鍵處理路徑上使用五個不同的對象,每個對象單獨分配和釋放。一起高速緩存這些對象,把堆調用從五個減少到一個,顯著減少了堆的負載,特別當每秒鐘處理 1,000 個以上的請求時。
如果大量使用“Automation”結構,請考慮從主線代碼中刪除“Automation BSTR”,或至少避免重復的 BSTR 操作。(BSTR 連接導致過多的重分配和分配/釋放操作。)
摘要
對所有平臺往往都存在堆實現,因此有巨大的開銷。每個單獨代碼都有特定的要求,但設計能采用本文討論的基本理論來減少堆之間的相互作用。
- 評價您的代碼中堆的使用。
- 改進您的代碼,以使用較少的堆調用:分析關鍵路徑和固定數據結構。
- 在實現自定義的包裝程序之前使用量化堆調用成本的方法。
- 如果對性能不滿意,請要求 OS 組改進堆。更多這類請求意味著對改進堆的更多關注。
- 要求 C 運行時組針對 OS 所提供的堆制作小巧的分配包裝程序。隨著 OS 堆的改進,C 運行時堆調用的成本將減小。
- 操作系統(Windows NT 家族)正在不斷改進堆。請隨時關注和利用這些改進。
Murali Krishnan 是 Internet Information Server (IIS) 組的首席軟件設計工程師。從 1.0 版本開始他就設計 IIS,并成功發行了 1.0 版本到 4.0 版本。Murali 組織并領導 IIS 性能組三年 (1995-1998), 從一開始就影響 IIS 性能。他擁有威斯康星州 Madison 大學的 M.S.和印度 Anna 大學的 B.S.。工作之外,他喜歡閱讀、打排球和家庭烹飪。
posted @
2009-02-03 08:49 幽幽 閱讀(487) |
評論 (0) |
編輯 收藏
現在是5:14, 外面正在下雨, 喜歡下雨, 因為聽到噼噼啪啪的雨聲, 我總能靜下心來,
是該靜下心來好好想想了, 最近總是很浮躁, 靜不下心來, 是該靜下心來看看書寫寫
代碼了.....
posted @
2009-02-03 05:18 幽幽 閱讀(211) |
評論 (0) |
編輯 收藏
導讀:習慣的力量是驚人的。習慣能載著你走向成功,也能馱著你滑向失敗。如何選擇,完全取決于你自己。
1.習慣的力量:35歲以前養成好習慣
你想成功嗎?那就及早培養有利于成功的好習慣。
習慣的力量是驚人的,35歲以前養成的習慣決定著你是否成功。
有這樣一個寓言故事:
一位沒有繼承人的富豪死后將自己的一大筆遺產贈送給遠房的一位親戚,這位親戚是一個常年靠乞討為生的乞丐。這名接受遺產的乞丐立即身價一變,成了百萬富翁。新聞記者便來采訪這名幸運的乞丐:"你繼承了遺產之后,你想做的第一件事是什么?"乞丐回答說:"我要買一只好一點的碗和一根結實的木棍,這樣我以后出去討飯時方便一些。"
可見,習慣對我們有著絕大的影響,因為它是一貫的,在不知不覺中,經年累月地影響著我們的行為,影響著我們的效率,左右著我們的成敗。
一個人一天的行為中,大約只有5%是屬于非習慣性的,而剩下的95%的行為都是習慣性的。即便是打破常規的創新,最終可以演變成為習慣性的創新。
根據行為心理學的研究結果:3周以上的重復會形成習慣;3個月以上的重復會形成穩定的習慣,即同一個動作,重復3周就會變成習慣性動作,形成穩定的習慣。
亞里士多德說:"人的行為總是一再重復。因此,卓越不是單一的舉動,而是習慣。""人的行為總是一再重復。因此,卓越不是單一的舉動,而是習慣。"所以,在實現成功的過程中,除了要不斷激發自己的成功欲望,要有信心、有熱情、有意志、有毅力等之外,還應該搭上習慣這一成功的快車,實現自己的目標。
有個動物學家做了一個實驗:他將一群跳蚤放入實驗用的大量杯里,上面蓋上一片透明的玻璃。跳蚤習性愛跳,于是很多跳蚤都撞上了蓋上的玻璃,不斷地發叮叮冬冬的聲音。過了一陣子,動物學家將玻璃片拿開,發現竟然所有跳蚤依然在跳,只是都已經將跳的高度保持在接近玻璃即止,以避免撞到頭。結果竟然沒有一只跳蚤能跳出來--依它們的能力不是跳不出來,只是它們已經適應了環境。
后來,那位動物學家就在量杯下放了一個酒精燈并且點燃了火。不到五分鐘,量杯燒熱了,所有跳蚤自然發揮求生的本能,每只跳蚤再也不管頭是否會撞痛(因為它們以為還有玻璃罩),全部都跳出量杯以外。這個試驗證明,跳蚤會為了適應環境,不愿改變習性,寧愿降低才能、封閉潛能去適應。
我想,人類之于環境也是如此。人類在適應外界大環境中,又創造出適合于自己的小環境,然后用習慣把自己困在自己所創造的環境中。所以,習慣決定著你的活動空間的大小,也決定著你的成敗。養成好習慣對于你的成功非常重要。
心理學巨匠威廉·詹姆士說:"播下一個行動,收獲一種習慣;播下一種習慣,收獲一種性格;播下一種性格,收獲一種命運。"
2.35歲以前成功必備的9大習慣
好習慣會使成功不期而至。我認為下面9個好習慣是成功必備的:
(1)積極思維的好習慣
有位秀才第三次進京趕考,住在一個經常住的店里。考試前兩天他做了三個夢:第一個夢是夢到自己在墻上種白菜,第二個夢是下雨天,他戴了斗笠還打著傘,第三個夢是夢到跟心愛的表妹脫光了衣服躺在一起,但是背靠著背。臨考之際做此夢,似乎有些深意,秀才第二天去找算命的解夢。算命的一聽,連拍大腿說:"你還是回家吧。你想想,高墻上種菜不是白費勁嗎?戴斗笠打雨傘不是多此一舉嗎?跟表妹脫光了衣服躺在一張床上,卻背靠背,不是沒戲嗎?"秀才一聽,心灰意冷,回店收拾包裹準備回家。店老板非常奇怪,問:"不是明天才考試嗎?今天怎么就打道回府了?"秀才如此這般說了一番,店老板樂了:"唉,我也會解夢的。我倒覺得,你這次一定能考中。你想想,墻上種菜不是高種嗎?戴斗笠打傘不是雙保險嗎?跟你表妹脫光了背靠背躺在床上,不是說明你翻身的時候就要到了嗎?"秀才一聽,更有道理,于是精神振奮地參加考試,居然中了個探花。
可見,事物本身并不影響人,人們只受到自己對事物看法的影響,人必須改變被動的思維習慣,養成積極的思維習慣。
怎樣才算養成了積極思維的習慣呢?當你在實現目標的過程中,面對具體的工作和任務時,你的大腦里去掉了"不可能"三個字,而代之以"我怎樣才能"時,可以說你就養成了積極思維的習慣了。
(2)高效工作的好習慣
一個人成功的欲望再強烈,也會被不利于成功的習慣所撕碎,而溶入平庸的日常生活中。所以說,思想決定行為,行為形成習慣,習慣決定性格,性格決定命運。你要想成功,就一定要養成高效率的工作習慣。
確定你的工作習慣是否有效率,是否有利于成功,我覺得可以用這個標準來檢驗:即在檢省自己工作的時候,你是否為未完成工作而感到憂慮,即有焦灼感。如果你應該做的事情而沒有做,或做而未做完,并經常為此而感到焦灼,那就證明你需要改變工作習慣,找到并養成一種高效率的工作習慣。
高效工作從辦公室開始:
1)了解你每天的精力充沛期。通常人們在早晨9點左右工作效率最高,可以把最困難的工作放到這時來完成。
2)每天集中一、兩個小時來處理手頭緊急的工作,不接電話、不開會、不受打擾。這樣可以事半功倍。
3)立刻回復重要的郵件,將不重要的丟棄。若任它們積累成堆,反而更費時間。
4)做個任務清單,將所有的項目和約定記在效率手冊中。手頭一定要帶著效率手冊以幫助自己按計劃行事。一個人一天的行為中,大約只有5%是屬于非習慣性的,而剩下的95%的行為都是習慣性的。
5)學會高效地利用零碎時間,用來讀點東西或是構思一個文件,不要發呆或做白日夢。
6)減少回電話的時間。如果你需要傳遞的只是一個信息,不妨發個手機短信。
7)對可能打來的電話做到心中有數,這樣在你接到所期待的電話后便可迅速找到所需要的各種材料,不必當時亂翻亂找。
8)學習上網高效搜尋的技能,以節省上網查詢的時間。把你經常要瀏覽的網站收集起來以便隨時找到。
9)用國際互聯網簡化商業旅行的安排。多數飯店和航線可以網上查詢和預訂。
10)只要情況允許就可委派別人分擔工作。事必躬親會使自己疲憊不堪,而且永遠也做不完。不妨請同事幫忙,或讓助手更努力地投入。
11)做靈活的日程安排,當你需要時便可以忙中偷閑。例如,在中午加班,然后早一小時離開辦公室去健身,或是每天工作10個小時,然后用星期五來赴約會、看醫生。
12)在離開辦公室之前開列次日工作的清單,這樣第二天早晨一來便可以全力以赴。
計劃習慣,就等于計劃成功。
凡事制定計劃有個名叫約翰·戈達德的美國人,當他15歲的時候,就把自己一生要做的事情列了一份清單,被稱做"生命清單"。在這份排列有序的清單中,他給自己所要攻克的127個具體目標。比如,探索尼羅河、攀登喜馬拉雅山、讀完莎士比亞的著作、寫一本書等。在44年后,他以超人的毅力和非凡的勇氣,在與命運的艱苦抗爭中,終于按計劃,一步一步地實現了106個目標,成為一名卓有成就的電影制片人、作家和演說家。
中國有句老話:"吃不窮,喝不窮,沒有計劃就受窮。"盡量按照自己的目標,有計劃地做事,這樣可以提高工作效率,快速實現目標。
(3)養成鍛煉身體的好習慣
增強保健意識
計劃習慣,就等于計劃成功。如果你想成就一番事業,你就必須有一個健康的身體;要想身體健康,首先要有保健意識。
我認識一個大學教師,身體一直很健康。早些時候,我們經常在一起玩。在談及各人身體狀況時,他說腎部偶爾有輕微不適的感覺。我們曾勸他去醫院檢查一下,但他自恃身體健康,不以為意。直至后來感覺比較疼痛,其愛人才強迫他去檢查。診斷結果是晚期腎癌。雖經手術化療的等治療措施,但終未能保住生命,死時才39歲。此前,他曾因學校分房、評職稱不如意,心情一直抑郁,他的病和情緒有關,但如果他保健意識強,及早去檢查,完全有可以進行預防,消患于未萌。保健意識差,讓他付出了生命的代價。
如何落實保健意識呢?一是要有生命第一、健康第一的意識,有了這種意識,你就會善待自己的身體、自己的心理,而不會隨意糟踏自己的身體。二是要注意掌握一些相關的知識。三是要使自己有一個對身體應變機制:定期去醫院做身體檢查;身體覺得有不適的地方,應及早去醫院檢查;在有條件的情況下,可以請一個保健醫生,給自己的健康提出忠告。
▲有計劃地鍛煉身體
鍛煉身體的重要性已經越來越多地為人們所接受,但我感覺很多人只停留在重視的意識階段,而缺乏相應的行動。我認為鍛煉既要針對特定工作姿勢所能引發的相應疾病有目的地進行,以防止和治療相應的疾病,更要把鍛煉當作一種樂趣,養成鍛煉的習慣。
因為工作需要,我經常與客戶打交道,并因處理突發事情四處奔忙,這在一定程度起到了鍛煉身體的作用,同時,我還每周堅持游泳一到兩次,以保證有足夠的精力去做工作,去享受生活。
身體鍛煉,就像努力爭取成功一樣,貴在堅持。
除上述兩點以,注意飲食結構,合理膳食,以及注意養成好的衛生習慣等,都是養成健康習慣的組成部分。
總之,健康是"革命"的本錢,是成功的保證。健康成就自己。
(4)不斷學習的好習慣"萬般皆下品,唯有讀書高"的年代已經過去了,但是養成讀書的好習慣則永遠不會過時。
哈利·杜魯門是美國歷史上著名的總統。他沒有讀過大學,曾經營農場,后來經營一間布店,經歷過多次失敗,當他最終擔任政府職務時,已年過五旬。但他有一個好習慣,就是不斷地閱讀。多年的閱讀,使杜魯門的知識非常淵博。他一卷一卷地讀了《大不列顛百科全書》以及所有查理斯·狄更斯和維克多·雨果的小說。此外,他還讀過威廉·莎士比亞的所有戲劇和十四行詩等。
杜魯門的廣泛閱讀和由此得到的豐富知識,使他能帶領美國順利度過第二次世界大戰的結束時期,并使這個國家很快進入戰后繁榮。他懂得讀書是成為一流領導人的基礎。讀書還使他在面對各種有爭議的、棘手的問題時,能迅速做出正確的決定。例如,在20世紀50年代他頂住壓力把人們敬愛的戰爭英雄道格拉斯·麥克阿瑟將軍解職。
他的信條是:"不是所有的讀書人都是一名領袖,然而每一位領袖必須是讀書人。"
美國前任總統克林頓說:在19世紀獲得一小塊土地,就是起家的本錢;而21世紀,人們最指望得到的贈品,再也不是土地,而聯邦政府的獎學金。因為他們知道,掌握知識就是掌握了一把開啟未來大門的鑰匙。"
每一個成功者都是有著良好閱讀習慣的人。世界500家大企業的CEO至少每個星期要翻閱大概30份雜志或圖書資訊,一個月可以翻閱100多本雜志,一年要翻閱1000本以上。
世界500家大企業的CEO至少每個星期要翻閱大概30份雜志或圖書資訊,一個月可以翻閱100多本雜志,一年要翻閱1000本以上。如果你每天讀15分鐘,你就有可能在一個月之內讀完一本書。一年你就至少讀過12本書了,10年之后,你會讀過總共120本書!想想看,每天只需要抽出15分鐘時間,你就可以輕易地讀完120本書,它可以幫助你在生活的各方面變得更加富有。如果你每天花雙倍的時間,也就是半個小時的話,一年就能讀25本書--10年就是250本!
我覺得,每一個想在35歲以前成功的人,每個月至少要讀一本書,兩本雜志。
(5)謙虛的好習慣
一個人沒有理由不謙虛。相對于人類的知識來講,任何博學者都只能是不及格。
著名科學家法拉第晚年,國家準備授予他爵位,以表彰他在物理、化學方面的杰出貢獻,但被他拒絕了。法拉第退休之后,仍然常去實驗室做一些雜事。一天,一位年輕人來實驗室做實驗。他對正在掃地的法拉第說道:"干這活,他們給你的錢一定不少吧?"老人笑笑,說道:"再多一點,我也用得著呀。""那你叫什么名字?老頭?""邁克爾·法拉第。"老人淡淡地回答道。年輕人驚呼起來:"哦,天哪!您就是偉大的法拉第先生!""不",法拉第糾正說,"我是平凡的法拉第。"
謙虛不僅是一種美德,更是是一種人生的智慧,是一種通過貶低自己來保護自己的計謀。
(6)自制的好習慣
任何一個成功者都有著非凡的自制力。
三國時期,蜀相諸葛亮親自率領蜀國大軍北伐曹魏,魏國大將司馬懿采取了閉城休戰、不予理睬的態度對付諸葛亮。他認為,蜀軍遠道來襲,后援補給必定不足,只要拖延時日,消耗蜀軍的實力,一定能抓住良機,戰勝敵人。
諸葛亮深知司馬懿沉默戰術的利害,幾次派兵到城下罵陣,企圖激怒魏兵,引誘司馬懿出城決戰,但司馬懿一直按兵不動。諸葛亮于是用激將法,派人給司馬懿送來一件女人衣裳,并修書一封說:"仲達不敢出戰,跟婦女有什么兩樣。你若是個知恥的男兒,就出來和蜀軍交戰,若不然,你就穿上這件女人的衣服。""士可殺不可辱。"這封充滿侮辱輕視的信,雖然激怒了司馬懿,但并沒使老謀深算的司馬懿改變主意,他強壓怒火穩住軍心,耐心等待。
相持了數月,諸葛亮不幸病逝軍中,蜀軍群龍無首,悄悄退兵,司馬懿不戰而勝。
抑制不住情緒的人,往往傷人又傷己如果司馬懿不能忍耐一時之氣,出城應戰,那么或許歷史將會重寫。
現代社會,人們面臨的誘惑越來越多,如果人們缺乏自制力,那么就會被誘惑牽著鼻子走,偏離成功的軌道。
(7)幽默的好習慣
有人說,男人需要幽默,就像女人需要一個漂亮的臉蛋一樣重要。
男人需要幽默,就像女人需要一個漂亮的臉蛋一樣重要。美國第16任總統林肯長相丑陋,但他從不忌諱這一點,相反,他常常詼諧地拿自己的長相開玩笑。在競選總統時,他的對手攻擊他兩面三刀,搞陰謀詭計。林肯聽了指著自己的臉說:"讓公眾來評判吧。如果我還有另一張臉的話,我會用現在這一張嗎?"還有一次,一個反對林肯的議員走到林肯跟前挖苦地問:"聽說總統您是一位成功的自我設計者?""不錯,先生。"林肯點點頭說,"不過我不明白,一個成功的設計者,怎么會把自己設計成這副模樣?"林肯就是這種幽默的方法,多次成功地化解了可能出現的尷尬和難堪場面。
沒有幽默的男人不一定就差,但懂得幽默的男人一定是一個優秀的人,懂得幽默的女人更是珍稀動物。
(8)微笑的好習慣
微笑是大度、從容的表現,也是交往的通行證。
舉世聞名的希爾頓大酒店,其創建人希爾頓在創業之初,經過多年探索,最終發現了一條簡單、易行、不花本錢的經營秘訣--微笑。從此,他要求所有員工:無論飯店本身遭遇到什么困難,希爾頓飯店服務員臉上的微笑永遠是屬于顧客的陽光。這束"陽光"最終使希爾頓飯店贏得了全世界一致好評。
在歐美發達國家,人們見面都要點頭微笑,使人們相互之間感到很溫暖。而在中國,如果你在大街上向一個女士微笑,那么你可能被說成"有病"。向西方人學習,讓我們致以相互的微笑吧。
從古至今,敬業是所有成功人士最重要的品質之一。
(9)敬業、樂業的好習慣
敬業是對渴望成功的人對待工作的基本要求,一個不敬業的人很難在他所從事的工作中做出成績。
美國標準石油公司有一個叫阿基勃特的小職員,開始并沒有引起人們的特別注意。他的敬業精神特別強,處處注意維護和宣傳企業的聲譽。在遠行住旅館時總不忘記在自己簽名的下方寫上"每桶四美元的標準石油"字樣,在給親友寫信時,甚至在打收條時也不例外,簽名后總不忘記寫那幾個字。為此,同事們都叫他"每桶四美元"。這事被公司的董事長洛克菲勒知道了,他邀請阿基勃特共進晚餐,并號召公司職員向他學習。后來,阿基勃特成為標準石油公司的第二任董事長。
3.35歲以前成功必須戒除的9大惡習
壞習慣使成功寸步難行。
與建立良好習慣相應的,是克服不良習慣。不破不立,不改掉不良習慣,好習慣是難以建立起來的。
古希臘的佛里幾亞國王葛第士以非常奇妙的方法,在戰車的軛打了一串結。他預言:誰能打開這個結,就可以征服亞洲。一直到公元前334年還沒有一個人能將繩結打開。這時。亞歷山大率軍入侵小亞細亞,他來到葛第士繩結前,不加考慮便拔劍砍斷了它。后來,他果然一舉占領了比希臘大50倍的波斯帝國。
一個孩子在山里割草,不小心被毒蛇咬傷了腳。孩子疼痛難忍,而醫院在遠處的小鎮上。孩子毫不猶豫地用鐮刀割斷受傷的腳趾,然后忍著巨痛艱難地走到醫院。雖然缺少了一個腳趾,但這個孩子以短暫的疼痛保住了自己的生命。
改掉壞習慣,就應該有亞歷山大的氣概,就應有那個小孩的果斷和勇敢,徹底改掉壞習慣,讓好習慣引領自己走向成功。
以下這9大惡習是你必須戒除的:
1)經常性遲到。你上班或開會經常遲到嗎?遲到是造成使老板和同事反感的種子,它傳達出的信息:你是一個只考慮自己、缺乏合作精神的人。
2)拖延。雖然你最終完成了工作,但拖后腿使你顯得不勝任。為什么會產生延誤呢?如果是因為缺少興趣,你就應該考慮一下你的擇業;如果是因為過度追求盡善盡美,這毫無疑問會增多你在工作中的延誤。社會心理學專家說:很多愛拖延的人都很害怕冒險和出錯,對失敗的恐懼使他們無從下手。
3)怨天尤人。這幾乎是失敗者共同的標簽。一個想要成功的人在遇到挫折時,應該冷靜地對待自己所面臨的問題,分析失敗的原因,進而找到解決問題的突破口。
4)一味取悅他人。一個真正稱職的員工應該對本職工作內存在的問題向上級說明并提出相應的解決辦法,而不應該只是附和上級的決定。對于管理者,應該有嚴明的獎懲方式,而不應該做"好好先生",這樣做雖然暫時取悅了少數人,卻會失去大多數人的支持。
5)傳播流言。每個人都可能會被別人評論,也會去評論他人,但如果津津樂道的是關于某人的流言蜚語,這種議論最好停止。世上沒有不透風的墻,你今天傳播的流言,早晚會被當事人知道,又何必去搬石頭砸自己的腳?所以,流言止于智者。
6)對他人求全責備、尖酸刻薄。每個人在工作中都可能有失誤。當工作中出現問題時,應該協助去解決,而不應該一味求全責備。特別是在自己無法做到的情況下,讓自己的下屬或別人去達到這些要求,很容易使人產生反感。長此以往,這種人在公司沒有任何威信而言。
7)出爾反爾。已經確定下來的事情,卻經常做變更,就會讓你的下屬或協助員工無從下手。你做出的承諾,如果無法兌現,會在大家面前失去信用。這樣的人,難以擔當重任。
8)傲慢無禮。這樣做并不能顯得你高人一頭,相反會引起別人的反感。因為,任何人都不會容忍別人瞧不起自己。傲慢無禮的人難以交到好的朋友。人脈就是財脈,年輕時養成這種習慣的人,相信你很難取得成功。
9)隨大流。人們可以隨大流,但不可以無主見。如果你習慣性地隨大流,那你就有可能形成思維定勢,沒有自己的主見,或者既便有,也不敢表達自己的主見,而沒有主見的人是不會成功的。( 世界經理人)
沒能發財的十大原因
1、不明白財富的定義;不明白成功的鏡像規律。
汗,還真不知道什么叫財富。再怯怯地問一聲:啥叫鏡像啊?
2、沒有致富的心態與觀念
承認自己沒有,比如有致富心態的,看到一個鋪面門庭若市,他會想到那里到底賣什么東西這么吸引人,回去自己也搗鼓點賣。而我只是想擠進去買點便宜貨。
3、沒有理財與致富的規劃
又一條沒有。從來沒為自己做過規劃,劃了也堅持不了三個月。
4、理財方式與訓練方式不恰當
來論壇之后才學著理財。方法正在學習中。
5、大多數人沒有投資在高報酬的領域中
這個肯定啦,如果有誰知道投資可獲得高報酬的,除了股票和傳銷,請你馬上告訴我。
6、沒有用商業手法控制支出
這個我也有。想花錢的時候就告訴自己先花了再說,不考慮控制。
7、不善于創造把握理財與致富的機會
這個我覺得我沒有,總認為是機會還沒來,來了我當然要抓住。
8、無創新意識不知如何決策
承認。確實不愿意創新。聽了那么多關于貨幣基金的介紹,可我還是一門心思在儲蓄,因為懶、嫌麻煩。
9、未以正確的思考方式解決問題
不承認,我一直都在我認為正確的道路上行走著。
10、沒有找到一位理財與致富向導
確實沒有。不過來理財生活以后又感覺向導太多了,有點暈。(阿里巴巴)
三種策略讓你賺到100萬
大多數年輕人的目標是100萬元,而且是愈早實現愈好。但是根據網絡調查顯示,有七成人認為,30歲時至少應該先擁有10萬元存款,但卻只有一成七的人能夠辦到。這就表示有相當多的年輕人,連10萬元的目標都還沒能達成,百萬財富更是一個遙遠的夢想。
于是在社會上各種致富法紛紛出籠。譬如嫁入豪門、娶個富家女、每期買彩票,這些方法似乎是最快、但也是最不切實際的。到底有沒有機會靠著自己的努力,提早賺到百萬財富,答案當然是"有",這里有短、中、長期三套戰略,供你參考。
2年戰略:高杠桿工具才能小兵立大功
如果想兩年就賺到百萬財富,最可能實現夢想的途徑就是利用高杠桿投資工具。雖然風險超高,但是報酬也高,想要以小搏大、倍數獲利,就要正確運用這種工具。只要你對趨勢敏感,行情不論走多或是走空,都有獲利機會。
高杠桿投資憑借的不是運氣,而是精準判斷盤勢,冷靜面對大盤起落,情緒絕不隨著輸贏起舞。但所謂"高收益高風險",想要兩年就得到暴利,等于是走著鋼索賺錢,因為期貨或是選擇權杠桿高,當看錯趨勢時,幾十萬元很快就輸光出場,是一條風險最高的求財途徑。因此,先模擬練功并嚴格控制投資金額,是激進主義者最重要的自保之道。
5年戰略:做老板、當top sales
如果自認為用期指或是選擇權賺大錢,心臟不夠強、武藝不夠高的話,年限不妨放寬一點,定5年戰略,也就是努力創業當老板、甚至是加盟總部的老板、或是努力成為業務高手。
什么樣的創業能夠5年就凈賺百萬元,當然是要能引領潮流或是抓住特殊機遇的創業。
程度更高段的賺錢方法則是當一群老板的老板,也就是成立加盟連鎖總部,只要能夠研發出獨特口味、或是獨特經營模式,而且能夠復制標準化程序,穩定收取加盟店上繳的權利金。
當然創業的成本高,學問也很大。如果不愿意當老板,只想繼續當伙計賺大錢,不妨選擇產品單價高、抽傭也高、制度完善的業務體系,只要用對方法,就可以成為個中高手
10年戰略:運用多種工具保守理財
如果自認投資手段不佳,也不適合創業當老板,或是不擅與人打交道,無法成為業務高手的話,那么便得回歸正統的理財管道,將累積財富的時間拉長至10年,積極開源、努力儲蓄守成,透過定期儲蓄,或是投資定存概念股,每年賺取股利,或是把錢交給專家理財,透過定期定額基金投資,逐步累積資產。
更傳統的方式是投資房地產,雖然國內房地產價格還有向下修正的空間,但只要選對地段,還是可以找到極具增值潛力的房子,不管是自住或投資,都是一種穩健的資產累積方式。
你是屬于急功近利型的兔子?還是穩扎穩打型的烏龜?其實都有適合的致富計劃,但要再提醒的是,不管選擇哪一種計劃,想要提前致富,一定要做足功課,懂得深入領受實踐,百萬財富將不是遙遠夢想!(理財加油站)
posted @
2008-12-19 08:07 幽幽 閱讀(267) |
評論 (0) |
編輯 收藏
彩色圖像轉換為黑白圖像時需要計算圖像中每像素有效的亮度值,通過匹配像素
亮度值可以輕松轉換為黑白圖像。
計算像素有效的亮度值可以使用下面的公式:
Y=0.3RED+0.59GREEN+0.11Blue
然后使用 Color.FromArgb(Y,Y,Y) 來把計算后的值轉換
轉換代碼可以使用下面的方法來實現:
C#
1
public Bitmap ConvertToGrayscale(Bitmap source)
2
3

{
4
5
Bitmap bm = new Bitmap(source.Width,source.Height);
6
7
for(int y=0;y<bm.Height;y++)
8
9
{
10
11
for(int x=0;x<bm.Width;x++)
12
13
{
14
15
Color c=source.GetPixel(x,y);
16
17
int luma = (int)(c.R*0.3 + c.G*0.59+ c.B*0.11);
18
19
bm.SetPixel(x,y,Color.FromArgb(luma,luma,luma));
20
21
}
22
23
}
24
25
return bm;
26
27
}
VB
1
Public Function ConvertToGrayscale()Function ConvertToGrayscale()Function ConvertToGrayscale()Function ConvertToGrayscale(ByVal source As Bitmap) as Bitmap
2
3
Dim bm as new Bitmap(source.Width,source.Height)
4
5
Dim x
6
7
Dim y
8
9
For y=0 To bm.Height
10
11
For x=0 To bm.Width
12
13
Dim c as Color = source.GetPixel(x,y)
14
15
Dim luma as Integer = CInt(c.R*0.3 + c.G*0.59 + c.B*0.11)
16
17
bm.SetPixel(x,y,Color.FromArgb(luma,luma,luma)
18
19
Next
20
21
Next
22
23
Return bm
24
25
End Function
posted @
2008-10-20 13:10 幽幽 閱讀(1007) |
評論 (0) |
編輯 收藏
從該DC中得到位圖數據
在DC加載了RGB24位圖,如何從該DC中得到位圖數據,給些代碼好嗎?
回復人: windows_editor(等咱有錢了,每天早上喝兩大碗豆漿)
LONG GetBitmapBits(
HBITMAP hbmp, // handle to bitmap
LONG cbBuffer, // number of bytes to copy
LPVOID lpvBits // buffer to receive bits
); //從位圖中取RGB值
SetBitmapBits 將位圖的數據加上頭格式轉化為位圖
回復人: bluebohe(薄荷)
HBITMAP GetSrcBit(HDC hDC,DWORD BitWidth, DWORD BitHeight)
{
HDC hBufDC;
HBITMAP hBitmap, hBitTemp;
//創建設備上下文(HDC)
hBufDC = CreateCompatibleDC(hDC);
//創建HBITMAP
hBitmap = CreateCompatibleBitmap(hDC, BitWidth, BitHeight);
hBitTemp = (HBITMAP) SelectObject(hBufDC, hBitmap);
//得到位圖緩沖區
StretchBlt(hBufDC, 0, 0, BitWidth, BitHeight,
hDC, 0, 0, BitWidth, BitHeight, SRCCOPY);
//得到最終的位圖信息
hBitmap = (HBITMAP) SelectObject(hBufDC, hBitTemp);
//釋放內存
DeleteObject(hBitTemp);
::DeleteDC(hBufDC);
return hBitmap;
}
BOOL SaveBmp(HBITMAP hBitmap, CString FileName)
{
//設備描述表
HDC hDC;
//當前分辨率下每象素所占字節數
int iBits;
//位圖中每象素所占字節數
WORD wBitCount;
//定義調色板大小, 位圖中像素字節大小 ,位圖文件大小 , 寫入文件字節數
DWORD dwPaletteSize=0, dwBmBitsSize=0, dwDIBSize=0, dwWritten=0;
//位圖屬性結構
BITMAP Bitmap;
//位圖文件頭結構
BITMAPFILEHEADER bmfHdr;
//位圖信息頭結構
BITMAPINFOHEADER bi;
//指向位圖信息頭結構
LPBITMAPINFOHEADER lpbi;
//定義文件,分配內存句柄,調色板句柄
HANDLE fh, hDib, hPal,hOldPal=NULL;
//計算位圖文件每個像素所占字節數
hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
DeleteDC(hDC);
if (iBits <= 1) wBitCount = 1;
else if (iBits <= 4) wBitCount = 4;
else if (iBits <= 8) wBitCount = 8;
else wBitCount = 24;
GetObject(hBitmap, sizeof(Bitmap), (LPSTR)&Bitmap);
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = Bitmap.bmWidth;
bi.biHeight = Bitmap.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = wBitCount;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrImportant = 0;
bi.biClrUsed = 0;
dwBmBitsSize = ((Bitmap.bmWidth * wBitCount + 31) / 32) * 4 * Bitmap.bmHeight;
//為位圖內容分配內存
hDib = GlobalAlloc(GHND,dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER));
lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
*lpbi = bi;
// 處理調色板
hPal = GetStockObject(DEFAULT_PALETTE);
if (hPal)
{
hDC = ::GetDC(NULL);
hOldPal = ::SelectPalette(hDC, (HPALETTE)hPal, FALSE);
RealizePalette(hDC);
}
// 獲取該調色板下新的像素值
GetDIBits(hDC, hBitmap, 0, (UINT) Bitmap.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER) +dwPaletteSize, (BITMAPINFO *)lpbi, DIB_RGB_COLORS);
//恢復調色板
if (hOldPal)
{
::SelectPalette(hDC, (HPALETTE)hOldPal, TRUE);
RealizePalette(hDC);
::ReleaseDC(NULL, hDC);
}
//創建位圖文件
fh = CreateFile(FileName, GENERIC_WRITE,0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (fh == INVALID_HANDLE_VALUE) return FALSE;
// 設置位圖文件頭
bmfHdr.bfType = 0x4D42; // "BM"
dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize;
bmfHdr.bfSize = dwDIBSize;
bmfHdr.bfReserved1 = 0;
bmfHdr.bfReserved2 = 0;
bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize;
// 寫入位圖文件頭
WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
// 寫入位圖文件其余內容
WriteFile(fh, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL);
//清除
GlobalUnlock(hDib);
GlobalFree(hDib);
CloseHandle(fh);
return TRUE;
}
posted @
2008-09-28 16:19 幽幽 閱讀(2274) |
評論 (0) |
編輯 收藏
轉自CSDN
在所有的預處理指令中,#Pragma 指令可能是最復雜的了,它的作用是設定編譯器的狀態或者是指示編譯器完成一些特定的動作。#pragma指令對每個編譯器給出了一個方法,在保持與C和C++語言完全兼容的情況下,給出主機或操作系統專有的特征。依據定義,編譯指示是機器或操作系統專有的,且對于每個編譯器都是不同的。
其格式一般為: #Pragma Para
其中Para 為參數,下面來看一些常用的參數。
(1)message 參數。 Message 參數是我最喜歡的一個參數,它能夠在編譯信息輸出窗
口中輸出相應的信息,這對于源代碼信息的控制是非常重要的。其使用方法為:
#Pragma message(“消息文本”)
當編譯器遇到這條指令時就在編譯輸出窗口中將消息文本打印出來。
當我們在程序中定義了許多宏來控制源代碼版本的時候,我們自己有可能都會忘記有沒有正確的設置這些宏,此時我們可以用這條指令在編譯的時候就進行檢查。假設我們希望判斷自己有沒有在源代碼的什么地方定義了_X86這個宏可以用下面的方法
#ifdef _X86
#Pragma message(“_X86 macro activated!”)
#endif
當我們定義了_X86這個宏以后,應用程序在編譯時就會在編譯輸出窗口里顯示“_
X86 macro activated!”。我們就不會因為不記得自己定義的一些特定的宏而抓耳撓腮了
。
(2)另一個使用得比較多的pragma參數是code_seg。格式如:
#pragma code_seg( ["section-name"[,"section-class"] ] )
它能夠設置程序中函數代碼存放的代碼段,當我們開發驅動程序的時候就會使用到它。
(3)#pragma once (比較常用)
只要在頭文件的最開始加入這條指令就能夠保證頭文件被編譯一次,這條指令實際上在VC6中就已經有了,但是考慮到兼容性并沒有太多的使用它。
(4)#pragma hdrstop表示預編譯頭文件到此為止,后面的頭文件不進行預編譯。BCB可以預編譯頭文件以加快鏈接的速度,但如果所有頭文件都進行預編譯又可能占太多磁盤空間,所以使用這個選項排除一些頭文件。
有時單元之間有依賴關系,比如單元A依賴單元B,所以單元B要先于單元A編譯。你可以用#pragma startup指定編譯優先級,如果使用了#pragma package(smart_init) ,BCB就會根據優先級的大小先后編譯。
(5)#pragma resource "*.dfm"表示把*.dfm文件中的資源加入工程。*.dfm中包括窗體
外觀的定義。
(6)#pragma warning( disable : 4507 34; once : 4385; error : 164 )
等價于:
#pragma warning(disable:4507 34) // 不顯示4507和34號警告信息
#pragma warning(once:4385) // 4385號警告信息僅報告一次
#pragma warning(error:164) // 把164號警告信息作為一個錯誤。
同時這個pragma warning 也支持如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
這里n代表一個警告等級(1---4)。
#pragma warning( push )保存所有警告信息的現有的警告狀態。
#pragma warning( push, n)保存所有警告信息的現有的警告狀態,并且把全局警告
等級設定為n。
#pragma warning( pop )向棧中彈出最后一個警告信息,在入棧和出棧之間所作的
一切改動取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
//.......
#pragma warning( pop )
在這段代碼的最后,重新保存所有的警告信息(包括4705,4706和4707)。
(7)pragma comment(...)
該指令將一個注釋記錄放入一個對象文件或可執行文件中。
常用的lib關鍵字,可以幫我們連入一個庫文件。
posted @
2008-08-19 11:01 幽幽 閱讀(291) |
評論 (0) |
編輯 收藏
一種給窗口添加陰影的方法 華南理工大學微軟技術俱樂部 |
|
因為自己很喜歡那些界面做得很漂亮的軟件或者使用各種美化界面的軟件,如avedesk,samurize等等。其中美化界面的一個重要的方面就是給窗口添加上陰影。雖然OS X已經原生的支持窗口陰影,但是windows要到Longhorn才開始支持原生的窗口陰影。現在如果想實現窗口陰影,一般都會借助第三方的軟件,例如windowFX或者YzShadow。其中YzShadow是一個免費軟件,我自己也在使用。但是這個軟件有個弱點,就是無法為Layered Window添加陰影。而我自己編寫的一個簡易便條程序Stickies(功能類似OneNote,但是功能簡單,小巧。)就是運用了Layered Window來作為軟件的界面,于是便自己嘗試添加窗口陰影。以下便是添加陰影的方法,寫下來與大家討論一下。
我的程序是在Visual Studio.NET 2003下編寫的MFC應用程序。我為了實現窗口陰影創建了一個Shadow的類。首先我們看看各類之間的關系:

1. Class ShadowCastingWindow 該類是一個應用程序的窗口,它會在桌面上投射下陰影。這個類是從CWnd繼承而來。 ShadowCastingWindow成員變量: m_Alpha 保存該窗口的透明度值。
ShadowCastingWindow成員函數: BOOL ShadowCastingWindow::CreateWindow( CString wndName, CWnd * pParentWnd ) { BOOL tmp = CWnd::CreateEx( WS_EX_TOOLWINDOW|WS_EX_LAYERED , … , WS_POPUP|WS_VISIBLE , … ); m_Shadow.CreateShadow( this, m_Alpha ); }
該函數用于創建應用程序的窗口并創建陰影。請留意CreateEx中窗口屬性WS_EX_...和WS_...的取值,這使得該應用程序的窗口是一個沒有標題欄的Layered Windows。是否有標題欄對于下文Shadow類中求遮擋窗口的大小會有所不同,這必須通過一個判斷邏輯或者根據程序的應用不同編寫好代碼。對于Layered Windows會有兩種刷新模式,一種就是傳統的消息機制,就是操作系統自動地在適當的時候發送WM_PAINT的消息給應用程序窗口,應用程序窗口則相應該消息,對窗口進行刷新;另一種方式則是在Windows2000以后才支持的UpdateLayeredWindow的機制,在這種機制下,應用程序不再處理WM_PAINT消息,所有的刷新均由用戶在內存中的一個繪圖上下文中繪制好圖像之后再通過UpdateLayeredWindow繪制到屏幕上,只要經過一次繪制,窗口的圖像便會保存在一塊預訂好的內存區域內,如果窗口的圖像沒有改變那么操作系統便會自動地處理刷新。
void ShadowCastingWindow::OnSizing(UINT fwSide, LPRECT pRect) { CWnd::OnSizing(fwSide, pRect); m_Shadow.OnShadowCastingWndNewSize(pRect->right - pRect->left, pRect->bottom - pRect->top); }
void ShadowCastingWindow::OnSize(UINT nType, int cx, int cy) { CWnd::OnSize(nType, cx, cy); m_Shadow.OnShadowCastingWndNewSize(cx,cy); }
void ShadowCastingWindow::OnMoving(UINT fwSide, LPRECT pRect) { CWnd::OnMoving(fwSide, pRect); m_Shadow.OnShadowCastingWndNewPos(pRect->left, pRect->top ); }
void ShadowCastingWindow::OnMove(int x, int y) { CWnd::OnMove(x, y); m_Shadow.OnShadowCastingWndNewPos(x, y ); }
這四個事件函數都是處理應用程序窗口大小或者位置變化的。只需要在其中調用Shadow類中相應的處理函數即可,Shadow便會自動地更改大小或者移動位置。可能有人會問為什么需要顯式地調整Shadow的位置和大小?因為從下文可以看到Shadow其實也是一個Layered Window,沒有父窗口,所以操作系統不可以自動地保持兩者的相對位置。
void ShadowCastingWindow::SetOpacity( int alpha ) { m_Alpha = alpha; m_Shadow.SetAlpha( alpha ); SetLayeredWindowAttributes( 0, (BYTE)m_Alpha, LWA_ALPHA ); Invalidate(); }
處理應用程序窗口透明度變化的函數,其中調用了陰影Shadow對于透明度變化的處理函數。并刷新應用程序窗口。
2. Class Shadow 該類繼承與CWnd類。 Shadow成員變量: CWnd * m_pShadowCastingWindow; 指向父窗口—需要投射陰影的窗口的指針。 int m_Alpha; 當前陰影的透明度。 int m_DeltaTop; int m_DeltaLeft; int m_DeltaRight; int m_DeltaButtom; 用于表示陰影的尺寸。計算方法如下:

Shadow成員函數: BOOL Shadow::CreateShadow(CWnd * pShadowCastingWnd, int alpha ) { //根據投射陰影的窗口的尺寸和各參數計算出陰影的尺寸。 CRect rect; pShadowCastingWnd->GetWindowRect(&rect); rect.top += m_DeltaTop; rect.left -= m_DeltaLeft; rect.right += m_DeltaRight; rect.bottom += m_DeltaButtom; m_Alpha = alpha; BOOL tmp = CWnd::CreateEx( WS_EX_TOOLWINDOW|WS_EX_LAYERED ,… , WS_POPUP|WS_VISIBLE , rect , …);
m_IsCreated = true; CustomizedPaint(); return tmp; }
創建陰影,由于陰影必須是沒有標題欄的,而且因為要繪制半透明的像素所以必須使用Layered Window。
void Shadow::CustomizedPaint(void) { if ( !m_IsCreated ) return;
BLENDFUNCTION blendPixelFunction= {AC_SRC_OVER, 0, m_Alpha, AC_SRC_ALPHA}; POINT ptWindowScreenPosition = {rect.left, rect.top}; POINT ptSrc = {0, 0}; SIZE szWindow = {rect.Width(), rect.Height()};
CDC * dcScreen = GetDesktopWindow()->GetDC(); CDC dcMemory; dcMemory.CreateCompatibleDC( dcScreen );
//----------------------------------- //把要繪制的內容繪制在dcMemory里。對于Shadow需要把投射陰影窗口所覆蓋的區 //域剪裁掉 //-----------------------------------
UpdateLayeredWindow( dcScreen, &ptWindowScreenPosition, &szWindow, &dcMemory, &ptSrc, 0, &blendPixelFunction, ULW_ALPHA);
GetDesktopWindow()->ReleaseDC(dcScreen); dcMemory.DeleteDC(); } 根據不同程序需要加上適當的繪制流程。比如可以通過畫一個長方形來表示陰影,這個效果自然就比較差;也可以利用一些在Photoshop中處理好的陰影圖片把它做適當的大小調整作為窗口的陰影這樣更容易做出陰影邊緣柔化的效果。這個CustomizedPaint只需要在窗口的內容被改變的時候才需要重新調用,其他時候系統會自動管理已經繪制的圖像,用它來刷新窗口,而不需要重新繪制。
BOOL Shadow::PreCreateWindow(CREATESTRUCT& cs) { cs.style &= ~WS_BORDER; cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, ::LoadCursor( NULL, IDC_CURSOR ), NULL, NULL); return CWnd::PreCreateWindow(cs); } 通過修改默認的cs.lpszClass使得窗口不再自動重畫背景。
void Shadow::OnShadowCastingWndNewSize( int x, int y ) { if ( !m_IsCreated ) return; SetWindowPos( m_pShadowCastingWindow,0,0,x+m_DeltaLeft+m_DeltaRight,y-m_DeltaTop+m_DeltaButtom, SWP_NOMOVE ); CustomizedPaint(); }
提供該投射陰影的窗口的接口函數,當投射陰影的窗口大小改變的時候便調用這個函數把新的窗口位置傳給Shadow,Shadow便會改變自己的大小,并重繪窗口。
void Shadow::OnShadowCastingWndNewPos( int x, int y ) { if ( !m_IsCreated ) return; SetWindowPos( m_pShadowCastingWindow, x-m_DeltaLeft, y+m_DeltaTop, 0, 0, SWP_NOSIZE ); }
提供該投射陰影的窗口的接口函數,當投射陰影的窗口位置改變的時候便調用這個函數把新的窗口位置傳給Shadow,Shadow便會改變自己的位置。注意了,這里并不需要重繪窗口,因為窗口的內容并沒有改變。
void Shadow::SetAlpha( int alpha ) { m_Alpha = alpha; CustomizedPaint(); }
提供該投射陰影的窗口的接口函數,當投射陰影的窗口的透明度改變的時候便調用這個函數把新透明度傳給Shadow,Shadow便會改變自己的透明度,并重繪窗口。
下面給出一個我自己寫的程序的效果圖:

3. 結論:
上面就簡單地介紹了一個繪制窗口陰影的方法。這種方法基本上可以適用于各種類型的窗口,其中需要注意一下幾點:
1. 在于Shadow::CreateShadow中如何正確取得投射陰影窗口m_pShadowCastingWindow的大小然后計算出陰影窗口的大小。
2. Shadow::CustomizedPaint中如何更高效的繪制陰影,例如剪裁掉投射陰影窗口遮擋住的窗口內容,避免繪制時出現閃爍。同時如何正確使用好UpdateLayeredWindow這個系統調用會是實現繪制陰影的關鍵。當然在當前的設計下,我們可以在CustomizePaint中繪制任何的東西,而不一定是陰影。? 大家可以在這里發揮想象力,讓窗口更加絢麗多彩。
其實這個程序只要讓他通過鉤子函數與特定的Win32API掛鉤,完全可以寫出一個可以給系統中所有窗口加上陰影效果的小軟件。大家不妨試試。如果做出來了,記得給我一份。
注:文章中的代碼都是示意性的,都是通過我自己寫的程序刪減后得到,未必能通過測試。旨在說明一些關鍵步驟需要注意的地方。如果問題歡迎email討論。
|
posted @
2008-08-17 12:18 幽幽 閱讀(5417) |
評論 (3) |
編輯 收藏
這篇文章翻譯至MSDN2005,給自己學習,也給所有覺得它有用的人,文中難免有翻譯不到位或者錯誤的地方,望高手指正。譯者:歐昊川(轉載麻煩注明出處及譯者)
2008年5月4日
這個概述討論了窗口的一些特性,如窗口類型、狀態、大小及位置。
1、窗口類型(WindowStyles)
這一節描述層疊窗口、彈出窗口、子窗口、分層窗口、僅處理消息的窗口這五種類型。
1.1層疊窗口(OverlappedWindows)
層疊窗口是一個具有標題欄、邊框和客戶區的頂層窗口;也就是說它適合做為應用程序主窗口。它也可以具有一個系統菜單,最小和最大化按鈕,以及滾動條。一個層疊窗口被典型地用于包含所有上述組件的應用程序主窗口。
通過在CreateWindowEx中指定WS_OVERLAPPED或者WS_OVERLAPPEDWINDOW樣式,一個應用程序就能創建一個層疊窗口。假如你使用第一個樣式,那么創建的窗口就具有一個標題欄和邊框;假如你使用第二個,那么窗口就具有一個標題欄,可以調整大小的邊框,系統菜單,以及最大最小化按鈕。
1.2彈出窗口(Pop-upWindows)
彈出窗口是一個非凡的層疊窗口,它被用于顯示在應用程序主窗口之外的對話框,消息框以及其他臨時窗口。標題欄對彈出窗口來說是可選的;除此之外,彈出窗口跟具有WS_OVERLAPPED樣式的層疊窗口一樣。
你可以通過在CreateWindowEx中指定WS_POPUP樣式來創建一個彈出窗口。假如要使用標題欄,就加入WS_CAPTION樣式。使用WS_POPUPWINDOW樣式來創建一個含有邊框和系統菜單的彈出窗口。WS_CAPTION樣式必須與WS_POPUPWINDOW樣式一起使用才能使系統菜單可見。
1.3子窗口(ChildWindows)
子窗口具有WS_CHILD樣式并且它被限制在其父窗口的客戶區中。應用程序典型地使用子窗口來把其父窗口的客戶區劃分成幾個功能區域。你可以通過在CreateWindowEx中指定WS_CHILD樣式來創建子窗口。
子窗口必須具有一個父窗口。父窗口可以是一個層疊窗口,彈出窗口,或者另外一個子窗口。你可以在CreateWindowEx中指定父窗口。假如你在CreateWindowEx中指定了WS_CHILD樣式但是沒有指定父窗口,那么系統不會創建這個子窗口。
子窗口只具有一個客戶區而沒有其他特性,除非這些特性被明確的請求。應用程序可以為子窗口添加標題欄,系統菜單,最小化最大化按鈕,邊框,以及滾動條。但是子窗口不能具有自定義菜單。假如應用程序指定了一個自定義菜單句柄,那么無論是在它注冊這個子窗口類還是創建這個子窗口時,這個菜單句柄都被忽略。假如沒有指定邊框樣式,系統將創建一個無邊框窗口。應用程序可以使用無邊框的子窗口來劃分父窗口的客戶區假如想保持這種劃分對用戶是不可見的話。
下面一節討論窗口的布置、裁剪、與父窗口的關系、消息四個主題。
1.4窗口布置(Positioning)
系統總是相對于父窗口客戶區的左上角來放置子窗口。子窗口的任何部分都不會出現在其父窗口的邊框之外。假如應用程序創建一個比父窗口大的子窗口,或者移動子窗口使得一個或者所有子窗口超出了父窗口的邊框,那么系統會裁剪子窗口,即在父窗口邊框之外的部分不被顯示。對父窗口產生影響的行為同樣會影響子窗口,這些行為如下:
posted @
2008-08-16 13:38 幽幽 閱讀(1168) |
評論 (0) |
編輯 收藏
OLE拖放實現
MFC本身的CView類是支持拖放操作的,通過研究CView類的源碼,大體知道它的實現原理是這樣的:CView類中有一個COleDropTarget類的對象,在視圖窗口初始化時,調用COleDropTarget類成員函數Register(),以此在系統中注冊該視圖窗口為拖放接收窗口。當進行拖放操作的鼠標指針處于視圖窗口范圍內時,COleDropTarge類會做出反應,它的OnDragEnter、OnDragOver、OnDropEx、OnDrop等成員函數被依次調用,這些函數默認均是調用與其相對應的CView類成員函數OnDragEnter、OnDragOver、OnDropEx、OnDrop等,程序員只需重載這些CView類成員函數,即可對拖動的過程及結果進行控制。
因為COleDropTarget默認只對CView提供支持,所以如果要讓其他的窗口支持拖放,我們必須同時對要支持拖放的窗口類和COleDropTarget類進行派生。把對拖放操作具體進行處理的代碼封裝成派生窗口類的成員函數,然后重載COleDropTarget中對應的五個虛函數,當它接收到拖放動作時,調用窗口派生類的處理函數即可。但這里有一個問題,就是我們怎么知道何時調用派生類的處理函數呢?答案是運用RTTI技術。如果COleDropTarget派生類收到的窗口指針類型,就是我們派生的窗口類,那么就調用它的處理函數,否則調用基類進行處理。
首先生成一個對話框工程,添加二個新類。
第一個類名為CListCtrlEx,父類為CListCtrl。添加完畢后,在CListCtrlEx的定義頭文件中加入DECLARE_DYNAMIC(CListCtrlEx),在其實現文件中加入IMPLEMENT_DYNAMIC(CListCtrlEx,CListCtrl),這樣就對CListCtrlEx類添加了RTTI運行期類型識別(Run Time Type Information)支持。
第二個類名為COleDropTargetEx,父類為COleDataTarget。
在CListCtrlEx中添加COleDropTargetEx類的對象,并添加下列公有虛函數的聲明:
virtual BOOL Initialize();
virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
virtual DROPEFFECT OnDropEx(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, DROPEFFECT dropList, CPoint point);
virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point);
virtual void OnDragLeave(CWnd* pWnd);
Initialize函數用于注冊CListCtrlEx成為拖放接收窗口;
OnDragOver在拖放鼠標進入窗口時被調用。此函數的返回值決定了后續的動作的類型:如果返回DROPEFFECT_MOVE,則產生一個剪切動作;如果返回DROPEFFECT_COPY,則產生一個復制動作,如果返回DROPEFFECT_NONE,則不會產生拖放動作,因為OnDropEx、OnDrop函數將不會被調用(OnDragLeave函數仍會被調用)。
OnDropEx函數會在OnDrop函數之前調用,如果OnDropEx函數沒有對拖放動作進行處理,則應用程序框架會接著調用OnDrop函數進行處理。所以必須要在派生類中重載OnDropEx函數——即使什么動作都都沒有做——否則我們的OnDrop函數將不會被執行到,因為沒有重載的話,將會調用基類的OnDropEx函數,而基類的OnDropEx函數對拖放是進行了處理的——盡管不是我們所想要的動作。當然你也可以把對拖放進行處理的動作放在OnDropEx中——那樣就不需要重載OnDrop了。
OnDragLeave函數會在鼠標離開窗口時被調用,在此可以進行一些簡單的清理工作。譬如在OnDragEnter或者OnDragOver函數中,我們改變了光標的形態,那么此時我們就應該把光標恢復過來。
這些函數中最重要的是OnDrop函數,拖放動作將在此進行處理,它的全部源碼如下:
BOOL CListCtrlEx::OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
{
UINT nFileCount = 0;
HDROP hDropFiles = NULL;
HGLOBAL hMemData = NULL;
AfxMessageBox("OnDrop");
if(pDataObject->IsDataAvailable(CF_HDROP))
{
hMemData = pDataObject->GetGlobalData(CF_HDROP);
hDropFiles = (HDROP)GlobalLock((HGLOBAL)hMemData); //鎖定內存塊
if(hDropFiles != NULL)
{
char chTemp[_MAX_PATH+1] = {0};
nFileCount = DragQueryFile(hDropFiles, 0xFFFFFFFF, NULL, 0);
for(UINT nCur=0; nCur<nFileCount; ++nCur) //遍歷取得每個文件名
{
ZeroMemory(chTemp, _MAX_PATH+1);
DragQueryFile(hDropFiles, nCur, (LPTSTR)chTemp, _MAX_PATH+1);
AddAllFiles(chTemp);
}
}
GlobalUnlock(hMemData);
return TRUE;
}
else
{
return FALSE;
}
}
在第二個類COleDropTarget中添加如下對應的函數:
virtual DROPEFFECT OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
virtual DROPEFFECT OnDropEx(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, DROPEFFECT dropList, CPoint point);
virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point);
virtual void OnDragLeave(CWnd* pWnd);
它們的動作都差不多:先用RTTI判斷窗口指針pWnd的類型,如果是CListCtrlEx,則調用CListCtrlEx中對應的處理函數,否則調用基類的處理函數。以OnDrop為例:
BOOL COleDropTargetEx::OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
{
CListCtrlEx* pListCtrlEx = NULL;
ASSERT_VALID(this);
ASSERT(IsWindow(pWnd->m_hWnd));
if(pWnd->IsKindOf(RUNTIME_CLASS(CListCtrlEx)))
{
pListCtrlEx = (CListCtrlEx*)pWnd;
return pListCtrlEx->OnDrop(pWnd, pDataObject, dropEffect, point);
}
else
{
return COleDropTarget::OnDrop(pWnd, pDataObject, dropEffect, point);
}
}
//倒霉的64K限制,只能再截斷了:(
至此,我們成功地為CListCtrlEx添加了文件拖入操作的支持。一個完整的拖放操作,還包括拖出動作,所以必須要為該類再添加拖出操作,即,將列表中的某一項或者多項拖出成為一個文件。這就需要用到另一個類:COleDataSource。具體步驟如下:
在CListCtrlEx中加入一個COleDataSource的實例,并映射列表框的LVN_BEGINDRAG消息處理函數,在此我們添加拖出操作的代碼。
實現拖出非常簡單,只需要依次調用COleDataSource的三個函數即可:Empty用于清空原先對象中緩存的數據,CacheGlobalData用來緩存數據以進行拖放操作,最后調用DoDragDrop啟動本次拖放操作。
但在調用之前,必須要做一些準備工作。主要的任務就是創建一個DROPFILES結構體,并拷貝要拖放的文件名到結構體后的內存中。DROPFILES結構體定義了CF_HDROP剪貼板格式,緊跟它后面的是一系列被拖放文件的路徑名。它的定義如下:
typedef struct _DROPFILES
{
DWORD pFiles; //文件名起始地址
POINT pt; //鼠標放下的位置,坐標由fNC成員指定
BOOL fNC; //為TRUE表示適用屏幕坐標系,否則使用客戶坐標系
BOOL fWide; //文件名字符串是否使用寬字符
} DROPFILES, FAR* LPDROPFILES;
拖放之前的準備動作的代碼如下:
uBufferSize = sizeof(DROPFILES) + uBufferSize + 1;
hMemData = GlobalAlloc(GPTR,uBufferSize);
ASSERT(hMemData != NULL);
lpDropFiles = (LPDROPFILES)GlobalLock(hMemData); //鎖定之,并設置相關成員
ASSERT(lpDropFiles != NULL);
lpDropFiles->pFiles = sizeof(DROPFILES);
#ifdef _UNICODE
lpDropFiles->fWide = TRUE;
#else
lpDropFiles->fWide = FALSE;
#endif
//把選中的所有文件名依次復制到DROPFILES結構體后面(全局內存中)
pItemPos = strSelectedList.GetHeadPosition();
pszStart = (char*)((LPBYTE)lpDropFiles + sizeof(DROPFILES));
while(pItemPos != NULL)
{
lstrcpy(pszStart, (LPCTSTR)strSelectedList.GetNext(pItemPos));
pszStart = strchr(pszStart,'\0') + 1; //下次的起始位置是上一次結尾+1
}
準備完畢之后就可以進行拖放了,拖放動作有DoDragDrop函數觸發,其原型如下:
DROPEFFECT DoDragDrop(
DWORD dwEffects = DROPEFFECT_COPY|DROPEFFECT_MOVE|DROPEFFECT_LINK, LPCRECT lpRectStartDrag = NULL,
COleDropSource* pDropSource = NULL
);
這里,dwEffects指定了允許施加于本COleDataSource實例之上的動作集:剪切、復制或無動作。
lpRectStartDrag指示拖放操作真正開始的矩形,如果鼠標沒有移出該矩形,則拖放操作視作放棄處理。如果本成員設為NULL,則該起始矩形將為一個像素大小。
pDropSource表明拖放所使用的COleDataSource對象。
而該函數的返回值,則表明本次拖放操作所實際產生的效果,至于具體產生何種效果,則由系統決定。譬如在拖放時按住Shift鍵,將產生剪切效果;按住Ctrl鍵,將產生復制效果,等等。
拖放的代碼如下:
m_oleDataSource.Empty();
m_oleDataSource.CacheGlobalData(CF_HDROP, hMemData);
DropResult = m_oleDataSource.DoDragDrop(DROPEFFECT_MOVE|DROPEFFECT_COPY);
最后一點要注意的是,在Windows NT 4.0以上的系統中,即使實際產生的是DROPEFFECT_MOVE動作,DoDragDrop函數也只返回DROPEFFECT_NONE。產生這個問題的原因在于,Windows NT 4.0的Shell會直接移動文件本身來對移動操作進行優化。返回值DROPEFFECT_MOVE最初的含義,就是通知執行拖放操作的應用程序去刪除原位置上的文件。但是因為Shell已經替應用程序完成了這個(刪除)動作,所以,函數返回DROPEFFECT_NONE。要想知道文件是否真的被移動了也很簡單,只要在函數返回之后檢查一下原位置上的文件是否存在就可以了。
Windows 9x系列的操作系統也會對移動進行同樣的優化動作,但是它不會返回DROPEFFECT_NONE來代替DROPEFFECT_MOVE。詳細的解釋參見MS知識庫Q182219。
posted @
2008-08-14 19:19 幽幽 閱讀(3184) |
評論 (0) |
編輯 收藏