Keyboard Backlight Control on Lenovo Ideapad & Xiaoxin Models

Ever found the keyboard backlight annoying? It keeps turnning on when booting Windows, and there is no configuration to disable it permanently in any Lenovo Utilities.

Just did some reverse engineering to find how to control keyboard backlight programmatically. The principal is simple, use \\.\EnergyDrv device exposed by Lenovo ACPI energy management driver. It is capable of controlling all keyboard backlight levels. Also other capabilities available 🙂 . See code:

Should built and run on any C99 compilers. Run with <app.exe> [0|1|2|3]. You can add it to task scheduler to disable keyboard backlight on startup.

Also checked other approaches. The usb/hid way does not work on an ideapad. The Keyboard_Core.dll hack also does not work, I cannot find the file in drivers.

Windows XP Targeting with C++ in Visual Studio 2012

Just downloaded and tried Visual Studio 2012(with update 2, version 11.0.60315.01). The Windows XP targeting is available(actually already available in update 1):

 photo vs2012_xp_target_zps10d34c42.png

The executable generated by the original VS2012 toolchain does not run under Windows XP. A error message box is shown:

 photo vs2012_xp_target_2_zpsc799786a.png

In update 1, the static and dynamic link libraries for the CRT, STL and MFC have been updated in-place to add runtime support for Windows XP and Windows Server 2003. And the runtime version is upgraded to 11.0.51106.1 from 11.0.50727.1.

Except the library update, there’s none real difference when selecting “v110” or “v110_xp” toolchain. I wrote a simple HelloWorld application and compare the two generated binary.

And the output:

The first difference represents the timestamps of the two binary. The other two differences standard for “Operating System Version” and “Subsystem Version”. We have 5.1 for Windows XP, 6.0 for Windows Vista and later. That’s all. And we can easily build a Windows XP binary from the command line with only one additional linker switch:

I also built a simple MFC application(dynamic link to MFC) with Windows XP target in VS2012. It runs fine under Windows XP with MFC DLLs copied in the same directory. From VS2010, the SxS assembly is not used any more. All you need to do is copy the dependent DLLs to the application directory and run.

Reference: http://blogs.msdn.com/b/vcblog/archive/2012/10/08/10357555.aspx

Apache Modules 学习笔记(1)

最近看了这本<<The Apache Modules Book>>: http://www.amazon.com/gp/product/B000SEGRM8/, 记录一下.

每次我们学一个新的东西的时候, 似乎都会写一个程序叫做”hello world”, 今天的目的也在于此. 看这本书的目的主要是为了了解Apache 的扩展性到底是如何做到的. Apache 主要提供了hook, filter, provider 等机制. 其次, 就是Apache 的跨平台和平台相关的优化. 本人对这些东西的了解还比较粗浅, 本书感觉也只是在大量的贴Apache 的源码, 所以还是要看Apache 的manual. 最后, Apache 的源码确实写的非常有参考价值, 很多东西我都不知道原来能那么用的.

好了, 进正题, 我们要写的是一个”hello world” 的generator module. 运行的结果如图:

apache_1_1

然后是代码, 有点长:

这个module 的用途是打印接受到的request 的header 信息. 需要知道的有两部分: module 的声明, module 的hook 函数. Apache 模块的都是通过”module” 这个struct 来声明导出的, 在这个struct 中会初始化这个模块的各个函数指针. 在我们的代码中, 中间5 个值都是NULL, 它们是用来安装配置文件相关的hook 的, 暂时不用. 最后一个hook 则指向一个相当于运行时的hook 函数, 在这个函数, 即”helloworld_hooks” 中, 我们指定Apache 的那些处理过程会被我们hook 到. 这里我们使用了ap_hook_handler 这个函数, 它表明我们的模块是一个generator handler. 它的参数helloworld_handler 依然是一个函数指针, 表示具体的处理过程. 其它的代码都是html 的生成, 先随便看看吧.

接下来是编译的问题. 如果用VC 的话, 那么就是简单的把apache, apr, apr-util 的include 和lib 的路径加进去, 基本就通过编译了. 不过有的module 可能会依赖其它module, 个么这个也自己加. 我写了一个简单的Makefile 来编译, 如下:

vs2005, vs2008 皆可通过编译. vs 的-I 选项似乎不支持绝对路径, 所以编译之前请修改$(APACHE) 变量. 另外, 发现一个问题就是, debug 编译的Apache 不能加载release 编译的module. 后来发现是vs2005/2008 的CRT dll的SxS 的问题. 所以, Apache 和module 的编译器最好是同一版本和配置, 这样CRT 才能被正确加载进来. 或者就是静态链接到CRT.

把编译出来的*.so 文件拷贝到Apache 的modules 文件夹下. 最后来修改配置文件. 打开httpd.conf, 添加如下代码:

LoadModule 指令用来加载模块, 第一个参数是在代码中导出(export) 的模块名, 第二个参数是模块的路径. 然后来设置映射关系, 凡是URL 是/helloworld 开头的, 都用helloworld 这个handle 来处理, 而helloworld 这个handle, 实际上只是我们在代码中字符串比较用的, 参见helloworld_handler 这个函数.

以上.

Building Apache Web Server with Visual Studio 2005

1. Source

a) apache 2.2.13: http://www.apache.org/dist/httpd/httpd-2.2.13-win32-src.zip
b) zlib 1.2.3 (for mod_deflate): http://www.zlib.net/zlib-1.2.3.tar.gz
c) openssl 0.9.8k (for mod_ssl): http://www.openssl.org/source/openssl-0.9.8k.tar.gz

2. Tools

a) ActivePerl: http://aspn.activestate.com/ASPN/Downloads/ActivePerl/
b) awk & patch tools: http://gnuwin32.sourceforge.net/packages.html

3. Steps

a) Setup Perl environment, add %Perl%/bin to %PATH%.
b) Also add awk, path tools to %PATH%.
c) Decompress the apache source code to %Apache%, D:Apache maybe.
d) Decompress the zlib into srclib subdirectory named zlib.
e) Decompress the openssl into srclib subdirectory named openssl.
f) Now the source tree should look like:

g) Patch zlib:
Download the patch from: http://www.apache.org/dist/httpd/binaries/win32/patches_applied/zlib-1.2.3-vc32-2005-rcver.patch. This patch contains minor fixes and enable generation of *.pdb files.
Copy the patch file into zlib subdirectory, swith to the directory in cmd.exe and run the command:

h) Patch openssl:
Download the patch from: http://www.apache.org/dist/httpd/binaries/win32/patches_applied/openssl-0.9.8k-vc32.patch. This patch will correct a link issue with zlib and enable generation of *.pdb files.
Copy the patch file into openssl subdirectory, swith to the directory in cmd.exe and run the command:

i) Build zlib:

j) Build openssl:

k) Patch Apache:
There’s an issue in the Makefile.win that build Apache in 2.2.13: https://issues.apache.org/bugzilla/show_bug.cgi?id=47659. Download the patch against branch into the %Apache% directory and run the command:

l) Build Apache using command line:
Now you can buid Apache by:

And install Apache by:

m) Build Apache using Visual Studio 2005:
There’s also a flaw in the *.vcproj conversion of *.dsp through Visual Studio 2005. We must run a perl script to fix it first:

Now, everything are OK. In Visual Studio 2005, open the Apache.dsw and convert all *.dsp files to *.vcproj files. Then build the project “BuildBin”. The project “InstallBin” project will distribute all the project in the Apache solution.

4. Debugging with Visual Studio 2005

It’s quite simple. After build the project “InstallBin”, open the property page of the “httpd” project. Switch to “Debugging” tab, change the Command value into your binary of installed directory. Now, add breakpoints, and press F5 to start your tracing or debugging.

5. Reference

a) Compiling Apache for Microsoft Windows
b) Apache 2.2.9 command line build with the Windows 2008 SDK

笔记 – Programming Windows (2)

今天来记录一下windows 中GDI (Graphics Device Interface) 的相关内容.

GDI 的目标是提供一种设备无关的绘图方式, 要支持不同的monitor 和graphics card.

1. WM_PAINT 消息

我们还是以以下的window procedure 代码为例:

这次关注的让然是WM_PAINT 消息的处理. 首先我们调用BeginPaint() 函数返回一个HDC 的handle. DC(Device Context) 可以理解成跟设备联系起来的, 可以在上面绘图的一个东西. 这里的设备一般指的是monitor, 但是也可以是printer. 得到这个handle 之后, 我们就可以在DC 上绘图了, DrawText() 函数就在HC 的正中间画了一个字符串. 最后就是EndPaint() 函数来说明绘图结束. 这里有2 个概念.

第一个概念叫做invalid region. 当一个被遮盖的窗口被重新显示的时候, 实际上要重画的只是被遮盖的部分. 而这些被遮盖的需要重画的部分就叫做invalid region. windows 会给窗口发WM_PAINT 消息来让窗口重画. 在代码中ps 变量(PAINTSTRUCT 结构)的rcPaint 域实际上包含了这个invalid region 的信息. 当我们调用BeginPaint() 的时候,实际上把这个invalid region 给validate 了. 不然的话, windows 检测到还有invalid region 没被重画就会不断的发送WM_PAINT 消息, CPU 会100% 的.

第二个概念叫做clipping region. 当我们调用BeginPaint() 的时候, 实际还把一个invalid region 转换成了一个clipping region. 什么意思呢? 就是之后的GDI 调用的绘图都会只限于这个clipping region 中. 如果某个GDI 调用画在了这个clipping region之外, 实际是不会有调用开销的, 这也是windows GDI 的一个优化. 虽然我觉得GDI 还是很慢的=v= (GDI+ 更慢…).

除了调用BeginPaint(), EndPaint() 来得到DC 外, 还可以通过调用GetDC() 这个API. 比如在处理一个鼠标消息, 可能需要在屏幕上画点什么的时候. 不过有这样几点需要注意: 1) BeginPaint(), EndPaint() 只能被用在WM_PAINT 消息中. 2) GetDC() 不会去把invalid region 给validate, 我们需要手动调用ValidateRect() 或ValidateRgn() 这2 个API. 完了之后, 记得调用ReleaseDC().

有的时候, 我们需要手动刷新一个window. 也有两种方法: 1) 调用InvalidateRect() 函数. 这种方法是post 一个WM_PAINT 消息到当前window 的message queue 中, 等待刷新. 一个message queue 中不会出现多个WM_PAINT 消息, windows 会自动合并. 2) 调用UpdateWindow() 函数来强制刷新window, 相当于send 一个WM_PAINT 消息来直接调用window procedure 中的处理代码.

2. GDI Objects

现在来看一下DC 的使用. 一个DC 可以有很多的属性. 有以下5 种: Bitmap, Brush, Font, Pen, Region. Bitmap 可以理解成DC 中的画布(canvas), 所有画在DC 上的元素实际都是画在Bitmap 这个属性中的. Brush 表示的是绘图的背景属性, 可以是单色, 渐变色, 或是一个图案(pattern). Font 自然是字体, 包括大小, 颜色, 粗细等. Pen 表示的是画笔的属性, 包括颜色, 粗细, 线段起始点, 拐点的一些绘画属性. Region 表示一个区域, 可以理解为Bitmap 这个canvas 上的一个clipping region.

我们通过SelectObject() 这个API 来设置这些属性. 以Brush 为例, 可以使用系统与定义的Brush:

也可以使用自定义的Brush:

其它4 种属性的调用相似. 只是最后自定义的属性, 使用完了一定要记得调用DeleteObject() 这个API 来删除, GDI 的object 也是会泄漏(leak) 的.

另外, 有一个logic object 的概念. 当我们调用CreateXXX() 函数的时候, 得到的GDI object 实际已经跟特定的DC 相关联了, 但是有时候, 我们并不要实际的GDI object, 而只是需要一个包含这些信息的一个数据结构. 于是我们就可以使用所谓的logic object. 以Font 为例. 在GDI 中CreateFont() 这个API 可以说是最麻烦的一个API 了, 光参数就有14 个. 相应的, 还有另外一个API 叫做CreateFontIndirect(), 它的参数是一个LOGFONT 结构, 其中的域对应了CreateFont() 的14 个参数, 所以可以用这两个API 完成同样的工作. 其它四种GDI object 也有类似的logic object.

3. GDI 坐标映射

到目前为止, 我们并没有设置过GDI 中的任何坐标. 所以我们到底是在用什么单位(unit) 来绘图的呢? 在GDI 中, 默认的坐标映射模式是MM_TEXT. 需要指出的是, 我们在GDI 函数中传入的数字都是逻辑坐标(logic coordinates), 而GDI 会根据当前的映射模式来重新加算设备相关的视点坐标(viewport coordinates).

其它的映射模式包括: MM_LOMETRIC, MM_HIMETRIC, MM_LOENGLISH, MM_HIENGLISH, MM_TWIPS, MM_ISOTROPIC, MM_ANISOTROPIC. 前5 个映射模式只是逻辑坐标的不同, 而x 轴和y 轴坐标都是等比例映射缩放的, 后两种映射模式允许非等比例的坐标映射. 具体的映射规则请查阅MSDN, 因为非常非常的麻烦, 丸子只给出一个映射的计算公式, 其中(xWinOrg, yWinOrg) 是逻辑坐标的原点, (xViewOrg, yViewOrg) 是视点坐标的原点:

  • xViewport = (xWindow – xWinOrg) * xViewExt/xWinExt + xViewOrg
  • yViewport = (yWindow – yWinOrg) * yViewExt/yWinExt + yViewOrg

这些数值当然不需要手动计算, GDI 主要提供了这样5 个函数来进行坐标映射的操作: SetMapMode(), SetWindowOrgEx(), SettWindowExtEx(), SetViewportOrgEx(), SetViewportExtEx().

根据丸子的经验, 为了计算坐标更容易, 一般只需要调用SetViewportOrgEx() 来设置视点坐标就可以了, 其它API 函数基本可以不用的.

4. DIB和DDB

DIB(Device-Independent Bitmap) 设备独立的位图, DDB(Device-Dependent Bitmap) 设备相关的位图.

什么意思呢? 简单来说, DIB 就是存在硬盘上的bitmap 文件, DDB 就是要在一个DC 上画bitmap 的时候, 一个HBITMAP 的handle 在windows 内存中表示的bitmap. 这里有个很搞笑的事情就是: windows 的GDI 函数中, 并没有提供从文件中读取DIB 的API 函数(Gdiplus 中有), 于是我们要手动写把DIB 转换成DDB 的函数, 这就需要我们了解DIB 的文件结构.

bitmap, 位图, 就是把一张图的所有颜色信息按照pixel 来存储, 所以我们可以把一个DIB 读到内存中, 并把这些按pixel 存储的信息放在一个数组中. 注意, 这里不仅仅是bmp 图片, jpg, png 等其它图片格式, 如果要画到GDI 的DC 上, 都要经过这写操作步骤. 这些步骤搞定之后, 我们可以调用SetDIBitsToDevice() 函数来把这些数组中的信息画到一个DC 上去, 注意倒数第二个参数:

调用SetDIBitsToDevice() 只是画DIB 的一种方法, 第二种是把一个DIB 转换成一个DDB, 然后就可以调用windows 的Bitblt() 函数来更高效的绘制bitmap 了. 之所以说更高效, 是因为SetDIBitsToDevice() 是一个一个pixel 画的, 而DDB 是已经包含跟DC 相关的bitmap 信息的了. 可以调用CreateDIBitmap() 函数来生产一个DDB, 嗯.. 你没看错, 名字和用处居然不一致. 然而这种方法的缺点是, 不能按pixel 来访问bitmap 的信息了.

于是, 我们有第三种方法, 调用CreateDIBSection() 函数. 这个函数既能转换DIB 到DDB, 又能提供按pixel 访问bitmap 信息的方法. 缺点是, 很难用, 这个API 函数返回的虽然是一个HBITMAP 的handle, 但是跟CreateDIBitmap() 返回的handle 不一样, 有诸多需要特别注意的地方. 所以丸子也不推荐使用= =.

总结一下就是, 对于一张不大的bitmap, 且要精心按pixel 操作的话, 使用SetDIBitsToDevice(). 对于比较大的bitmap, 且要被画多次, 那么使用CreateDIBitmap(), 因为DDB 的绘制会比较快速. 当然, CreateDIBSection() 提供了前两者的好处, 问题就是比较难用.

以上.