1、Windows CE 应用 程序 开发 亿 道 电子 微 软TTT 认证培 训 讲师: 刘 立 明 emdoor内 容 SDK 的介绍 本地应 用程序 与可托 管的应 用程序 本地应 用程序 开发 emdoorSDK emdoor开发SDK 的 步骤 定制好一个wince 的 工程映象. 利用SDK 的向导配置SDK. 编译SDK. 把SDK 安装到Visual Studio 2005 emdoorSDK 常规 选项 的配 置 emdoorSDK 可支 持库 的配 置 emdoorSDK 支持CPU 的 配置 emdoorSDK 中传 输通 道的 配置 emdoor编译SDK emdoor安装
2、SDK emdoor内 容 SDK 的介绍 本地应 用程序 与可托 管的应 用程序 本地应 用程序 开发 emdoor本地代码架构 MFC, ATL, WTL, and STL Microsoft Foundation Class Library (MFC) 面向对象编程架构(OOP ) Active Template Library (ATL) 支持COM 和ActiveX 组件 Windows Template Library (WTL) Win32 C+ Standard Template Library (STL) C+ 模板 库 Frameworks ship with VS2005
3、 not CE 6.0 .NET emdoor.NET 托管 程序 支持 Compact Framework 编译成中间件 C# Visual Basic .NET 不能直接访问Win32 API emdoor.NET Compact Framework .NET Compact Framework 目标设备OS 必须支持.NET 包 .NET 执行引擎 编译成中间件 .NET Class Library 窗体类, Data and XML 类和支持GDI support 桌面 .NET Framework 的子集 emdoor本地 应用 程序 对不同的CPU 架构 或平台 , 程序必须 重新
4、编译 开发者自 己管理系 统资源 ; WIN32 程序无需其 他支持文 件 ; 可以访问 所有OS 服务和API 函数 在PC 机 运行时必 须重新编 译 支持COM, ActiveX 编程 emdoor托管 应用 程序 运行在 所有目 标设备 ; 运行引 擎管理 系统资 源 ; 需要.NET CF 文 件支持; 应用程 序只能 访问.NET CF 支 持的服 务 可直接 运行在PC 机上 , 无需重 新编译 emdoor内 容 SDK 的介绍 本地应 用程序 与可托 管的应 用程序 本地应 用程序 开发 emdoor本地 应用 程序 开发 本地应用程序的开发工具: Platform Buil
5、der EVC Visual studio 2005 本地应用程序的开发过程: 从Platform Builder 导出SDK 安装SDK. Win32 编 程. emdoor线 程同 步 在使用线程时 ,会经常遇到两个概念 ,即线程冲突和线程死 锁。 线程冲突 :如果线程A 读写数据G ,线程B 也正在读取数据 G ,那么很 显然,该操作将导致数据冲突,引起数据混乱。 这里需要使用同步技术,以保证线程A 和线程B 依次读写数 据G ,避免 数据冲突。 线程死锁 :例如A 工人为加工III 零 件在等待B 提供的I 零 件, 而B 正好在等待应由A 加工提供的II 零件来装 配I 零件。 由于
6、他 们之间再没有其他的任何人帮助通信或其他通信手段。所以 他们一直在等对方的零件而进入死锁状态。 死锁属于逻辑错 误,无法通过线程同步来解决。 WinCE 实现 线程同步的常用方法 :事件(Event )、互斥 (Mutex ) 、 信号量(Semaphore )、临界区 (CriticalSection )。 emdoor线 程 同步- 利 用事 件 同步(1) “ 事件对象” 是实现线程同步最基本的方法之一,一个事件对象可以处于“ 已标 示” 和“ 未标示两种状态,如果将事件对象设置为“ 已标示” 状态,表示可以执行 同步操作,事件对象处于“ 未标示” 状态,则表示需要等待事件对象变为“
7、已标 示” 状态才可以进行同步操作。下面介绍利用事件同步所需要的API 函数。 (1 )CreateEvent 函数。创建事件对象函数CreateEvent , 其声明如下 : HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes,/CE 不支持,设为 NULL BOOL bManualReset, / 设置是否手动设置事件对象状态 BOOL bInitialState, / 事件对象 初始状态 LPTSTR lpName / 事件对象 名称 ); 参数bManualReset 表示是否手动设置事件对象状态,当其值为TRUE 时
8、,在调用完等待函数(WaitForSingObject ,WaitForMutipleObject )后, 则必须调用ResetEvent 函数,以设置事件对象没有被标示,当其值为FALSE 时,系统调用完等待函数,会自动将事件对象设置为未标示状态。 参数bInitialState 表示事件对象初始状态,当其值为TRUE 是 ,事件对象 初始化状态为已标示,当其值为FALSE 时,事件对象初始状态为未标示。 如果创建事件函数对象CreateEvent 执行成 功 ,将返回事件对象句柄。 若失败,则返回0 ,在不用事件句柄时,需要使用CloseHandle() 将其关 闭 , 以释放资源。 em
9、door线 程 同步- 利 用事 件 同步(2) (2 )SetEvent 函数和ResetEvent 函数。函 数SetEvent () 的功能是将事件对象设置为已标示状态。该函数的声明如 下: BOOL SetEvent (HANDLE hEvent ); 参数hEvent 表示事件对象句柄。 函数ResetEvent 函数功能将事件对象设置成未标示状 态,该函数的声明如下: BOOL ResetEvent (HANDLE hEvent ); (3) 使用事 件同步的一般使用流程 通常情况,在主线程中,用户利用CreateEvent 函数 创建一个事件对象,并且将参数bManualRese
10、t 设为 FALSE , 参数bInitialState 也设为FALSE ,此时事件对象 状态未标示。然后在线程里通过WaitForSingleObject 函 数来等待事件被标示。此时,只要在主线程中调用 SetEvent 函数,将事件对象设置成已标示。那么线程里 的WaitForSingleObject 函数便会 返回,继续执行,同时 将事件对象状态设置成未标示。 emdoor线 程 同步- 利 用互 斥 同步(1) 互斥同步 类似 于事件对 象同 步 。 互斥 同步 也将创建 一个 互斥对象 , 该 互斥对象 也有“ 被线 程拥有” 和“ 不 被线程 拥有” 两种状 态 ; 当互 斥对
11、 象处 于“ 不 被线程 拥有” 状态 , 表示可以 执行 相关操作 ; 当 互斥对象 处于“ 被 线程拥有” 状 态 , 表示 此时 不可以执 行相 关操作 。 通过 等待函数 请求 互斥对象 实现 同步 。 (1 )CreateMutex 函 数 。 通过CreateMutex 函数创 建 互斥对象 , 该 函数 定义如下 : HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, /CE 不支持 BOOL bInitialOwner, / 初 始 化拥有状 态 ,TRUE 表示拥 有 , FALSE 表示未被拥有 LPCT
12、STR lpName / 互斥 名称 ); 如果创建 互斥 函数对象CreateMutex 执行 成功 , 将 返回 互斥对象 句柄 。 若 失败 , 则返回ERROR_INVALID_HANDLE , 在不用互 斥句 柄时 , 需 要使 用CloseHandle() 将其关 闭 , 以释放 资源 。 emdoor线 程 同步- 利 用互 斥 同步(2) (2 )ReleaseMutex 函数。在使用等待函数请求互斥对象 时,如果请求到互斥对象的拥有权,则等待函数将自动设 置互斥对象状态为“ 未被拥有” 。ReleaseMutex 函数负责 释 放某个线程对象互斥对象的拥有权,也就是将互斥对象
13、设 置为“ 未被线程拥有” 状态。ReleaseMutex 函数定义如下: BOOL ReleaseMutex ( HANDLE hHandle );hHandle 表示互斥对象句柄; (3 )利用互斥同步的一般使用流程 利用互斥同步的一般使用流程是:首先利用CreateMutex 函数创建互斥对象,并将CreateMutex 中的参 数 bInitialOwer 设置为FALSE ,使互 斥对象处于“ 未被线程拥 有” 状态 。然后利用WaitForObject 等待互斥 对象,执行相 关操作。处理完成后,利用ReleaseMutex 函 数释放线程 对互斥对象的拥有权。当所有线程执行完毕后
14、,需要使用 CloseHandle() 将其关 闭 。 emdoor线 程 同步- 利 用临 界 区同 步 (1) “ 临界 区” 是进 行线程同 步的 另一种方 法 , 它能够阻 止两 个或多个 不同 的线程在 同一 时间内访 问同 一个代码 区域 。 它通过 调用 EnterCriticalSection 函数来 指出已经 进入 代码的临 界区 , 如果另 一线 程也调用 了EnterCritialSection 函数 , 并 且参数指 向同 一临界区 对象 , 那么另一 线程 将阻塞 , 直到 第一个线 程调 用了LeaveCriticalSection 函 数离开临 界区 为止 。 临
15、界区同 步所 需要的API 函 数 : (1) InitializeCriticalSection 函数 。 如 果要 使用临界 区 , 首先要使 用 InitializeCriticalSection 函数 创建临界 区 , 该函数定 义如 下 : void InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); (2 ) DeleteCriticalSection 函数 , 当结 束使用临 界区 对象时 , 必须 调用DeleteCriticalSection 函数释放 临界 区对象所 占有 的资源 。 该函 数
16、定义如 下 : void DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); ); emdoor线 程 同步- 利 用临 界 区同 步(2) (3 )EnterCriticalSection 函数, 在创建了临界区对象后,需要 调用EnterCriticalSection 函数进入 临界区 ,以保护代码 ,该 函数定义如下 : void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 如果临界区对象已经属于另一个线程,那么此函数将阻塞直 到另一线程离
17、开临界区才返回。 (4 )LeaveCriticalSection 函数。 如果要离开临界区,只需要 调用LeaveCriticalSection 函数即 可 。该函数定义如下 : void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection emdoor线 程 同步- 利 用信 号 量同 步 (1) 信号量是 建立 在互斥基 础上 , 并增加 了资 源计数的 功能 。 它允许 预定 数 目的线程 同时 进入要同 步的 代码 。 通 过设 置信号量 计数 为1 , 只 允许 一 个线程同 时访 问同步代 码 , 而实现线 程同 步
18、 。 信号 量同 步所需要 的API 函数 : (1 ) CreateSemaphore 函数 。 在 使用 信号量实 现同 步时 , 需 要调 用 CreateSemaphore 函 数创 建信号量 对象 。 该函数 定义 如下 : HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, /CE 不支持 LONG lInitialCount, / 信 号 量初始化 计数 值 LONG lMaximumCount, / 信号量计 数最 大值 LPCTSTR lpName / 信号 量对象名 称 ); 如创建信 号
19、量 成功 , 函 数返 回信号量 对象 句柄 , 否 则返 回NULL 值 。 emdoor线 程 同步- 利 用信 号 量同 步(2) (2 )ReleaseSemaphore 函 数 。在使 用等待函 数请求 信号量时 ,等待函 数自动给 信号量计 数减1 , 那么 当计数减 到0 时, 信号量对 象将不能 被请求 。 ReleaseSemaphore 函数负 责给信号 量计数加 值 , 使信号量 可以被请 求 。此函 数定义如 下 : BOOL ReleaseSemaphore( HANDLE hSemaphore, / 信号量句 柄 LONG lReleaseCount, / 信号量计
20、 数增加的 值 LPLONG lpPreviousCount / 输出量, 表示上 一次信 号量计数 ); emdoor动 态链 接库 简介 动态链接库(Dynamic Link Library ,简称DLL )是一些编 译过的可执行的程序模块,可以在应用程序中或其他DLL 中被调用。DLL 的应用 非常广泛 ,可以实现多个应用程序 的代码和资源共享,是WinCE 程序 设计中的一个非常重要 的组成部分。 DLL 设计程 序的优点 : 共享代码、资源和数据。DLL 作为 一种基于Windows 的 程 序模块,不仅可以包含可执行的代码,还可以包括数据和 各种资源等,扩大了库文件的使用范围; 可
21、将系统模块化,方便升级。 隐藏实现的细节。在某些情况下,用户可能想隐藏例程的 细节,可以采用DLL 来 实现 ,DLL 的例程可以被应用程序 访问,而不显示其中代码的细节。 DLL 与语言 无关 emdoorDLL 的 调用 不论使用 何种语言 对编译好 的DLL 进 行调用时 , 基本上都 有两种调 用方式 , 即静态调 用方式和 动 态调用方 式 。 静态调用 方式由编 译系统完 成对DLL 的加载和 应 用程序结 束时DLL 卸载的编 码 (如还 有其它程 序 使用该DLL ,则Windows 对DLL 的应用 记录减1 , 直到所有 相关程序 都结束对 该DLL 的 使用时才 释 放它
22、), 简单实用 , 但不够 灵活 , 只 能满足一 般 要求。 动态调用 方式是由 编程者用API 函数 加载和卸 载 DLL 来 达 到调用DLL 的目的 ,使用上 较复杂 , 但 能更加有 效地使用 内存 ,是 编制大型 应用程序 时 的重要方 式 。 emdoorDLL 的 调 用 DLL 的 静 态 调 用 DLL 的静态 调用由编译系统完成对DLL 的加载 和应用程序 结束时DLL 卸载,在EVC 或VS.net 中静态调 用DLL 非常 简 单,首先将动态链接库的.LIB 文件 加入到应用程序的工程 中,然后在使用DLL 中 的函数文件里引用DLL 的头文件 (.h ) 即可。 当
23、开发人员通过静态方式编译并生成应用程序时,应用程 序中的调用函数与LIB 文件中的导出符号相匹配,这些符 号或标示进入到生成的EXE 文件中 。当应用程序运行过程 中需要加载DLL 文件时 ,操作系统将根据这些信息查寻并 加载DLL , 然后通过符号或标示实现对DLL 函 数的动态链 接。当加载应用程序的EXE 文件时 ,所有被应用程序调用 的DLL 文件 都被加载到内存中 ,这时可执行程序直接通过 函数名调用DLL 的输出 函数 ,其调用方法与调用程序内部 函数相同。 emdoorDLL 的 调 用 DLL 的 动 态 调 用 动态调用 方式是由 编程者用API 函数 加载和卸 载DLL 来
24、达到调 用DLL 的 目的 , 动 态调用是 指在应用 程序 中使用LoadLibrary 函数或MFC 提供的 AfxLoadLibrary 函 数显式调 用自己所 需要的动 态链 接库,动 态链接库 的文件名 就是上面 两个函数 的参 数,然后 在使用GetProAddress() 函数获取所需 要引 入的函数 。完成上 述操作后 ,应用程 序可以调 用引 入的函数 。在应用 程序退出 之前 ,应 该使用 FreeLibrary 函数或MFC 提供 的AfxFreeLibrary 函数 来释放动 态链接库 。 emdoor动 态链 接库 的创 建 新建一个基于Smart Device 的
25、MFC Smart Device DLL emdoor动 态链 接库 的创 建 选择所支 持的平台 emdoor动 态链 接库 的创 建 DLL 的类型 选择 emdoor输出 函数 的实 现方 法 DLL 中导 出函 数的声明 有两 种方式 : 一种 方法在函 数名 称声明中 加上 修饰符 _declspec(dllexport) , 表 示输出 , 此外 , 还有一 种修 饰符 extern “ C” _declspec(dllexport) , 也 表示输出 , 而 且该类DLL 不 仅可以被C+ 调用 , 还可以被C 调 用 。 在C+ 下 定义C 函 数时 , 需要加 上extern
26、 “ C” 关键 字 。 下面 为DLL 测 试程 序的输出 函数 的声明 。 ( 在DllTest.h 文件 中 )。 #ifdef DLLTEST_EXPORTS #define DLLTEST_API _declspec(dllexport) #else #define DLLTEST_API _declspec(dllimport) #endif extern “C“ void DLLTEST_API SetLight(unsigned char data); void DLLTEST_API Ledshift(int shiftdir ,int count); void DLLTES
27、T_API LedControl(DWORD dwCode,int shiftdir=-1); emdoor输出 函数 的实 现方 法 导出函数 另外一 种方式是 采用模 块定义(.def) 文 件 声明 ,.def 文 件为 链接器提 供了有 关被链 接程序的 导出 、 属性及其 他方面 的信息 。 ; DllTest.def : Declares the module parameters for the DLL. LIBRARY “DllTest“ EXPORTS SetLight Ledshift LedControl 采用模块 定义.def 导出函数 声明 , 如果要求 导出函 数能
28、够被C 语 言 掉用 , 必 须在函 数的实现 前加 extern “C“ 进行修饰 。 extern “C“ void SetLight(unsigned char data) if(m_bShiftRunning) m_bStop=TRUE; Sleep(500); *pLightReg=data; emdoor动 态 链 接 库 的 调 用- 静态调用DLL 的步骤 利用VS 生成 一个DLL 调用 测试应用 程序 , 并将上 述编 译好 的.dll 和.lib 文件拷贝到 工程 的目录下 。 并 进行设置 emdoor动 态 链 接 库 的 调 用- 静态调用DLL 的步骤 如果在DL
29、L 的函数的导出采用头文件的实现方法,必须将 DLL 的头文 件DllTest.h 拷贝到在调用DLL 的工 程中 ,并在 实现文件中引用DllTest.h 文件,代码如下: #include “DllTest.h“ 这样就可以使用DLL 的 导出函数 。 如果采用模块(.def ) 导出DLL 中 实现函数 ,则必须在调用 DLL 的实现 文件声明导入函数 ,代码如下: extern “C“ void _declspec(dllimport) SetLight(unsigned char data); extern “C“ void _declspec(dllimport) Ledshift
30、(int shiftdir ,int count); extern “C“ void _declspec(dllimport) LedControl(DWORD dwCode,int shiftdir=-1); emdoor动态调 用DLL 的步骤 动态调用方式是使用LoadLibrary API 函数加载DLL ,然后在使用GetProAddress() 函数获取所需要引入的函数。 具体实现方式: 添加DllTest 动态链接库 工程的实现函数的定义,代码如下: typedef void (*pLedControl) (DWORD,int); typedef void (*pSetLight
31、) (unsigned char); typedef void (*pLedShift) (int,int); 利用LoadLibrary API 函数动态加载动态链接库,代码的黑体部分; BOOL CTestDllDlg:OnInitDialog() CDialog:OnInitDialog(); SetIcon(m_hIcon, TRUE); / Set big icon SetIcon(m_hIcon, FALSE); / Set small icon hModule=LoadLibrary(_T(“DllTest.dll“); if(hModule=NULL) MessageBox(_
32、T(“Load Dll file failed“),_T(“System Information“),MB_OK|MB_ICONERROR); return TRUE; / return TRUE unless you set the focus to a control 利用GetProcAddress API 函数获取需要引用 的函数,代码如下: void CTestDllDlg:OnBnClickedbtnstop() pLedControl LedControl =(pLedControl)GetProcAddress (hModule,_T(“LedControl“); if(LedControl=NULL) MessageBox(_T(“Load LedControl function failed“),_T(“System Information“),MB_OK|MB_ICONERROR); else LedControl(2,-1); emdooremdoor