WinCE 是微软开发的嵌入式实时操作系统,虽然现在已经被更现代的 Windows IoT Core 和 Windows Embedded 8/10 等取代,但在许多工业设备、医疗仪器、车载导航等老旧设备中仍然广泛存在,学习 WinCE C 编程对于维护和开发这些系统仍然具有很高的实用价值。
WinCE C 编程的核心概念
WinCE C 编程与桌面 Windows C 编程(Win32 API)非常相似,但有几个关键的区别和需要注意的地方。
核心编程模型:Win32 API
WinCE 的核心是 Win32 API 的一个子集,这意味着你熟悉的 CreateFile, ReadFile, WriteFile, CreateThread, MessageBox 等函数在 WinCE 中大多都存在,这是 WinCE C 编程的基础。
关键差异点
- CPU 架构 (ARM/x86/MIPS):WinCE 支持多种 CPU 架构,最常见的是 ARM,你的代码需要针对目标设备的 CPU 架构进行交叉编译,你不能直接在 x86 的 PC 上编译然后运行在 ARM 设备上。
- 内存管理:WinCE 设备通常内存有限,动态内存分配 (
malloc,new) 需要格外小心,避免内存泄漏。GlobalAlloc/LocalAlloc等 16 风格的内存函数在 WinCE 中仍然可用,但推荐使用更现代的HeapAlloc。 - GUI 模型 (Windows CE vs. Windows Mobile):
- Windows CE (标准设备):通常使用
Win32 API来创建窗口、控件,这与桌面 Windows 开发非常相似。 - Windows Mobile (智能手机):为了更小的屏幕和触控操作,它引入了
Window Mobile Control(CECOMMAND按钮),其消息机制和控件创建方式与标准 WinCE 有细微差别。
- Windows CE (标准设备):通常使用
- 文件系统:WinCE 支持多种文件系统,最常见的是 FAT32,它也支持更现代的 TFAT (Transaction-safe FAT) 来防止突然断电导致文件损坏。
- 没有
conio.h:在 DOS 和一些旧的嵌入式环境中,conio.h提供了如getch(),clrscr()等控制台函数。标准 WinCE SDK 中不包含conio.h,如果你的程序需要一个命令行窗口,通常使用CreateProcess来创建一个控制台子进程。
开发环境搭建
这是最关键的一步,因为 WinCE 开发已经不再是主流,所以环境配置可能需要一些技巧。
必需的工具
- Visual Studio (VS):你需要一个支持旧版 SDK 的 Visual Studio。Visual Studio 2008 是最经典、最稳定的选择,也是很多遗留项目的标准,VS 2010 也可以,但配置可能稍复杂。VS 2025 及更高版本 默认不再包含 WinCE SDK,但可以通过安装旧版 SDK 的方式来支持,不推荐新手尝试。
- Windows Embedded CE Platform Builder (PB):
- 这是什么? Platform Builder 是用于定制和构建整个 WinCE 操作系统镜像的工具,如果你只是想在现有的 WinCE 设备上开发应用程序,你不一定需要 PB。
- 什么时候需要? 当你需要:
- 为你的设备定制内核,添加或删除驱动程序。
- 创建一个全新的、包含你所有驱动和应用的操作系统镜像。
- 深入理解底层系统。
- 注意:PB 是一个极其庞大和复杂的工具,通常由硬件厂商或 BSP(板级支持包)开发者使用。
标准应用程序开发流程(不需要 PB)
对于大多数应用开发者,流程如下:
- 安装 Visual Studio 2008。
- 安装 Windows Mobile 6 Professional SDK 或 Windows Embedded CE 6.0 R2 SDK。
这些 SDK 会为 VS 2008 添加相应的项目模板和库文件,你可以从微软官网下载(可能需要翻墙或寻找历史存档)。
- 连接设备:
- 通过 ActiveSync (旧版) 或 Windows Mobile Device Center (新版,适用于 WinCE 5.0+) 将你的 WinCE 设备与 PC 连接。
- 确保设备开启了“启用无线连接”或类似的选项,PC 和设备处于同一网络,以便进行远程调试。
第一个 WinCE C 程序:Hello World
我们将创建一个简单的窗口程序。
创建项目
- 打开 Visual Studio 2008。
- 选择 "File" -> "New" -> "Project..."。
- 在 "Visual C++" 下,选择 "Smart Device"。
- 选择 "Windows Mobile 6 Professional" SDK(或你安装的其他 WinCE SDK)。
- 选择 "Empty Smart Device Project"。
- 给项目命名,
WinCE_HelloWorld,点击 OK。
添加代码
在解决方案资源管理器中,右键点击 "Source Files" -> "Add" -> "New Item..."。
选择 "C++ File (.cpp)",命名为 main.c,然后点击 "Add"。
将以下代码粘贴到 main.c 中:
#include <windows.h> // Win32 API 的核心头文件
// 窗口过程函数的声明
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 1. 注册窗口类
WNDCLASS wc = {0};
wc.lpfnWndProc = WindowProc; // 指向窗口过程函数
wc.hInstance = hInstance;
wc.lpszClassName = TEXT("HelloWinCEClass"); // 窗口类名
if (!RegisterClass(&wc)) {
MessageBox(NULL, TEXT("窗口类注册失败!"), TEXT("错误"), MB_OK | MB_ICONERROR);
return 1;
}
// 2. 创建窗口
HWND hwnd = CreateWindow(
TEXT("HelloWinCEClass"), // 窗口类名
TEXT("我的第一个 WinCE 程序"), // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口样式
CW_USEDEFAULT, // x 坐标
CW_USEDEFAULT, // y 坐标
320, // 宽度
240, // 高度
NULL, // 父窗口句柄
NULL, // 菜单句柄
hInstance, // 实例句柄
NULL // 创建参数
);
if (hwnd == NULL) {
MessageBox(NULL, TEXT("窗口创建失败!"), TEXT("错误"), MB_OK | MB_ICONERROR);
return 1;
}
// 3. 显示和更新窗口
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// 4. 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg); // 翻译消息(如键盘消息)
DispatchMessage(&msg); // 分发消息到窗口过程
}
return (int)msg.wParam; // 程序退出时返回
}
// 窗口过程函数:处理发送到窗口的所有消息
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
// 当窗口需要重绘时(如首次显示、被遮挡后显示)
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// 在这里进行绘制
TextOut(hdc, 50, 50, TEXT("Hello, Windows CE!"), 18);
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY: {
// 当窗口被销毁时
PostQuitMessage(0); // 发送 WM_QUIT 消息,使 GetMessage 返回 0
return 0;
}
// 处理其他消息...
default:
// 对于我们不关心的消息,交给系统默认处理
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
编译和部署
- 选择配置:在 VS 顶部,确保选择 "Release" 和 "ARMV4I" (或你的设备对应的 CPU 架构)。
- 连接设备:确保你的 WinCE 设备已通过 USB 或网络连接到 PC,并且设备上的调试服务已启动。
- 部署:右键点击项目,选择 "Deploy",VS 会自动编译代码,然后将生成的
.exe文件复制到设备的\Program Files目录下。 - 运行:在你的 WinCE 设备上,通过文件管理器找到并运行
WinCE_HelloWorld.exe,你应该能看到一个标题为 "我的第一个 WinCE 程序" 的窗口,里面有 "Hello, Windows CE!" 的文字。
WinCE C 编程常见任务和注意事项
串口编程
WinCE 串口编程和桌面 Windows 几乎一样。
#include <windows.h>
HANDLE hSerial = CreateFile(TEXT("COM1:"), // 串口名
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);
if (hSerial == INVALID_HANDLE_VALUE) {
// 处理错误
}
DCB dcb = {0};
dcb.DCBlength = sizeof(dcb);
GetCommState(hSerial, &dcb);
dcb.BaudRate = CBR_9600; // 设置波特率
dcb.ByteSize = 8; // 数据位
dcb.Parity = NOPARITY; // 无校验
dcb.StopBits = ONESTOPBIT; // 停止位
SetCommState(hSerial, &dcb);
// 写数据
char data[] = "Hello from PC!";
DWORD bytesWritten;
WriteFile(hSerial, data, strlen(data), &bytesWritten, NULL);
// 读数据
char buffer[256];
DWORD bytesRead;
ReadFile(hSerial, buffer, sizeof(buffer)-1, &bytesRead, NULL);
if (bytesRead > 0) {
buffer[bytesRead] = '\0';
// 处理接收到的数据
}
CloseHandle(hSerial);
多线程编程
同样使用 CreateThread。
DWORD WINAPI MyThreadProc(LPVOID lpParam) {
// 线程要执行的代码
for (int i = 0; i < 10; i++) {
Sleep(1000); // 休眠1秒
// 注意:在 WinCE 中,UI 控件不能在非 UI 线程中直接操作
}
return 0;
}
// 在主线程中创建线程
HANDLE hThread = CreateThread(NULL, 0, MyThreadProc, NULL, 0, NULL);
if (hThread == NULL) {
// 错误处理
}
// 等待线程结束 (可选)
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
文件和注册表操作
文件操作 (CreateFile, ReadFile, WriteFile, FindFirstFile) 和注册表操作 (RegOpenKeyEx, RegQueryValueEx, RegSetValueEx) 的 API 与桌面版基本一致。
注意事项
TCHAR和_T():为了同时支持 ASCII 和 Unicode (WinCE 主要使用 Unicode),代码中应广泛使用TCHAR代替char,用_T()或TEXT()宏包围字符串字面量。TCHAR szText[] = _T("你好");。- 错误处理:几乎所有的 Win32 API 在失败时都会返回一个无效值(如
NULL或0),并设置GetLastError(),养成良好的错误检查习惯至关重要。 - 屏幕和触摸:获取屏幕分辨率可以使用
GetSystemMetrics,处理触摸输入主要通过WM_TOUCH消息,这比处理鼠标消息要复杂一些。 - 电源管理:如果你的应用需要长时间运行,应考虑电源管理,可以使用
SetSystemPowerState来请求系统休眠或关机,或者通过注册NOTIFICATION_EVENT来响应电源变化。
学习资源
由于 WinCE 已是旧技术,资源相对分散。
- 微软官方文档 (MSDN):最好的资源,尽管是历史文档,但内容最权威,搜索 "Windows CE Documentation"。
- 书籍:寻找旧版的书籍,如《Windows CE 程序设计》、《Programming Windows CE》等。
- 博客和论坛:
- CodeProject:有很多 WinCE 开发的示例文章。
- Stack Overflow:搜索标签
[windows-ce]。 - 国内的 CSDN、博客园:搜索关键词 "WinCE 开发"、"WinCE 串口" 等,能找到很多中文实践经验和踩坑记录。
- 硬件厂商文档:如果你在使用特定硬件(如工业主板),硬件厂商提供的 BSP 文档 是最宝贵的资源,里面包含了针对该硬件的驱动说明和开发指南。
WinCE C 编程本质上是在一个资源受限、架构多样的嵌入式设备上使用熟悉的 Win32 API,其核心挑战在于:
- 环境搭建:找到合适的工具链和 SDK。
- 交叉编译:确保为正确的 CPU 架构编译。
- 硬件适配:理解并使用特定硬件的驱动和 BSP。
掌握了这些要点,你就可以有效地在 WinCE 平台上进行 C 语言开发和维护工作。
