程序圖標的獲取
在我們的手機中,不同的程序有不同的圖標,怎么抓取這些程序的圖標呢?不用急啊,系統給我們提供了一個API:ExtractIconEx。
這個函數使用起來很簡便,下面是一個示例代碼,一看就會了。
已知程序路徑szFilePath
HICON hIcon = NULL;
ExtractIconEx(szFilePath, 0, NULL, &hIcon, 1);
函數具體怎么用呢?下面我們簡單看一下:
ExtractIconEx
作用
從指定的執行文件或DLL中獲得圖標句柄。
原型
HICON ExtractIconEx(
LPCTSTR lpszFile,
int nIconIndex,
HICON FAR* phiconLarge,
HICON FAR* phiconSmall,
UINT nIcons
);
參數
lpszFile:抽取圖標的執行文件或DLL文件的路徑
nIconIndex:第一個圖標的索引。如果是Windows CE 2.10或以后的版本,該值必須為0或-N,當N為指定資源標識。nIcons該值必須為1.
phiconLarge:從文件中抽取的大圖標句柄。
phiconSmall:從文件中抽取的小圖標句柄。
nIcons:從文件中抽取的圖標數
返回值
如果是Windows CE 2.10或以后的版本,該函數返回獲得的圖標數組的第一個圖標句柄。如果phiconLarge和phiconSmall都不為NULL的話,則返回值默認為第一個大圖標。
posted @
2009-07-23 16:03 Sandy 閱讀(1415) |
評論 (0) |
編輯 收藏
在指定目錄下創建文件夾
在博客里,有人問我這個問題:如何在指定目錄下創建文件夾。這個我還真的沒有做過。用CreateFile,似乎里面也沒有跟文件夾相關的任何東西。于是就在SDK中搜了一下,CreateDirectory。不錯,這個就可以用來創建文件夾。
下面來仔細學習一下這個API。
CreateDirectory
功能:
This function creates a new directory. If the underlying file system supports security on files and directories, the function applies a specified security descriptor to the new directory.
(創建一個新的文件夾。如果基本的文件系統在文件或文件夾上支持安全描述,那么該函數將在新建的文件夾上應用指定的安全描述)
原型:
BOOL CreateDirectory(
LPCTSTR lpPathName,
LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
參數:
lpPathName:包含將被創建的文件夾路徑的字符串,字符串的長度不超過MAX_PATH。
lpSecurityAttributes:忽略,設為NULL
返回值:
成功則返回非零,失敗則返回零。
備注:
Some file systems, such as NTFS file system, support compression or encryption for individual files and directories. On volumes formatted for such a file system, a new directory inherits the compression and encryption attributes of its parent directory.
In Windows CE 5.0 and later, full path canonicalization is performed before CreateDirectory processes a path name. As a result, trailing backslashes that may appear in a user-provided path name are ignored.
一些文件系統,像NTFS文件系統,對于個別的文件或目錄支持壓縮或加密。以卷格式化的文件系統,一個新的目錄將繼承它父目錄的壓縮和加密特性。
在Windows CE 5.0或以后的版本中,全路徑的規范化將在CreateDirectory處理路徑名字前執行。結果是,出現在用戶指定的路徑名中的反斜線將被忽略。
posted @
2009-07-21 18:14 Sandy 閱讀(2778) |
評論 (0) |
編輯 收藏
對話框顯示OK或CANCEL的簡便方法
最近很忙,有時也犯點懶。轉的東西多些,自己寫東西的時候少了好多。曾經豪言的100篇總結,似乎也只完成了5或6篇。離目標還有很遠的距離。所以繼續。
這次是一個很小的知識點,只是簡單的改變對話框的OK或CANCEL按鈕。我們的法寶是SHDoneButton函數。
我先給幾個小示例:
已知想要改變的窗口句柄hWnd;
// 顯示OK按鈕
SHDoneButton(hWnd, SHDB_SHOW);
// 顯示CANCEL按鈕
SHDoneButton(hWnd, SHDB_SHOWCANCEL);
我們再具體看一下這個函數的使用。原來我認為挺簡單的,仔細看了一下SDK,竟然還有那么多的注意事項,那我們來學習一下這個函數
SHDoneButton
功能:This function is provided for applications that need to dynamically show or hide the OK button based on the state of the application.(可以使應用程序動態的顯示或隱藏OK按鈕)
原型:
BOOL SHDoneButton(
HWND hwndRequester,
DWORD dwState
);
參數:
hwndRequester :[in] Handle to the top-level window requesting the Done button. (指定窗口)
dwState :[in] Specifies the button state. (指定按鈕的狀態)
按鈕的狀態有三個值,分別為:
SHDB_SHOW:添加屬性WS_EX_CAPTIONOKBTN到指定的窗口。當指定窗口成為最前的窗口時,OK按鈕將會顯現。注意指定窗口不能設置為樣式WS_CAPTION。
SHDB_HIDE:從指定窗口移出WS_EX_CAPTIONOKBTN屬性。下次當指定窗口成為最前窗口時,OK按鈕將不再顯示。
SHDB_SHOWCANCEL:將在窗口顯示[X]按鈕。當[X]按鈕按下的時候,將會發送一個WM_COMMAND消息,指定IDCANCEL值。
返回值:
成功則返回TRUE,失敗則返回FALSE。
備注:
Typically, the Done button (the OK button that appears in the upper-right corner of the screen) is managed by the shell, and showing or hiding the OK button happens automatically. A top-level window that needs the Done button to appear should use the following window styles:
Must have WS_EX_CAPTIONOKBTN
Must not have WS_CAPTION
WS_CHILD
Note WS_CAPTION is defined as (WS_BORDER
WS_DLGFRAME). To make the OK button appear, you must ensure that your window does not have either of these styles.
Whenever the foreground window changes, the shell checks the style bits of the window to determine if the OK button should appear in the navigation bar.
To suppress the OK button, use the WS_NONAVDONEBUTTON style.
(通常情況下,確定按鈕是由shell控制的,自動的顯示和隱藏OK按鈕。一個最頂層的窗口需要確定按鈕出現時需要使用下列窗口樣式:
必須具有WS_EX_CAPTIONOKBTN屬性
必須不能含有WS_CAPTION和WS_CHILD屬性樣式。
當最前窗口改變時,shell將檢查窗口的樣式來決定OK按鈕是否出現在導航按鈕中。)
對于這個備注,我沒有怎么注意過。所以為了驗證我這里做了一下實驗。發現WS_CAPTION這個屬性對OK按鈕的顯示沒有多大影響。只是顯示[X]按鈕沒有實驗出來。
posted @
2009-07-21 18:13 Sandy 閱讀(1187) |
評論 (0) |
編輯 收藏
設計模式學習筆記
最近利用早晨的大好時光,學習了一下設計模式,同時學習了英語。目前一共學習了八種模式:
一、 The Observer Pattern
It defines a one to many relationship between a set of objects. When the state of one object changes, all of its dependents are notified.
二、 The Decorator Pattern
It attaches additional responsibilities to an object dynamically.Decorators provide a flexible alternative to subclassing for extending functionality.
三、 The Factory Pattern
It defines an interface for creating an object, but lets subclasses decide which class to instantiate. Factory method lets a class defer instantiation to subclass.
四、 The Abstract Pattern
It provides an interface for creating families of related or dependent objects without specifying their concrete classes.
五、 The Singleton Pattern
It ensures a class has only one instance, and provides a global point of access to it.
六、 The Command Pattern
It encapsulates a request as an object, thereby letting you parameterize other objects with different requests, queue or log requests, and support undoable operations.
七、 The Adapter Pattern
It converts the interface of a class into another interface the clients expect. Adapter lets classes work together that couldn’t otherwise because of imcompatible interface.
八、 The Façade Pattern
It provides a unified interface to a set of interfaces in a subsystem. Façade defines a higher-level interface that makes the subsystem easier to use.
在這上述的八個模式中,包含了設計模式的三種類型:創建型模式,結構型模式,行為模式。其中Factory、Abstract Factory、Singleton為創建型模式,Decorator、Adapter、Facade為結構型模式,其余的Command和Observer為行為模式。
不過,感覺有時侯理解的特別清楚,但過一段時間就忘了。所以一定要將其用起來,才能對其有深深的體會,不會就是死的知識。一定要讓其活起來。
呵呵,又看了一些蝸牛的家寫的設計模式趣解,反而更有助于理解呦,不妨看看。鏈接地址為:http://m.shnenglu.com/bangle/archive/2008/08/23/59725.html
posted @
2009-07-21 18:11 Sandy 閱讀(379) |
評論 (0) |
編輯 收藏
摘自:
http://yulinlu.blog.163.com/blog/static/58815698200812284225975/
#include <windows.h>
#include <nled.h>

void LedOn(int id)


{
NLED_SETTINGS_INFO settings;
settings.LedNum= id;
settings.OffOnBlink= 1;
NLedSetDevice(NLED_SETTINGS_INFO_ID, &settings);
}
void LedOff(int id)


{
NLED_SETTINGS_INFO settings;
settings.LedNum= id;
settings.OffOnBlink= 0;
NLedSetDevice(NLED_SETTINGS_INFO_ID, &settings);
}

int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)


{
for (int i = 0; i < 10; i++)

{
LedOn(1);
Sleep(400);
LedOff(1);
Sleep(200);
}
return 0;
}
運行了一下,還蠻有意思的
posted @
2009-07-17 16:56 Sandy 閱讀(327) |
評論 (0) |
編輯 收藏
摘自: http://yulinlu.blog.163.com/blog/static/588156982008113111911557/
PerformCallBack4
強制令別的進程調用某個API,如果這個API是LoadLibrary的話,就相當于線程注入了,由coredll.dll提供
PerformCallBack4函數的定義:
[DllImport("coredll.dll")]
public static extern uint PerformCallBack4(ref CallBackInfo CallBackInfo,
IntPtr ni_pVoid1,IntPtr ni_pVoid2,IntPtr ni_pVoid3);
其中函數的參數CallBackInfo結構定義:
public struct CallBackInfo
{
public IntPtr hProc; //遠程的目標進程
public IntPtr pfn; //指向遠程目標進程的函數地址的指針
public IntPtr pvArg0; //函數的需要的第一個參數
}
而PerformCallback4的 ni_pVoid1、ni_pVoid2、ni_pVoid3為傳遞到遠程目標進程執行函數的其它三個參數。
例子:
/*-------------------------------------------------------------------
FUNCTION: CallCoredllInProc
PURPOSE: CallCoredllInProc uses undocumented method
PerformCallBack4 to call exported methods from coredll.dll in
the specified process.
PARAMETERS:
HANDLE p_hProcess - handle to the process, where the call should
be made
LPCTSTR p_pszMethodName - name of method exported from coredll,
such as VirtualAlloc, VirtualFree, etc.
DWORD p_dwParam1, p_dwParam2, p_dwParam3, p_dwParam4 - arguments
DWORD * p_pdwResult - pointer to the return value
RETURNS:
TRUE on success, FALSE on failure
-------------------------------------------------------------------*/
BOOL CallCoredllInProc
(
HANDLE p_hProcess,
LPCTSTR p_pszMethodName,
DWORD p_dwParam1, DWORD p_dwParam2,
DWORD p_dwParam3, DWORD p_dwParam4,
DWORD * p_pdwResult)
{
HINSTANCE l_hCoreDll = NULL;
BOOL l_bReturn = FALSE;
__try
{
//Use undocumented method PerformCallBack4
//to call method in NK.EXE.
CALLBACKINFO CallbackInfo;
CallbackInfo.m_hDestinationProcessHandle = p_hProcess;
l_hCoreDll = LoadLibrary(_T("COREDLL"));
CallbackInfo.m_pFunction =
(FARPROC)GetProcAddress(l_hCoreDll, p_pszMethodName);
if(!CallbackInfo.m_pFunction)
{
/*HTRACE(TG_Error,
_T("GetProcAddress(%x, %s) failed. Err %d"),
l_hCoreDll, p_pszMethodName, GetLastError());
*/
}
else
{
CallbackInfo.m_pFirstArgument = (LPVOID)p_dwParam1;
DWORD l_dwResult = PerformCallBack4
(&CallbackInfo, p_dwParam2, p_dwParam3, p_dwParam4);
if(p_pdwResult)
{
*p_pdwResult = l_dwResult;
}
l_bReturn = TRUE;
}
}
__except(1)
{
/*
HTRACE(TG_Error, _T("Exception in CallCoredllInProc(%s)"),
p_pszMethodName);
*/
l_bReturn = FALSE;
}
if(l_hCoreDll)
{
FreeLibrary(l_hCoreDll);
}
return l_bReturn;
}//BOOL CallCoredllInProc
CreateAPISet
CE6.0以前是個未公開API,不過6.0以后就公開了
This function creates an API set from the list of functions passed as a parameter.
Syntax
HANDLE CreateAPISet(
char acName[4],
USHORT cFunctions,
const PFNVOID *ppfnMethods,
const ULONGLONG *pu64Sig
);
Parameters
acName
[in] Name of the API set.
cFunctions
[in] Number of functions for this API set.
ppfnMethods
[in] Array of functions for the API set.
pu64Sig
[in] Array of signatures for the functions.
Return Value
A handle to the API set.
Remarks
Before any process can become a handle server, the process must create and register a handle-based API set with this function and RegisterAPISet.
Requirements
Header pkfuncs.h
Library coredll.lib
Windows Embedded CE Windows Embedded CE 6.0 and later
CE6.0以前在coredll.dll里面有這個函數
RegisterAPISet
CE6.0以前是個未公開API,不過6.0以后就公開了
This function registers an API set.
Syntax
BOOL RegisterAPISet(
HANDLE hASet,
DWORD dwSetID
);
Parameters
hASet
[in] Handle to API set created by the CreateAPISet function.
dwSetID
[in] Type of API set. You must perform a bitwise OR operation on this parameter with REGISTER_APISET_TYPE to create a handle-based API set.
Return Value
TRUE indicates success. FALSE indicates failure. Call GetLastError to get extended error information.
Remarks
Before any process can become a handle server, the process must create and register a handle-based API set with CreateAPISet and RegisterAPISet.
Requirements
Header pkfuncs.h
Library coredll.lib
Windows Embedded CE Windows Embedded CE 6.0 and later
CE6.0以前在coredll.dll里面有這個函數
QueryAPISetID
根據名字查詢該API的ID,由coredll.dll提供
Syntax
int QueryAPISetID(
char *pName
);
Parameters
pName
[in] API的名字
Return Value
API的ID
GetAPIAddress
獲取特定API的特定Method的地址,由coredll.dll提供
FARPROC GetAPIAddress(
int setId,
int iMethod
);
Parameters
setId
[in] API的ID
iMethod
[in] Method的ID
Return Value
該Method的地址
GetProcessIndexFromID
根據進程的ID計算出進程的序號(這個序號就是進程處于第幾個slot),由coredll.dll提供
Syntax
DWORD GetProcessIndexFromID(
HANDLE hProc
);
Parameters
hProc
[in] 進程的句柄,這里為什么不是進程的ID而是進程的句柄呢?非常簡單,因為在CE中進程的句柄就是進程的ID!
Return Value
進程的序號
posted @
2009-07-16 16:37 Sandy 閱讀(608) |
評論 (0) |
編輯 收藏
摘自:
HTTP://WWW.Yonsm.NET/read.php?296近日,由于程序設計需要,我對WincowsCE 的內存布局進行了研究,由于發現國內在這方面的文檔資料較少,于是在研究告一段落之際,形成這篇示例文檔,以望拋磚引玉,得到別的高手的指正。
一、程序實現的先決條件
由于windows系統的窗體消息總是投遞至一個特定進程的指定窗體消息函數中。于是在本地進程(自己的應用程序)中取得屬于其它進程的窗體的消息必須實現以下兩個部分:
1、將需要掛接窗體的代碼放到目標進程的地址空間中去?!?br> 2、執行這一段代碼,并獲得目標進程窗體的消息?!?br> 這兩步看起來很簡單,但在實現過程中就比較困難。由于Windows CE作為嵌入式移動設備操作系統,與windows 98/2000/XP等桌面操作系統在內核的設計理念以及API的支持上有極大的區別。這就直接導致了常規的桌面系統利用全局鼠標鉤子注入/遠程線程注入等方法在CE中完全得不通。不過可喜的是,微軟在開發工具中提供的remotexxx等遠程調試程序使我清楚這個目標并不是不可能的任務,微軟既然可以做到,那就是說在CE的內部一定有一套完整的跨進程內存訪問/代碼注入的機制?! ?br>二、程序實現的基本原理
經過兩天的google 搜索,在網上我發現了一個沒有在微軟文檔中聲明的有趣的API函數:PerformCallBack4,傳說中這個函數可以在自己的應用程序中執行指定的進程中的一個函數,So Cool!這好象正是我所需要的東西。雖然網上也傳聞這個函數在wm5不受支持,其實經過實踐這個傳聞只是謠傳而已! PerformCallBack4函數的定義:
[DllImport("coredll.dll")]
public static extern uint PerformCallBack4
(ref CallBackInfo CallBackInfo,
IntPtr ni_pVoid1,
IntPtr ni_pVoid2,
IntPtr ni_pVoid3);
其中函數的參數CallBackInfo結構定義:
[StructLayout(LayoutKind.Sequential)]
public struct CallBackInfo{public IntPtr hProc; //遠程的目標進程
public IntPtr pfn; //指向遠程目標進程的函數地址的指針
public IntPtr pvArg0; //函數的需要的第一個參數}
//end struct
而PerformCallback4的 ni_pVoid1、ni_pVoid2、ni_pVoid3為傳遞到遠程目標進程執行函數的其它三個參數?! ≈劣趯⒋a放到目標進程的內存空間,我們可以利用CE設計上的一個特性:
1、為了節約內存使用,CE將所有程序調用的動態鏈接庫(DLL)都映射到同一個內存地址中?! ?br>2、CE的內存布局中劃分有一個slot0的內存位置,這個內存位置是由正在執行的進程所占有的,每一個特定的時間片,只能有一個進程可以占有這個內存空間。在進程要求執行時,系統并不直接執行進程所處內存位置的代碼,而是將該進程的執行代碼復制到slot0的內存位置中產生一個副本執行。也就是說進程在執行時內存將會有進程執行代碼的兩個完全一樣的版本:存在于slot0中正在執行的進程代碼和進程本身所處的內存中的代碼。 在這個特性下,可以得到結論:如果進程A通過LoadLibrary函數裝載Test.dll,而進程B也通過LoadLibrary函數裝載同一個Test.dll,這個Test.dll的所有函數在進程A和進程B中執行時,相對于slot0中的進程執行代碼都會得到同一地址。
3、在CE中,系統在內存中劃分出33個slot,slot0保留給正在執行的進程,然后在進程啟動時將所有的代碼放到除slot0以外的一個slot中(這就是臭名昭著的CE系統中內存最多只能有不多于32個程序執行的限制的來由)。在進程執行時,每個應用程序的內存訪問默認只能訪問slot0內存空間中的地址以及進程所處的slot內存空間的地址。 但為使設備驅動程序可以訪問到它們所需的其它應用程序數據,CE提供了兩個函數以打破這個限制,SetKmode和SetProcPermission,SetKmode函數告訴系統,當前運行的進程是否需要在內核模式中執行;SetProcPermission函數可以接受一個位掩碼,每一位代碼一個slot的訪問控制,1代表可以訪問該slot的內存內容。0表示不能訪問該slot的內存內容。這兩個函數在msdn中有幫助文檔,可參閱msdn的文檔說明?! ?br> 本文我們對實現的原理進行了剖析,在下一篇文章中我們將以一個小示例程序演示實現的全過程。 在文章《淺析Windows CE跨進程內存注入實現窗體消息掛接(上)》中,我們已經得到了這個七巧板游戲所需要的所有小板塊,剩下的事就是等待我們按一定順序將合適的板塊放到合適的位置,本章我們開始進行真刀真槍的實戰演練。
程序目標:捕獲explore窗體(也就是程序窗體的消息并輸出到WinProcInfo.txt中)
程序的執行步驟設計如下:
1、編寫一個窗體消息掛接DLL,這個DLL提供一個,函數中利用setwindowlong函數將窗體的默認消息處理過程改為這個掛接DLL中定義的一個窗體過程。
2、在C#程序中利用findwindow等API函數獲得exlore類窗體的句柄及窗體所屬的進程,并使用performcallback4在目標進程空間中執行coredll.dll的loadLibrary函數將我們寫的掛接dll放到目標進程中。
3、在C#程序中使用performcallback4在目標進程空間中執行掛接DLL提供的導出接口函數實現跨進程窗體消息截獲.
一、程序的實現如下:
在VS2005中建立一個智能設備的MFC DLL,命名為HookWindowsProcMFCDLL。
在HookWindowsProcMFCDLL.cpp中進行掛接DLL的核心編碼:
LRESULT CALLBACK fnHookWindowProc(HWND hwnd,UINT msg,WPARAM wparam, LPARAM lparam);
int __declspec(dllexport) WINAPI fnAttachWinProc(HWND ni_hAttatchWin,PVOID ,PVOID,PVOID);
int __declspec(dllexport) WINAPI fnDetachWinMsgProc(HWND ni_hDetachWin);
WNDPROC tpOldWindowProc;
FILE *m_pDebugOutputFile;
//將一個窗體消息處理掛接到net精簡版MessageWindow對象上的代碼typedef struct{ WNDPROC OldWinProc;//保留窗體原始消息處理過程的函數指針 HWND WindowHandle;//保存net精簡版中對應的窗口掛接的MessageWindow對象的句柄} DEFUDT_AttachWinInfo; //end struct
CMap<HWND,HWND,DEFUDT_AttachWinInfo,DEFUDT_AttachWinInfo> m_aAttachWinInfoMap;
//對指定的窗口進程進行掛接
int __declspec(dllexport) WINAPI fnAttachWinProc(HWND ni_hAttatchWin,PVOID ni_0,PVOID ni_1,PVOID ni_2 )
{
DEFUDT_AttachWinInfo tudtAttachWinInfo;
m_pDebugOutputFile = fopen("\\Storage Card\\WinProcInfo.txt", "w");
WNDPROC tpOldWindowProc=(WNDPROC)::SetWindowLong(ni_hAttatchWin, GWL_WNDPROC,(LONG) fnHookWindowProc );
fprintf(m_pDebugOutputFile,"Attatch successfully! OldWindowProc: %08X\n",tpOldWindowProc); tudtAttachWinInfo.OldWinProc=tpOldWindowProc ;
tudtAttachWinInfo.WindowHandle=ni_hAttatchWin;
m_aAttachWinInfoMap.SetAt(ni_hAttatchWin,tudtAttachWinInfo);
fclose(m_pDebugOutputFile);
return 77; // (int)tpOldWindowProc ;
}//end function
int __declspec(dllexport) WINAPI fnDetachWinMsgProc(HWND ni_hDetachWin)
{
DEFUDT_AttachWinInfo tudtAttachWinInfo;
WNDPROC tpOldWindowProc; //取得在ncf中消息接收窗口對應的原始消息處理函數的函數指針 m_aAttachWinInfoMap.Lookup(ni_hDetachWin,tudtAttachWinInfo) ;//將窗體的消息處理函數設為默認的處理過程 tpOldWindowProc =(WNDPROC) SetWindowLong(ni_hDetachWin,GWL_WNDPROC , (LONG)tudtAttachWinInfo.OldWinProc); //將掛接信息消息處理映謝類中刪除
m_aAttachWinInfoMap.RemoveKey(ni_hDetachWin);
return (int)tpOldWindowProc ;
}//end function
LRESULT CALLBACK fnHookWindowProc(HWND hwnd,UINT msg,WPARAM wparam, LPARAM lparam)
{
DEFUDT_AttachWinInfo tudtAttachWinInfo;
m_aAttachWinInfoMap.Lookup(hwnd,tudtAttachWinInfo) ;
m_pDebugOutputFile = fopen("\\Storage Card\\WinProcInfo.txt", "a");
if (m_pDebugOutputFile!=NULL)
{
fprintf(m_pDebugOutputFile,"HWND: %08X Msg: %08X Wparam %08X Lparam %08X \n",hwnd,msg,wparam,lparam);
}//EHD IF
fclose(m_pDebugOutputFile);
//tudtAttachWin=maatt
LRESULT tobjResult= ::CallWindowProc(tudtAttachWinInfo.OldWinProc ,hwnd,msg,wparam,lparam);
return tobjResult;
}//end function
而在C#的主程序中,我們使用這個DLL掛接explore類的程序窗體,以下給出掛接部分的代碼:
int m_hTargetWindow;//要掛接的目標窗體句柄I
ntPtr m_hTargetProcess;//目標窗體所屬的進程
IntPtr m_hModule; //掛接DLL的句柄
private void Form1_Load(object sender, EventArgs e)
{
IntPtr tpTemp = IntPtr.Zero, tpTempa = IntPtr.Zero;
uint tuntApiRet;
m_hTargetWindow = (int)clsCECoreAPI.FindWindow("Explore", null ); //資源管理器 0x0013e800;
//掛接指定的進程窗體消息
IntPtr thCurrentProcess = clsCECoreAPI.GetCurrentProcess();
m_hTargetProcess=IntPtr.Zero ;// (IntPtr) (unchecked((int)0xedd84e4a));
tuntApiRet= clsCECoreAPI.GetWindowThreadProcessId(new IntPtr(unchecked((int) m_hTargetWindow)),ref m_hTargetProcess);
string tstrArgument;
tstrArgument = "\\Program Files\\processinject\\HookWindowsProcMFCDLL.dll";
// HookWindowsProcMFCDLL.dll";
IntPtr tpArg0;
int tintOriginalKMode = clsCECoreAPI.SetKMode(1);
int tintOriginalProcPermission = (int)clsCECoreAPI.SetProcPermissions(0xffffffff);
IntPtr tpFuncProc = clsCECoreAPI.GetProcAddress(clsCECoreAPI.GetModuleHandle("coredll.dll"), "LoadLibraryW");
CallBackInfo tudtCALLBACKINFO;
tpArg0 = clsCECoreAPI.MapPtrToProcess(tstrArgument, thCurrentProcess);
tudtCALLBACKINFO.hProc = m_hTargetProcess;// Proc;
tudtCALLBACKINFO.pfn = clsCECoreAPI.MapPtrToProcess(tpFuncProc, m_hTargetProcess); tudtCALLBACKINFO.pvArg0 = tpArg0;
m_hModule =new IntPtr(unchecked((int) clsCECoreAPI.PerformCallBack4(reftudtCALLBACKINFO,IntPtr.Zero,IntPtr.Zero,IntPtr.Zero )));
//clsCECoreAPI.Sleep(1000);
IntPtr thModule = clsCECoreAPI.LoadLibrary("HookWindowsProcMFCDLL.dll");
tpFuncProc = clsCECoreAPI.GetProcAddress(thModule, "fnAttachWinProc");
tpArg0 = (IntPtr) m_hTargetWindow;
// clsCECoreAPI.MapPtrToProcess(ref thTargetWindow, thCurrentProcess);
tudtCALLBACKINFO.hProc = m_hTargetProcess;
tudtCALLBACKINFO.pfn = clsCECoreAPI.MapPtrToProcess(tpFuncProc, m_hTargetProcess); tudtCALLBACKINFO.pvArg0 = tpArg0 ;
tuntApiRet = clsCECoreAPI.PerformCallBack4(reftudtCALLBACKINFO,IntPtr.Zero,IntPtr.Zero,IntPtr.Zero );
//clsCECoreAPI.Sleep(5000);
}
[DllImport("HookWindowsProcMFCDLL.dll")]
public static extern int fnAttachWinProc(IntPtr ni_hAttatchWin);
[DllImport("HookWindowsProcMFCDLL.dll")]
public static extern int fnDetachWinMsgProc(IntPtr ni_hDetachWin);
取消掛接的代碼根據上述代碼很容易就可以建立,不再細敘。
注:clsCECoreAPI的函數全是封裝的標準CE API,由于這些API在msdn 中都有詳細的文檔注釋,因篇幅所限,不再將代碼一一列舉.
在執行這個程序時,將模擬器的共享路徑設為PC機的桌面,這樣模擬器的storage card目錄就等同桌面了,點模擬器的開始菜單,選程序,你就可以看到explore窗體的消息都輸出到桌面的WinProcInfo.txt文件中了,運行結果如下:
目前本程序只在PPC2003/wm5 for PPC測試通過,由于smartphone系統在編譯時使用了和ppc系統不同的機制,內存運作不明,本程序在smartphone上無法正確運行,有好的建議的話請指教一二,謝謝.
作者:莫藝潛轉自:
http://cqreview.yesky.com/dev/185/2590685.shtml一個令我好奇的領域.
posted @
2009-07-16 16:11 Sandy 閱讀(475) |
評論 (0) |
編輯 收藏
轉: 異步非阻塞套接字Winsock開發網絡通信程序的經典入門
摘自:
http://hi.baidu.com/mcu%5Fspaces/blog/item/aee07a66ed816323ab184cdf.html對于許多初學者來說,網絡通信程序的開發,普遍的一個現象就是覺得難以入手。許多概念,諸如:同步(Sync)/異步(Async),阻塞(Block)/非阻塞(Unblock)等,初學者往往迷惑不清,只知其所以而不知起所以然。
異步方式指的是發送方不等接收方響應,便接著發下個數據包的通信方式;而同步指發送方發出數據后,等收到接收方發回的響應,才發下一個數據包的通信方式。
阻塞套接字是指執行此套接字的網絡調用時,直到成功才返回,否則一直阻塞在此網絡調用上,比如調用recv()函數讀取網絡緩沖區中的數據,如果沒有數據到達,將一直掛在recv()這個函數調用上,直到讀到一些數據,此函數調用才返回;而非阻塞套接字是指執行此套接字的網絡調用時,不管是否執行成功,都立即返回。比如調用recv()函數讀取網絡緩沖區中數據,不管是否讀到數據都立即返回,而不會一直掛在此函數調用上。在實際Windows網絡通信軟件開發中,異步非阻塞套接字是用的最多的。平常所說的C/S(客戶端/服務器)結構的軟件就是異步非阻塞模式的。
對于這些概念,初學者的理解也許只能似是而非,我將用一個最簡單的例子說明異步非阻塞Socket的基本原理和工作機制。目的是讓初學者不僅對Socket異步非阻塞的概念有個非常透徹的理解,而且也給他們提供一個用Socket開發網絡通信應用程序的快速入門方法。操作系統是Windows 98(或NT4.0),開發工具是Visual C++6.0。
MFC提供了一個異步類CAsyncSocket,它封裝了異步、非阻塞Socket的基本功能,用它做常用的網絡通信軟件很方便。但它屏蔽了Socket的異步、非阻塞等概念,開發人員無需了解異步、非阻塞Socket的原理和工作機制。因此,建議初學者學習編網絡通信程序時,暫且不要用MFC提供的類,而先用Winsock2 API,這樣有助于對異步、非阻塞Socket編程機制的理解。
為了簡單起見,服務器端和客戶端的應用程序均是基于MFC的標準對話框,網絡通信部分基于Winsock2 API實現。
先做服務器端應用程序。
用MFC向導做一個基于對話框的應用程序SocketSever,注意第三步中不要選上Windwos Sockets選項。在做好工程后,創建一個SeverSock,將它設置為異步非阻塞模式,并為它注冊各種網絡異步事件,然后與自定義的網絡異步事件聯系上,最后還要將它設置為監聽模式。在自定義的網絡異步事件的回調函數中,你可以得到各種網絡異步事件,根據它們的類型,做不同的處理。下面將詳細介紹如何編寫相關代碼。
在SocketSeverDlg.h文件的類定義之前增加如下定義:
#define NETWORK_EVENT WM_USER+166 file://定義網絡事件
SOCKET ServerSock; file://服務器端Socket
在類定義中增加如下定義:
class CSocketSeverDlg : CDialog
{
public:
SOCKET ClientSock[CLNT_MAX_NUM]; file://存儲與客戶端通信的Socket的數組
/*各種網絡異步事件的處理函數*/
void OnClose(SOCKET CurSock); file://對端Socket斷開
void OnSend(SOCKET CurSock); file://發送網絡數據包
void OnReceive(SOCKET CurSock); file://網絡數據包到達
void OnAccept(SOCKET CurSock); file://客戶端連接請求
BOOL InitNetwork(); file://初始化網絡函數
void OnNetEvent(WPARAM wParam, LPARAM lParam); file://異步事件回調函數
…
};
在SocketSeverDlg.cpp文件中增加消息映射,其中OnNetEvent是異步事件回調函數名:
ON_MESSAGE(NETWORK_EVENT,OnNetEvent)
定義初始化網絡函數,在SocketSeverDlg.cpp文件的OnInitDialog()中調此函數即可。
BOOL CSocketSeverDlg::InitNetwork()
{
WSADATA wsaData;
//初始化TCP協議
BOOL ret = WSAStartup(MAKEWORD(2,2), &wsaData);
if(ret != 0)
{
MessageBox('初始化網絡協議失敗!');
return FALSE;
}
//創建服務器端套接字
ServerSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(ServerSock == INVALID_SOCKET)
{
MessageBox('創建套接字失敗!');
closesocket(ServerSock);
WSACleanup();
return FALSE;
}
//綁定到本地一個端口上
sockaddr_in localaddr;
localaddr.sin_family = AF_INET;
localaddr.sin_port = htons(8888); //端口號不要與其他應用程序沖突
localaddr.sin_addr.s_addr = 0;
if(bind(ServerSock ,(struct sockaddr*)&localaddr,sizeof(sockaddr))
= = SOCKET_ERROR)
{
MessageBox('綁定地址失敗!');
closesocket(ServerSock);
WSACleanup();
return FALSE;
}
//將SeverSock設置為異步非阻塞模式,并為它注冊各種網絡異步事件,其中m_hWnd
//為應用程序的主對話框或主窗口的句柄
if(WSAAsyncSelect(ServerSock, m_hWnd, NETWORK_EVENT, FD_ACCEPT | FD_CLOSE | FD_READ | FD_WRITE) == SOCKET_ERROR)
{
MessageBox('注冊網絡異步事件失敗!');
WSACleanup();
return FALSE;
}
listen(ServerSock, 5); file://設置偵聽模式
return TRUE;
}
下面定義網絡異步事件的回調函數
void CSocketSeverDlg::OnNetEvent(WPARAM wParam, LPARAM lParam)
{
//調用Winsock API函數,得到網絡事件類型
int iEvent = WSAGETSELECTEVENT(lParam);
//調用Winsock API函數,得到發生此事件的客戶端套接字
SOCKET CurSock= (SOCKET)wParam;
switch(iEvent)
{
case FD_ACCEPT: //客戶端連接請求事件
OnAccept(CurSock);
break;
case FD_CLOSE: //客戶端斷開事件:
OnClose(CurSock);
break;
case FD_READ: //網絡數據包到達事件
OnReceive(CurSock);
break;
case FD_WRITE: //發送網絡數據事件
OnSend(CurSock);
break;
default: break;
}
}
以下是發生在相應Socket上的各種網絡異步事件的處理函數,其中OnAccept傳進來的參數是服務器端創建的套接字,OnClose()、OnReceive()和OnSend()傳進來的參數均是服務器端在接受客戶端連接時新創建的用與此客戶端通信的Socket。
void CSocketSeverDlg::OnAccept(SOCKET CurSock)
{
//接受連接請求,并保存與發起連接請求的客戶端進行通信Socket
//為新的socket注冊異步事件,注意沒有Accept事件
}
void CSocketSeverDlg::OnClose(SOCET CurSock)
{
//結束與相應的客戶端的通信,釋放相應資源
}
void CSocketSeverDlg::OnSend(SOCET CurSock)
{
//在給客戶端發數據時做相關預處理
}
void CSocketSeverDlg::OnReceive(SOCET CurSock)
{
//讀出網絡緩沖區中的數據包
}
用同樣的方法建立一個客戶端應用程序。初始化網絡部分,不需要將套接字設置為監聽模式。注冊異步事件時,沒有FD_ACCEPT,但增加了FD_CONNECT事件,因此沒有OnAccept()函數,但增加了OnConnect()函數。向服務器發出連接請求時,使用connect()函數,連接成功后,會響應到OnConnect()函數中。下面是OnConnect()函數的定義,傳進來的參數是客戶端Socket和服務器端發回來的連接是否成功的標志。
void CSocketClntDlg::OnConnect(SOCKET CurSock, int error)
{
if(0 = = error)
{
if(CurSock = = ClntSock)
MessageBox('連接服務器成功!');
}
}
定義OnReceive()函數,處理網絡數據到達事件;
定義OnSend()函數,處理發送網絡數據事件;
定義OnClose()函數,處理服務器的關閉事件。
以上就是用基于Windows消息機制的異步I/O模型做服務器和客戶端應用程序的基本方法。另外還可以用事件模型、重疊模型或完成端口模型,讀者可以參考有關書籍。
在實現了上面的例子后,你將對Winsock編網絡通信程序的機制有了一定的了解。接下來你可以進行更精彩的編程, 不僅可以在網上傳輸普通數據,而且還以傳輸語音、視頻數據,你還可以自己做一個網絡資源共享的服務器軟件,和你的同學在實驗室的局域網里可以共同分享你的成果。
同步服務器套接字掛起應用程序的執行,直到套接字上接收到連接請求。同步服務器套接字不適用于在操作中大量使用網絡的應用程序,但它們可能適用于簡單的網絡應用程序。使用 Bind 和 Listen 方法設置 Socket 以在終結點上偵聽之后,Socket 就可以隨時使用 Accept 方法接受傳入的連接請求了。應用程序被掛起,直到調用 Accept 方法時接收到連接請求。
接收到連接請求時,Accept 返回一個與連接客戶端關聯的新 Socket 實例。下面的示例讀取客戶端數據,在控制臺上顯示該數據,然后將該數據回顯到客戶端。Socket 不指定任何消息協議,因此字符串“<EOF>”標記消息數據的結尾。它假定一個名為 listener 的 Socket 已初始化,并綁定到一個終結點。
Console.WriteLine("Waiting for a connection...");
Socket handler = listener.Accept();
String data = null;
while (true) {
bytes = new byte[1024];
int bytesRec = handler.Receive(bytes);
data += Encoding.ASCII.GetString(bytes,0,bytesRec);
if (data.IndexOf("<EOF>") > -1) {
break;
}
}
Console.WriteLine( "Text received : {0}", data);
byte[] msg = Encoding.ASCII.GetBytes(data);
handler.Send(msg);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
msdn官方說明:http://msdn2.microsoft.com/zh-cn/library/80z2essb(VS.80).aspx
posted @
2009-07-16 15:56 Sandy 閱讀(2274) |
評論 (0) |
編輯 收藏
WinCE驅動開發問題精華集錦-4
摘自:
http://hi.baidu.com/mcu%5Fspaces/blog/item/528dde03af8c5d733912bb40.html61、WINCE的socket函數好像不支持發送/接收超時? 是的,最早版本的WINCE支持選項SO_RCVTIMEO、SO_SNDTIMEO,后來卻不支持了。
62、WINCE下如何設置窗口最大化和最小化? WINCE的幫助文檔在介紹API ShowWindow函數的參數時指出SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE, SW_SHOWDEFAULT, SW_SHOWMAXIMIZED, SW_SHOWMINIMIZED, SW_SHOWMINNOACTIVE都不被支持,但實際上并不完全是這樣,具體來說:
SW_MAXIMIZE 比原來窗口大,但不是最大化
SW_MINIMIZE 編譯成功,但是不起作用
SW_SHOWMAXIMIZED 最大化
SW_SHOWMINIMIZED 編譯出錯
SW_RESTORE 能恢復
SW_SHOWDEFAULT 編譯出錯
SW_SHOWMINNOACTIVE 編譯出錯
SW_HIDE 能夠隱藏
63、如何用程序調用控制面板的觸摸屏校對程序? 兩種辦法:
1)、調用API TouchCalibrate函數
2)、調用CreateProcess,參數1為L"\\windows\\ctlpnl.exe",參數2為L"cplmain.cpl,9"。
64、 如何獲得U盤或者其它類型的存儲器總容量和剩余可用容量? 調 用API GetStoreInfo得到扇區數、每扇區字節數,相乘即是總容量。調用API GetDiskFreeSpaceEx得到剩余可用容量。
65、三星2440頭文件定義#define IIC_BASE 0xB1400000 // 54000000,datasheet是54000000,那么怎么轉成0xB1400000?
物理地址映射方法分為兩種,一種靜態映射另一種為動態映射。在OEMAddressTable中定義了物理地址與虛擬地址的映射關系屬于靜態映射,用VirtualCopy映射屬于動態映射,采用哪種辦法都可以。問題中提到的屬于靜態映射,2440的BSP在map.a文件中定義了IIC控制寄存器的物理起始地址和對應的虛擬地址如下:
DCD 0x91400000, 0x54000000, 1 ;
在OEMAddressTable中定義的虛擬地址范圍在0x8000 0000—0x9FFF FFFF,這部分可緩存,適合內核程序和應用程序使用,同時WINCE內核在0xA000 0000—0xBFFF FFFF中映射了另一份,指向了同樣的物理地址,這部分不可緩存,適合驅動程序使用。三星ARM處理器帶有L1級高速緩存,可緩存會提高執行效率。對于特殊的設備寄存器適合映射到不可緩存的虛擬地址。
當驅動程序調用VirtualCopy對0xB1400000地址讀寫時,WINCE自動將這個地址減去0x2000 0000,也就是0x91400000,對應的物理地址就是0x54000000,也就是IIC控制寄存器的物理起始地址。
66、基于RAM的注冊表如何保存數據?
調用API RegCopyFile備份注冊表。調用API RegRestoreFile恢復注冊表,然后調用KernelIoControl熱啟動使恢復生效。
67、如何隱藏和顯示winCE下標準外殼的任務欄? HANDLE hTaskBar = FindWindow(L"HHTaskBar", NULL);
ShowWindow(hTaskBar, SW_HIDE);
ShowWindow(hTaskBar, SW_SHOWNORMAL);
68、如果能讓WINCE的IE瀏覽器播放flash動畫? 播放flash需要Macromedia Flash Player SDK,參見http://www.adobe.com/products/flashplayer_sdk/。這和real player相似,都需要WINCE平臺的SDK,都需要申請。
69、WINCE下內核模式和用戶模式有什么區別? 為了使讀者能夠詳細了解WINCE的地址映射原理還有兩種模式,在這里我分幾個部分說明:
1)、WINCE內核nk.exe的任務是管理操作系統核心功能。按照OEMAddressTable的映射要求,所有物理地址都映射到0x80000000以上,所以對于內核程序nk.exe和內核模式下的線程來說,只要訪問0x80000000以上的有效虛擬地址經MMU就能夠訪問物理地址,無需再映射是內核模式的一個特點。內核模式的第二個特點是沒有地址訪問限制,內核模式線程可以訪問任何有效虛擬地址,所謂有效虛擬地址是指有實際事物對應。
2)、用戶模式線程只能訪問0x80000000以下的虛擬地址空間,WINCE6.0之前版本的內核為每個進程劃分32MB的地址空間,在不調用特殊函數的情況下不能相互訪問,這樣的設計使得WINCE系統更安全、更穩定,限制訪問地址是用戶模式的第一個特點。第二個特點就是需要多一層映射,如果線程要訪問物理內存的話需要先映射到0x80000000以上,再經MMU訪問物理內存地址。
WINCE的線程具有轉移性(參考API GetCallerProcess的說明,有一個很好的例子),當應用程序的線程調用API或者調用驅動程序接口函數時,該線程會轉移到gwes.exe、device.exe、filesys.exe等進程中執行,轉移是由WINCE內核操作的,它會修改線程的上下文,記錄線程的當前進程、調用者進程、擁有者進程三個值。
3)、如果在定制內核的時候選擇了“Full Kernel Mode”,那么在這個內核上運行的所有線程都處于內核模式,即使調用SetKMode(FALSE)后線程仍然具有內核模式的特點,能夠訪問任何有效的虛擬地址。假設現有一個64MB RAM的WINCE產品,RAM映射從0x80000000到0x84000000,如果線程處于內核模式,它就直接可以訪問這個范圍的虛擬地址:
在OnButton1()中編寫
DWORD oldMode = SetKMode(FALSE);
volatile int *piTemp = (volatile int*)(0x20000000+0x84000000-0x00019000); ///或者(0x84000000-0x00019000)
*piTemp = 12345;
在OnButton2()中編寫
DWORD oldMode = SetKMode(FALSE);
volatile int *piTemp = (volatile int*)(0x20000000+0x84000000-0x00019000); ///或者(0x84000000-0x00019000)
int iTemp = *piTemp;
先只執行OnButton1()然后關閉程序,再重啟程序然后執行OnButton2(),iTemp仍然等于12345。結果說明了兩點:內核模式線程可以直接訪問0x80000000以上的有效虛擬地址;我們寫到RAM中的數據沒有丟失,說明虛擬地址有效。
如果在定制內核的時候沒有選擇“Full Kernel Mode”,那么在這個內核上運行的所有線程都處于用戶模式??梢哉{用SetKMode(TRUE)使調用線程暫時處于內核模式,還是原來的假設環境,我再舉個例子:
在OnButton1()中編寫
DWORD oldMode = SetKMode(TRUE);
volatile int *piTemp = (volatile int*)(0x20000000+0x84000000-0x00019000); ///或者(0x84000000-0x00019000)
*piTemp = 12345;
在用戶模式下,如果不調用SetKMode(TRUE),那么執行*piTemp = 12345一定會彈出對話框,提示地址訪問非法,如果調用SetKMode(TRUE)就不會提示地址訪問非法,而且在OnButton2()中仍然能得到12345這個值。
通過這兩個例子我相信讀者能夠完全了解兩種模式的區別了。
4)、WINCE提供了兩個函數SetKMode和SetProcPermissions,其中SetKMode能夠把調用線程切換到內核模式,還可以切換回用戶模式。SetProcPermissions + GetCurrentPermissions添加當前進程訪問權限給調用線程,SetProcPermissions (0xFFFFFFFF)能讓調用線程訪問所有進程空間,但是調用線程仍然處于用戶模式。SetKMode和SetProcPermissions函數使得用戶模式的特點不那么明晰。
如上所說一個應用程序的線程可能轉移到其它兩個進程地址空間中讀寫數據,而每一個線程在被創建的時候只有訪問創建它的進程地址空間的權限,所以驅動程序開發者必須在驅動程序讀寫數據前調用SetKMode或者SetProcPermissions增加調用此函數的線程訪問其它進程空間的權限。如果一個應用程序的線程只轉移到一個進程地址空間,一般為設備管理器進程device.exe,這種情況下不必增加線程訪問其它進程空間的權限,但如果驅動程序本身創建了一個線程,那還是要調用SetKMode或者SetProcPermissions增加新的線程訪問其它進程的權限的,因為驅動程序創建線程時,當前進程為設備管理器,所以新線程只具有訪問設備管理器進程空間的權限,而不具備訪問應用程序進程空間的權限。
5)、可能一個編寫過簡單的流驅動的初學者會很疑惑,因為開發一個簡單的流驅動程序根本不需要調用這些函數,也沒有調用過MapPtrToProcess,那是因為如果標準流驅動接口函數的參數為指針(ReadFile、WriteFile、DeviceIoControl參數都有指針),WINCE內核會自動映射指針包含的地址,但僅此而已,其余任何情況都要求開發者自行處理,比如流接口函數的參數是一個指向結構體的指針PA,而結構體中包括指針PB,PB指針就必須在流接口函數中映射,映射后才能訪問,否則就會造成地址訪問非法。所以結構體中每個指針都要映射。
為了讓讀者能了解其中的原因,我舉個例子:
假設設備管理器被加載到Slot4,應用程序A被加載到Slot 8,A只有一個主線程T,T開始執行,按照WINCE的規定,正獲得CPU的進程必須映射到Slot0,那么在執行代碼的時候A的所有虛擬地址都被減去一個偏移值,也就是8×0x02000000,A調用DeviceIoControl,傳遞一個指向一個結構體的指針B,而這個結構體中包含一個指針C,指針C包含的地址假設為0x00030000,當執行DeviceIoControl時WINCE把設備管理器的進程地址空間映射到Slot0,因為放在注冊表[HKLM\Drivers\BuiltIn]下的驅動程序是由設備管理器加載的,自然驅動程序的代碼段被加載到設備管理器進程空間,但是線程仍然是T,此時T的當前所在進程為設備管理器(CurrentProcess),A變成了T的調用者進程(CallerProcess),T自動具有了訪問調用者進程空間的權限。這時訪問Slot0中的虛擬地址其實質就是訪問設備管理器的進程地址空間,要把地址加上一個偏移值,也就是4×0x02000000,所以DeviceIoControl訪問指針C包含的地址時本應該加上8×0x02000000,卻加上4×0x02000000,結果地址并不是設備管理器的合法區域,系統就會提示地址訪問非法。而如果做了一個映射,指針C包含的地址就會被加一個正確的偏移值,使地址處于A的地址空間Slot 8中,T此時具有訪問A進程空間的權限,訪問到正確的虛擬地址當然會得到正確的數據了。
70、 為什么WINCE目錄下的例子用build+sysgen能夠編譯成EXE文件,而我添加的例子就不能編譯呢? 如果這個例子是一個應用程序,那么肯定包括代碼文件(.h .c .cpp)和資源文件(.rc和其它資源文件),build工具根據source文件內容把代碼文件編譯成lib文件,資源文件編譯成.res文件,sysgen工具根據makefile文件內容將source文件中列出的需要鏈接的各個庫文件合并成一個EXE文件。所以說關鍵在于makefile文件,WINCE目錄下凡是能夠用build+sysgen編譯的都在makefile中有如何鏈接的設置,而我們添加的例子當然沒有在makefile中找到如何鏈接的設置,nmake工具就會提示不知道如何創建。
71、pcienum.exe干什么用的? 如果你要開發某一個PCI設備的驅動程序,首先要知道這個PCI設備的信息(如VendorID、DeviceID、BaseClass、SubClass)和PCI總線的信息。運行這個pcienum.exe就能得到相關信息。pcienum.exe提供了源碼,位置\Public\Common\Oak\Drivers\Ceddk\Test\Pcienum。
72、wince下如何讓操作系統進入待機模式?又如何把它激活? 通過注冊表就可以設置,前提是你的驅動和硬件都支持。注冊表項參見標題為“GWES Suspend Time-outs”的幫助文檔。
[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Power]
"BattPowerOff"=dword:300
"ExtPowerOff"=dword:0
"WakeupPowerOff"=dword:60
"ScreenPowerOff"=dword:0
73、 現有一個GPRS模塊,如何通過GPRS連接到Internet? 1)、先在內核中加入WAN下面的幾個組件,如RAS/PPP、TAPI。WINCE采用unimodem驅動,所以不必擔心沒有Modem驅動的支持。
2)、WINCE啟動后新建一個撥號連接,比如名稱叫“gprs1”,輸入用戶名、密碼、電話號碼。電話號碼不同,所采用的模式不一樣,例如“*99#”是GPRS模式,“17201”是普通的數據模式,速度差很多,價錢也差很多。
3)、開始連接,連接過程會在對話框中顯示,直到顯示“連接成功”。
4)、打開瀏覽器或者自己開發的通訊軟件測試網絡連接情況。
5)、關閉連接。
6)、保存[HKEY_CURRENT_USER\Comm\RasBook\gprs1]下的所有數據,添加到project.reg中,重新編譯后內核中就有了一個撥號連接“gprs1”。
7)、調用RAS函數可以修改撥號連接“gprs1”的參數,如用戶名、密碼、電話號碼,但是不能修改硬件設置,如波特率、串口、數據位、停止位等。RAS函數還能夠撥號、掛斷。為了修改波特率可以多保存幾個撥號連接,也可以直接調用TAPI開發撥號軟件,另外WINCE自帶的撥號連接是有源碼的,位置在\PUBLIC\COMMON\OAK\DRIVERS\NETSAMP\CONNMC。
74、采用基于HIVE的注冊表如何刪除用戶保存在注冊表中的數據,恢復到出廠時的注冊表? 用戶修改的數據保存在user.hv文件中,直接刪除一定失敗,所以不能通過刪除文件實現恢復出廠設置。微軟考慮到了這個問題,在WINCE啟動過程中filesys.exe加載注冊表時會調用OEMIoControl函數并傳遞一個IOCTL,這個IOCTL在pkfuncs.h中定義如下:
#define IOCTL_HAL_GET_HIVE_CLEAN_FLAG CTL_CODE(FILE_DEVICE_HAL, 49, METHOD_BUFFERED, FILE_ANY_ACCESS)
f ilesys.exe會分別傳遞參數HIVECLEANFLAG_SYSTEM和HIVECLEANFLAG_USERS,如果返回值為TRUE那么filesys.exe清除原來的注冊表文件,如果返回值為FALSE那么filesys.exe保留原來的注冊表文件。默認WINCE并沒有實現這個IOCTL,所以OEM要刪除注冊表文件就必須先編寫這個IOCTL代碼。代碼的例子可參考標題為“IOCTL_HAL_GET_HIVE_CLEAN_FLAG”的幫助文檔。另外必須在ioctl.h和ioctl.c兩個文件中編寫該代碼。在ioctl.c文件中找到const OAL_IOCTL_HANDLER g_oalIoCtlTable[],添加IOCTL和對應的處理函數。要進一步了解這個全局數組,參見標題為“IOCTL Library”的幫助文檔。
75、如何在不刪除必要組件的前提下減小內核文件長度?
要減小內核文件長度首先要在使用PB的定制內核向導中選擇自定義,也就是說對于每個組件都由自己來選擇,而不是選擇PB的標準配置。但減小內核文件長度最有效最直接的辦法是縮小字體,尤其對于東亞字體,采用字體壓縮技術并且選擇合理的字庫文件將明顯縮小文件長度。
1)、在定制內核時選擇AGFA AC3 Font Compression組件。SYSGEN變量為SYSGEN_AGFA_FONT。
2)、參考標題為“East Asian Font Versions”的幫助文檔,從中選擇你需要的字庫文件加到內核中,從文檔可以看出加AC3壓縮比不加壓縮在文件長度方面差距很大。
76、如何得到WAV文件播放的總時間? 1、直接讀取wav文件頭信息,從文件起始地址偏移28個字節長度為4個字節保存的是每秒鐘播放的字節數,從文件起始地址偏移40個字節長度為4個字節保存的是聲音數據的總的字節數,相除就是播放時間。
2、調用IGraphBuilder::RenderFile打開一個wav文件,然后通過IGraphBuilder得到IMediaSeeking指針,再調用IMediaSeeking::GetDuration得到總的時間(結果要除以10000000),IMediaSeeking::GetCurrentPosition得到當前播放時間。
77、如何在Dialog-Based程序中加入menubar?
先調用CommandBar_Create再調用CommandBar_InsertMenubar。
78、請問MultiByteToWideChar與_T、L、TEXT的區別? MultiByteToWideChar函數轉換的對象可以是常量也可以是變量。其它只能轉換常量。_T和TEXT會根據當前系統是否定義_UNICODE宏來決定是否轉換,而L就是轉換成寬字符,當然也包括其他類型常量的轉換。
79、 在用USB線纜通過ActiveSync同步有效的情況下,如何插上USB線纜后WINCE自動與PC同步? 1)、新建一個撥號連接,假設名稱為“usb1”,選擇連接類型為“直接連接”,并在連接設備里選擇通過USB線纜連接。
2)、將注冊表[HKEY_CURRENT_USER\Comm\RasBook\usb1]下的數據添加到project.reg或者platform.reg中。
3)、在[HKEY_CURRENT_USER\ControlPanel\Comm]下添加如下:
"AutoCnct"=dword:1 ///直接連接
"Cnct"="usb1" ///連接名稱
4)、重新編譯內核。為了節省編譯時間也可以在內核工程下搜索*.reg文件,將2、3步驟中的注冊表數據添加其中,然后直接make image。
80、如何通過進程句柄來獲得該進程的主窗口句柄?
好像沒有API能夠通過進程句柄直接獲得主窗口的句柄,因為并非每個應用程序都帶UI。但是可以反過來,先枚舉當前系統所有主窗口,然后根據每個窗口的句柄調用GetWindowThreadProcessId函數得到進程的ID,再調用OpenProcess得到進程句柄,與現有的進程句柄比較。
81、我做的顯示驅動DLL已經編譯成功了,但是在加載顯示驅動的過程中彈出話框,提示如下: unhandled exception in gwes.exe (0xc0000005 access violation)
提示的錯誤——地址訪問非法,表明你的驅動程序代碼并沒有在讀寫數據前添加SetKMode(TRUE)或者SetProcPermissions(0xFFFFFFFF)函數讓線程能夠訪問任何進程的地址空間。你可以調用 IsBadReadPtr和IsBadWritePtr函數檢測地址是否能夠合法訪問。編寫和gwes有關的驅動程序應該首先調用SetKMode(TRUE)或者SetProcPermissions(0xFFFFFFFF)函數,這是一個好習慣。
82、請問在嵌入式系統中如何設置GPRS拔號用的APN? 對一個撥號連接比如“我的連接”單擊鼠標右鍵,在彈出的菜單中選擇“屬性”,然后單擊“配置”—“撥號選項”,在“附加設置”中添加AT命令如“+cgdc,"ip","cmnet"”。“cmnet”位置即為APN。
83、WINCE的IP Phone功能如何? WINCE的voip需要c-s-c結構,既需要服務器的中轉,而skype采用第三代p2p技術就不需要中轉,但是在gprs下也做不到語音流暢。skype有pocket pc版本,但是無線方面需要wlan或者cdma。
84、 三星ARM平臺如何定義自己的中斷ID? 以S3C2410為例,在oalintr.h文件中定義中斷ID,也稱SYSINTR,例如 #define SYSINTR_MYINT (SYSINTR_FIRMWARE+20),最大值不能超過SYSINTR_FIRMWARE+23。然后在armint.c文件中找到OEMInterruptHandler函數,用if (IntPendVal == INTSRC_XXX) 判斷當前發生的中斷源號,然后返回SYSINTR_MYINT。內核分別調用OEMInterruptDisable(禁止當前中斷)、OEMInterruptDone(中斷處理結束)、OEMInterruptEnable(當前中斷有效)三個函數,參數都為中斷ID,在這三個函數中用 case SYSINTR_MYINT判斷當前要處理的中斷。
85、如何開發軟件從PC端復制文件到基于WINCE的設備? 調用RAPI(Remote Application Programming Interface)函數,此函數集由桌面計算機調用,由基于WINCE的設備執行。一旦連接上就可以在桌面計算機端調用RAPI。通過注冊表還可以限制RAPI能夠訪問目錄的范圍。具體參考RAPI和RDP(遠程桌面協議)。
86、請問如何對NandFlash分區、格式化?
你看看WINCE420\PUBLIC\COMMON\OAK\DRIVERS\ETHDBG\BOOTPART\bootpart.cpp,在Eboot中先要調用BP_LowLevelFormat(
DWORD dwStartBlock, DWORD dwNumBlocks, DWORD dwFlags)再flash的一個區域建立空的MBR,然后連續兩次調用BP_OpenPartition(DWORD dwStartSector, DWORD dwNumSectors, DWORD dwPartType, BOOL fActive, DWORD dwCreationFlags)函數來建立BINFS和FAT分區。建好后,將nk.bin燒入binfs分區中。
87、要做個彈出對話框具有 always on top 屬性,如何實現? 調用SetWindowPos(.. , HWND_TOPMOST, ...., SWP_NOACTIVATE)。
88、s3c2410+WINCE下網絡PING一會就斷,如何解決?
原因在于中斷處理程序把已經產生的中斷標志清除掉了,這樣就丟失一次中斷。因為原驅動里配置中斷為上升沿觸發,一次中斷丟失就導致不會再產生中斷信號跳變,因為只有在中斷服務中讀取了cs8900的 Interrupt status queue寄存器后,才會產生下一次中斷!解決辦法:
1)、在cfw.c文件中全局定義BOOL Inited = FALSE
2)、修改OEMInterruptEnable()中case SYSINTR_ETHER: 下面的語句為:
if(Inited == FALSE)
{
s2410IOP->rEINTPEND = 0x200;
s2410INT->rSRCPND = BIT_EINT8_23;
if (s2410INT->rINTPND & BIT_EINT8_23)
s2410INT->rINTPND = BIT_EINT8_23;
Inited = TRUE;
}
s2410IOP->rEINTMASK &= ~0x200;
s2410INT->rINTMSK &= ~BIT_EINT8_23;
break;
89、已經搜索到文件,如何用CListBox以圖標形式顯示出來?
CListCtrl ListCtrl;
CImageList ImageList;
ImageList.Create(IDB_BITMAP, 48, 2, RGB(0,0,0));
ListCtrl.SetImageList(&ImageList, LVSIL_NORMAL);
ListCtrl.InsertItem(iListIndex, strItem, 1);
90、如何改變控制面板中電源屬性對話框的尺寸? 1)、需要修改對話框的尺寸是因為對話框是以資源方式加載的,不會根據當前系統顯示分辨率而自我調節尺寸。
2)、安裝WINCE后有一些組件(feature)的資源文件*.res就已經有了,如果你不改變,那么build內核的時候PB只是把這些.res復制到工程目錄下,然后與*.obj合并成EXE、DLL、CPL。所以修改了.rc文件里面的對話框尺寸后要重新編譯.rc文件為.res文件,然后再覆蓋原來WINCE自帶的.res文件。
3)、改變對話框尺寸有兩種辦法:一種方法是更改系統字體字號,系統字體的字號變化會影響對話框的尺寸,但是缺點是所有系統字體有關的UI都會改變。另一種是在.rc文件中調整對話框尺寸,然后編譯成.res文件,再將.res復制到對應的語言目錄里,比如目錄名為0804(中文),再執行Rebuild命令重新編譯內核,或者執行sysgen+build。在研究中我發現.res文件雖然能夠直接用EVC打開、修改、保存,但是和其它Obj鏈接成EXE、DLL、CPL后并不能運行,所以還是建議讀者用CE自帶的rc工具編譯最好。讀者可在PB的命令行中鍵入“rc /?”了解rc.exe工具的用途和參數。
91、使用EVC build之后連接模擬器的時候,提示download file等了一會又出現download failed?
一般這樣的問題從下面幾個步驟解決:
1)、如果之前能啟動模擬器而現在不能,那么先clean然后重啟計算機再build。
2)、如果開發的主機為WINXP+SP2,可能存在與EVC模擬器不兼容的情況,檢查C:\boot.ini,將/noexecute=optin改為/execute=optin。
3)、檢查你的模擬器是否能運行,假設你正用的SDK名稱為MYSDK,單擊菜單tools—configure platform manager,選擇MYSDK—MYSDK emulator,再單擊properties—test,看看模擬器是否能夠啟動,如果能啟動那問題就不大。
4)、單擊菜單build—update remote output files,看看模擬器是否能夠啟動。
5)、如果上述辦法均不行,關閉EVC然后重新建立一個新的工程,編譯,看看模擬器是否能夠啟動,如果能啟動說明原來工程出了問題,最好恢復原工程的備份。
92、如何設置能夠自動撥號、禁止自動撥號? 在[HKEY_LOCAL_MACHINE\Comm\Autodial]下是自動撥號的注冊表設置。
Enabled=DWORD:1 ///是否能夠自動撥號
FailRetryWaitMS=DWORD ///如果失敗再次撥號的等待時間
RasEntryName1= REG_SZ ///自動撥號采用的撥號連接名稱
更多細節請參考標題為“Auto Dial Registry Settings”的幫助文檔。
posted @
2009-07-16 15:52 Sandy 閱讀(730) |
評論 (0) |
編輯 收藏
WinCE驅動開發問題精華集錦-3
摘自:
http://hi.baidu.com/mcu%5Fspaces/blog/item/4f3139df56ec7e1763279847.html41、WinCE下如何讀寫幾百兆的大文件呢? 使用內存映射文件嗎? 一般嵌入式設備配備128MB物理內存就算頂級的了,所以要讀寫幾百MB的文件用內存映射文件技術是最好的選擇了。映射文件之后讀數據是非常容易的,要注意的是寫數據,內存映射方面的API沒有提供改變文件長度的功能,所以要在關閉映射文件對象后用文件API改變文件長度。
42、請問如何改系統調度的默認時間片值? 更改schedule.c文件中的dwDefaultThreadQuantum 變量,然后重新編譯該文件并SYSGEN。調用API CeGetThreadQuantum就知道更改是否生效。
43、如何讓系統加載自己寫的驅動程序? 兩種辦法:
1)、在[HKEY_LOCAL_MACHINE\Drivers\BuiltIn]下添加注冊鍵。
2)、在應用程序中調用ActivateDeviceEx。
44、在一些文件中用分號來表示注釋,例如下面的內容
; @CESYSGEN IF SERVERS_MODULES_HTTPD
; @CESYSGEN ENDIF
在“CESYSGEN...”前加了“@”,有沒有什么特別的含義? 在WINCE的一些文件中,用“;”作為注釋并在注釋文字中用@CESYSGEN作為標記,后面接條件語句。Cefilter.exe工具負責按照條件來篩選文件內容,所以不要輕易地刪除包含@CESYSGEN的注釋語句。
45、通過串口建立ActiveSync聯接,串口線用三線的可以嗎? 不可以,因為用串口同步時要用到其余口的狀態。
46、 WINCE是否支持MAPI? 不支持。
47、如何旋轉屏幕顯示的內容? 例子代碼如下(前提是顯示驅動程序支持旋轉):
DEVMODE devmode = {0};
devmode.dmSize = sizeof(DEVMODE);
devmode.dmDisplayOrientation = DMDO_90; ///垂直模式
devmode.dmFields = DM_DISPLAYORIENTATION;
ChangeDisplaySettingsEx(NULL, &devmode, NULL, 0, NULL); ///改變顯示的設置
CRect rcWorkArea(0, 0, 320, 240); ///整個屏幕尺寸
///設置客戶區大小并廣播消息,這樣所有軟件也就隨之更改顯示
SystemParametersInfo(SPI_SETWORKAREA, 0, (void*)&rcWorkArea, SPIF_SENDCHANGE);
48、請問如何修改字形緩存的容量? [HKEY_LOCAL_MACHINE\System\GDI\GLYPHCACHE]
"limit"=dword:0400
49、 如何得到從WINCE啟動開始到現在的時間? 調用API GetTickCount,得到的值為32位整數,單位為毫秒。
50、如何調用WINCE的軟鍵盤? 調用API SipShowIM(SIPF_ON),前提是內核加入了軟鍵盤組件。
51、 基于HIVE的注冊表,如何在系統關閉前保存注冊表的數據到文件system.hv? 調用API RegFlushKey函數。
52、使用VirtualAlloc和VirtualCopy的時候需要注意哪些事項? 1)、VirtualAlloc的作用是申請虛擬地址空間,這肯定不是最終的目的,最終目的可能是申請物理內存、映射寄存器、提交文件等。沒有一個目的會在意虛擬地址空間的位置,所以盡量傳遞參數1為0,也就是讓WINCE自動分配虛擬地址空間。VirtualAlloc分配地址空間實際上是以64KB為單位,所以要指定申請的虛擬空間的首地址的話,參數1應該為64KB的整數倍,申請的長度也應該為64KB的整數倍,即使你不需要那么大。
2)、VirtualCopy的主要作用是映射物理地址空間,如果參數2為物理地址,那么最后一個參數要添加PAGE_PHYSICAL,參數2必須是256的整數倍。如果參數2為虛擬地址(0x80000000以上),那么最后一個參數就不要添加PAGE_PHYSICAL,WINCE內核會根據這個虛擬地址找到對應的物理地址。
53、驅動程序和應用程序之間傳遞數據時何時調用MapPtrToProcess?
因為設備管理器負責加載驅動程序DLL,這意味著當應用程序調用驅動程序接口函數的時候,WINCE內核會將調用驅動程序接口函數的線程轉移到設備管理器的進程空間然后執行具體的驅動程序代碼,應用程序和設備管理器處于兩個進程空間,這就造成設備管理器無法訪問應用程序傳遞的指針(虛擬地址),所以當我們在應用程序中傳遞指針給流驅動程序接口函數時,WINCE內核從中作了一個地址映射,例如ReadFile、WriteFile、DeviceIoControl函數的參數凡是指針都經過了映射才傳遞給驅動程序,所以很多驅動程序開發者并不了解其中的奧秘就可以編程了。但是如果參數是一個指向一個結構體的指針,而結構體里包括一個或多個指針,那么WINCE內核并不負責映射,所以就需要開發者在驅動程序接口函數中調用API函數MapPtrToProcess來映射地址。例如:pPointer_retval = MapPtrToProcess(pPointer, GetCallerProcess());
55、 如何判斷可插拔的設備是否存在? 1)、通過查找注冊表的值。凡是由API ActivateDeviceEx加載的驅動程序都在[HKEY_LOCAL_MACHINE\Drivers\Active]鍵下有注冊鍵,通過查找“name”或者其它鍵值就能夠找到。設備管理器就調用這個API。如果是PCI設備,在注冊表[HLM\Drivers\BuiltIn\PCI\Instance]下查找關鍵字,例如[HLM\Drivers\BuiltIn\PCI\Instance\WaveDev1],說明音頻驅動已經加載。
2)、調用驅動程序接口函數,根據返回值或者執行結果來判斷。
56、如何做到通過串口過來的一個信號啟動自己開發的應用程序? 創建一個線程負責等待串口過來的信號,調用API SetCommMask設置要等待的信號種類,具體可以等待的信號種類參見參數2的說明。然后再調用API WaitCommEvent函數等待這個信號,接收之后再調用API CreateProcess啟動應用程序。
57、在WINCE中如何只能啟動應用程序的一個實例? 常用的兩種辦法:
1)、如果應用程序實例創建了窗口,可通過API FindWindow函數通過窗口類名和窗口標題名稱來查找,前提是系統內不會出現窗口名稱重復的情況。
2)、應用程序初始化的時候創建一個事件或互斥等內核對象,因為內核對象是由內核創建,名稱在系統內唯一。
58、能不能自己編輯一個數字簽名文件導入到手機上,這樣就可以用這個簽名簽自己的程序了? WINCE的內核簽名機制的用途是限制非法的可執行模塊EXE、DLL等在設備上運行。要求內核的加載模塊用公鑰驗證請求加載的EXE、DLL的簽名是否合法,而這個公鑰是在定制內核的時候加進去的,所以除內核的定制者以外的人無法修改這個驗證機制。
59、 在WINCE下是否能夠得到某一進程使用的物理內存總量? 目前沒發現有這樣一個API能夠得到指定進程使用的物理內存總量。只有GlobalMemoryStatus能夠得到整個系統使用的物理內存總量。
60、 應用程序如何控制lcd的亮度?如何獲得電池的電量? 從常見的平臺如Geode、三星ARM系列來看,的確在驅動方面沒有統一的控制LCD或者其它種類屏幕亮度的接口函數,所以只能根據具體平臺提供的接口來做。從幫助文檔來看微軟的帶有DirectDraw功能的顯示驅動程序的確有標準的增加亮度的接口函數,關于背景光參見標題為“Enabling a Backlight”的幫助文檔。
獲得電池電量有標準的接口函數GetSystemPowerStatusEx,前提是驅動程序和硬件都要支持。
posted @
2009-07-16 15:45 Sandy 閱讀(439) |
評論 (0) |
編輯 收藏