程序图标的获取
在我们的手机中,不同的程序有不同的图标,怎么抓取这些程序的图标呢?不用急啊,系统给我们提供了一个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 阅读(1396) |
评论 (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 阅读(2751) |
评论 (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 阅读(1152) |
评论 (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://www.cppblog.com/bangle/archive/2008/08/23/59725.html
posted @
2009-07-21 18:11 Sandy 阅读(369) |
评论 (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 阅读(313) |
评论 (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 阅读(589) |
评论 (0) |
编辑 收藏
摘自:
HTTP://WWW.Yonsm.NET/read.php?296近日,由于程序设计需要,我对WincowsCE 的内存布局进行了研究,由于发现国内在这方面的文档资料较少,于是在研究告一段落之际,形成这篇示例文档,以望抛砖引玉,得到别的高手的指正。
一、程序实现的先决条件
由于windows系统的窗体消息总是投递至一个特定进程的指定窗体消息函数中。于是在本地进程(自己的应用程序)中取得属于其它进程的窗体的消息必须实现以下两个部分:
1、将需要挂接窗体的代码放到目标进程的地址空间中去。
2、执行这一段代码,并获得目标进程窗体的消息。
这两步看起来很简单,但在实现过程中就比较困难。由于Windows CE作为嵌入式移动设备操作系统,与windows 98/2000/XP等桌面操作系统在内核的设计理念以及API的支持上有极大的区别。这就直接导致了常规的桌面系统利用全局鼠标钩子注入/远程线程注入等方法在CE中完全得不通。不过可喜的是,微软在开发工具中提供的remotexxx等远程调试程序使我清楚这个目标并不是不可能的任务,微软既然可以做到,那就是说在CE的内部一定有一套完整的跨进程内存访问/代码注入的机制。
二、程序实现的基本原理
经过两天的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为传递到远程目标进程执行函数的其它三个参数。 至于将代码放到目标进程的内存空间,我们可以利用CE设计上的一个特性:
1、为了节约内存使用,CE将所有程序调用的动态链接库(DLL)都映射到同一个内存地址中。
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的文档说明。
本文我们对实现的原理进行了剖析,在下一篇文章中我们将以一个小示例程序演示实现的全过程。 在文章《浅析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 阅读(457) |
评论 (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 阅读(2238) |
评论 (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 阅读(717) |
评论 (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 阅读(425) |
评论 (0) |
编辑 收藏