在Ruby中便利的调用Win32 API (使用windows-pr和CStruct)
缘由
平时工作中,我主要使用C/C++和Ruby。有时经常需要调用API来写一些工具,但又不想动用重量级的C/C++.这时就想到了用Ruby来写。 话说Ruby,确实是程序员的好帮手,方便,快捷。是居家旅行......的必备良药。
在Ruby中有一个Win32API ,是用来调用API的。但是用起来略为有点啰嗦和冗长。 还好,RubyForge上有个Win32 Utils 项目,专门提供了对API调用的封装,使得调用API更方便了一步。 而且,Win32 Utils 已经分门别类的封装好了很多Windows的功能模块,可以直接使用。如win32-file,win32-dir等。
但是,有两个原因导致了Win32API 和Win32 Utils 在使用上的不方便:
-
不管是Win32API 还是Win32 Utils ,虽然解决了调用API的问题,没有很好的解决结构体参数的问题。 我们知道,Windows的很多API大都需要结构体指针的参数,来传入或是传出信息。使用者大多利用String#unpack和Array#pack来处理。
-
Win32 Utils 提供的封装好的功能模块,如: win32-file,win32-dir等,是对API的进一步封装,将API封装在底下。 这样一来,如果想直接利用API来写程序的话,这些模块暂时就用不上了。
windows-pr
概述
Win32 Utils 中有3个十分重要的gem: win32-api ,windows-api ,windows-pr .其他的功能模块都是基于这三个之上。我们来说说这三个的关系:-
win32-api
封装了对API的调用。主要就是一个dll: api.so
-
windows-api
对上面的 win32-api进行了简单包装,使得使用更简单一些。
-
windows-pr
依赖与上面两个,定义了大量的Win32的API和常量,免去了我们自己定义函数的麻烦。当然,也有一些API没有封装,还得需要我们自己动手。
安装
windows-pr的安装很简单: gem install windows-pr解决方案如下:
-
安装windows-pr: gem install windows-pr
这一步,会给你安装3个gem:windows-pr,windows-api 和win32-api-1.4.6-x86-mingw32 (win32-api当前版本是:1.4.6)
-
安装DevKit
按照官方 的步骤来,很简单。DevKit主要就是用来编译Ruby本地扩展的。 -
安装win32-api:gem install win32-api --platform=ruby
这一步,会安装gem:win32-api-1.4.6,并生成新的api.so
- 用win32-api-1.4.6的api.so 替换掉win32-api-1.4.6-x86-mingw32中的api.so。
使用
实际上,Win32 Utils封装的windows的各个功能模块就是使用windows-pr的例子,有兴趣的,可以看看。大家对Win32 Utils也比较熟悉,这里就不过多介绍了。CStruct
概述
CStruct是Ruby 语言用来模仿 C 语言结构体。 有过Win32开发经验的童鞋们一定都知道,Win32 SDK中,大部分的API都需要有结构体指针作参数。例如: void GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer);
Ruby基本的二进制操作主要是利用String#pack和Array#unpack来进行。但是,如果来处理C语言中的结构体就有些冗长啰嗦了。 如何能模拟C语言中的结构体呢? CStruct 就是来做这个事情的。(注意:此CStruct 并不是 DL模块中的CStruct!)
这里只是大体介绍一下CStruct。更多的信息及示例,请看CStruct项目的主页 .
安装
CStruct的安装也很简单: gem install cstructCStruct的基本用法
先看一个C语言的结构体Point:struct Point { int x; int y; };使用CStruct在Ruby中模拟结构体 Point:
class Point < CStruct int32:x int32:y end看起来是比较直观。看看Point的简单用法,来自CStruct的主页。
require 'cstruct ' # struct Point in Ruby: class Point < CStruct int32:x int32:y end # create a Point's instance point = Point .new # assign like as C language point.x = 10 point.y = 20 puts "point.x = #{point.x},point.y = #{point.y}"CStruct提供了几个方法,用于取得结构体相关的信息,如(仅列出常用的):
-
类方法:size,__size__
取得结构体的大小。另:__size__ 是size的一个别名. -
实例方法:<<
给结构体实例赋值。 -
实例方法:data
返回结构体实例的内部存储buffer.这个方法在调用api时经常用到。
Win32Struct
struct MEMORYSTATUS { DWORD dwLength; DWORD dwMemoryLoad; DWORD dwTotalPhys; DWORD dwAvailPhys; DWORD dwTotalPageFile; DWORD dwAvailPageFile; DWORD dwTotalVirtual; DWORD dwAvailVirtual; } ;
class MEMORYSTATUS < Win32Struct DWORD :dwLength DWORD :dwMemoryLoad DWORD :dwTotalPhys DWORD :dwAvailPhys DWORD :dwTotalPageFile DWORD :dwAvailPageFile DWORD :dwTotalVirtual DWORD :dwAvailVirtual end是不是挺像C语言的结构体?
windows-pr & CStruct
# CStruct Examples require 'windows/memory' require 'win32struct' include Windows::Memory # example: # typedef struct _MEMORYSTATUS { # DWORD dwLength; # DWORD dwMemoryLoad; # DWORD dwTotalPhys; # DWORD dwAvailPhys; # DWORD dwTotalPageFile; # DWORD dwAvailPageFile; # DWORD dwTotalVirtual; # DWORD dwAvailVirtual; # } MEMORYSTATUS, *LPMEMORYSTATUS; class MEMORYSTATUS < Win32Struct DWORD :dwLength DWORD :dwMemoryLoad DWORD :dwTotalPhys DWORD :dwAvailPhys DWORD :dwTotalPageFile DWORD :dwAvailPageFile DWORD :dwTotalVirtual DWORD :dwAvailVirtual end # create a MEMORYSTATUS's instance stat = MEMORYSTATUS.new {|st| st.dwLength = MEMORYSTATUS.size } # call API "GlobalMemoryStatus" - See also MSDN GlobalMemoryStatus(stat.data) #output printf "[Physical Memory]\n" printf " total:%12d bytes\n",stat.dwTotalPhys printf " free :%12d bytes\n",stat.dwAvailPhys printf "[Virtual Memory]\n" printf " total:%12d bytes\n",stat.dwTotalVirtual printf " free :%12d bytes\n",stat.dwAvailVirtual printf "[Paging File]\n" printf " total:%12d bytes\n",stat.dwTotalPageFile printf " free :%12d bytes\n",stat.dwAvailPageFile上面的示例代码,像是用C直接调用API一样直观。免去了自己大量的pack,unpack。 CStruct还有其他Win32的例子,比如:列举所有进程 ,获取系统信息等 ,获取系统版本等 ,有兴趣的可以看看。
结束
二维指针 也是 指针。在32平台下,用uint32或DWORD就能表示。