回 帖 发 新 帖 刷新版面

主题:[原创]漫漫原创路 - API类型库篇

写了个API函数,常数,类型声明的类型库,目前只完成了以下几个模块:
Atom
Menu
Error Code
Error Handling
Registry
Window
Window Class

粗略数了一下,大概只声明了两百多个API吧,相关的常数和类型都已经包含在里面的了。请参看附图:
[img]http://blog.programfan.com/upfile/200704/20070409212342.jpg[/img]

由于这个类型库是我手工写的,里面的绝大多函数我都亲自去测试过了,所以使用应该没什么问题(在你的工程中引用这个类型库就可以使用的了)。

少部分(大概十多个)函数我没有测试,是因为这些我对这些函数比较陌生。例如一些MDI的函数,我对其用法不是很清楚,所以没有亲自去测试过,一般这些函数比较少用。
上面那些函数分类模块是根据MSDN2005分的,还有很多模块没有写好,我会在以后慢慢完成它,各位朋友,可以帮我测试一下里面的函数声明是否正确。有什么Bug麻烦回帖告知。

我同时提供UNICODE ANSI版本的API类型库下载。我建议你使用UNICODE版本,我个人认为使用UNICODE版本比VB那个API声明器声明的ANSI版本函数要高效很多,因为NT系统和VB内部的字符串函数都是用UNICODE实现的,如果声明为ANSI版本,就等于VB把自身的UNICODE字符串转化为ASCII字符串,再调用API,然后在API中,Windows把VB传给它的ASCII字符串转换成UNICODE字符串,再调用UNICODE的API.然后返回时又把UNICODE转换成ASCII传给VB,VB再把它转换成UNICODE,明显效率低下:
UNICODE(VB)-> ASCII(VB)-> ASCII(WINDOWS)-> UNICODE(WINDOWS)-> ASCII(WINDOWS)-> ASCII(VB)-> UNICODE(VB)

事实上,我在里面同时提供了两个版本的API,例如SetWindowTextA SetWindowTextW, 只不过我在UNICODE版本的类型库中把SetWindowText设为它的UNICODE版本:SetWindowTextW,在ANSI版本的类型库中,把SetWindowText设为它的ASCII版本:SetWindowTextA。

还有,里面的一些函数我根据常用的用法把它的原有形式改变了,如果你想调用原有形式,就调用xxxPT.PT是Proto Type(原型)的意思。

至于SetLastError的问题,我暂时使用了usesgetlasterror属性(为了测试方便),也就是说,你每调用一个API函数,VB就会自动调用GetLastError把错误代码存储在Err.LastDllError中,我个人认为这样效率太低了。事实上,调用常规声明的API函数,VB就是这样做的。我会在以后版本的类型库中把它删去。

如果使用类型库中声明的API函数,VB会自动把调用的函数入口导入EXE,而不是调用msvbvm60.dll中的DllFunctionCall来间接调用API.个人认为其中少了许多复杂的转换,效率更高。看一下图示:
用常规声明:
[img]http://blog.programfan.com/upfile/200704/20070409225225.jpg[/img]
用类型库声明:
[img]http://blog.programfan.com/upfile/200704/20070409224828.jpg[/img]
至于反汇编代码的区别,我就不帖出来了,相信大家都想得到。

好了,闲话少说,让我们看一下实例吧。下面我们就写一个实现窗体透明效果的程序,实例及源码:
[img]http://blog.programfan.com/upfile/200704/200704100803049.jpg[/img]

OK,这个程序就是那个简单,简单得我不用提供源码下载了。

UNICODE版本:[url]http://upload.programfan.com/upfile/200704092217768.rar[/url]
ANSI版本:[url]http://upload.programfan.com/upfile/200704092219365.rar[/url]


关于UNICODE和ASCII版本的一些区别图示:
ASCII:
[img]http://blog.programfan.com/upfile/200704/20070409222957.jpg[/img]
UNICODE:
[img]http://blog.programfan.com/upfile/200704/2007040922309.jpg[/img]
可以看出UNICODE版本少了很多额外的代码。

注意上图,你会发现有个__vbaSetSystemError函数调用,其实就是调用GetLastError得到Dll错误号码并存储到Err.LastDllError属性中,并且调用SetLastError(0)把错误号码清除。以后我会把usesgetlasterror属性去掉,让VB不自动产生这段代码。

OK, That's all.

回复列表 (共15个回复)

沙发

厉害!

板凳

只是工作量有点大

3 楼

哈哈,楼上言重了,这些只不过是众多API的一小部分,还有很多没有完成呢.

下面是目前所有没有测试过或者测试过不成功的函数:

GetMenuBarInfo, LoadMenuIndirect(不会用)
RtlLookupFunctionEntry, RtlVirtualUnwind, RtlUnwindEx(不会用)
RegLoadKey, RegOpenUserClassesRoot, RegReplaceKey, RegRestoreKey, RegSaveKey, RegSaveKeyEx(要把程序提升权限才可以调用成功)
ArrangeIconicWindows, CascadeWindows, BeginDeferWindowPos, DeferWindowPos, EndDeferWindowPos, TileWindows, GetAltTabInfo, UpdateLayeredWindow(MDI和其它一些和窗口有关的函数,不会用)
GetClassInfoLg, GetClassInfoExLg(It will crash)(不知道原因,程序会崩溃,在VC下调用则失败)

这些函数可以在相应的DLL中找到入口,但由于我能力所限,不知道怎么使用.

除上面那些函数,类型库中其它的函数,我都一一亲自去测试过.即使是上面的函数,也是照MSDN中声明的形式人工转换而来的.

不知道为什么,大家好像都不喜欢用,大家都喜欢用常规声明的方法。我个人认为那样效率太低了,这样每个每次人写程序都在做着这些相同、重复而繁重的工作,无论从写代码速度还是执行速度来看都比较慢。不知道大家是怎么想的?
如果你认为我不对,那么请你找出KEY_ALL_ACCESS这个常数的值,呵呵,晕了吧。但如果引用了这个类型库,什么都不用做,直接键入KEY_ALL_ACCESS就可以用了。要是还不信,再试试FILE_ALL_ACCESS,LWA_ALPHA 或者SetLayeredWindowAttributes。哈哈,晕了吧?

类型库只是起到声明的作用,它里面并没有实际代码,所以程序编译后完成可以脱离类型库环境运行。其实VB的许多函数,常数也是通过类型库声明的,例如Dir Kill FileCopy等函数就是msvbvm60.dll这个DLL里面的TYPELIB资源声明的(类型库资源),用ExeScope打开它就可以看到这些东西:
[color=blue]Module Constants;
GUID={343DB180-2BCC-1069-82D4-00DD010EDFAA};
  vbObjectError = $80040000;
  vbNullString = ;
  vbNullChar = ;
  vbCrLf = #13#10;
  vbNewLine = #13#10;
  vbCr = #13;
  vbLf = #10;
  vbBack = #8;
  vbFormFeed = #12;
  vbTab = #9;
  vbVerticalTab = #11;
Module FileSystem;
GUID={3738A200-2BCC-1069-82D7-00DD010EDFAA};
  function ChDir(Path:BSTR);
  function ChDrive(Drive:BSTR);
  function EOF(FileNumber:I2): bool;
  function FileAttr(FileNumber:I2; ReturnType:I2): I4;
  function FileCopy(Source:BSTR; Destination:BSTR);
  function FileDateTime(PathName:BSTR): variant;
  function FileLen(PathName:BSTR): I4;
  function GetAttr(PathName:BSTR): VbFileAttribute;
  function Kill(PathName:^variant);
  function Loc(FileNumber:I2): I4;
  function LOF(FileNumber:I2): I4;
  function MkDir(Path:BSTR);
  function Reset;
  function RmDir(Path:BSTR);
  function Seek(FileNumber:I2): I4;
  function SetAttr(PathName:BSTR; Attributes:VbFileAttribute);
  function _B_str_CurDir([Drive:^variant]): BSTR;
  function _B_var_CurDir([Drive:^variant]): variant;
  function FreeFile([RangeNumber:^variant]): I2;
  function Dir(PathName:^variant; Attributes:VbFileAttribute): BSTR;[/color]

无所谓啦,反正人各有志,每个人都有他自己的想法。那就自己用吧!

4 楼

[quote]如果你认为我不对,那么请你找出KEY_ALL_ACCESS这个常数的值,呵呵,晕了吧。但如果引用了这个类型库,什么都不用做,直接键入KEY_ALL_ACCESS就可以用了。要是还不信,再试试FILE_ALL_ACCESS,LWA_ALPHA 或者SetLayeredWindowAttributes。哈哈,晕了吧?
[/quote]

我是网上找来的,不知道对不?

Private Const SYNCHRONIZE = &H100000
Private Const STANDARD_RIGHTS_ALL = &H1F0000
Private Const KEY_QUERY_VALUE = &H1
Private Const KEY_SET_VALUE = &H2
Private Const KEY_CREATE_LINK = &H20
Private Const KEY_CREATE_SUB_KEY = &H4
Private Const KEY_ENUMERATE_SUB_KEYS = &H8
Private Const KEY_EVENT = &H1
Private Const KEY_NOTIFY = &H10
Private Const READ_CONTROL = &H20000
Private Const STANDARD_RIGHTS_READ = (READ_CONTROL)
Private Const STANDARD_RIGHTS_WRITE = (READ_CONTROL)
Private Const KEY_ALL_ACCESS = ((STANDARD_RIGHTS_ALL Or KEY_QUERY_VALUE Or KEY_SET_VALUE Or KEY_CREATE_SUB_KEY Or KEY_ENUMERATE_SUB_KEYS Or KEY_NOTIFY Or KEY_CREATE_LINK) And (Not SYNCHRONIZE))
Private Const FILE_GENERIC_EXECUTE As Long = (STANDARD_RIGHTS_EXECUTE Or FILE_READ_ATTRIBUTES Or FILE_EXECUTE Or SYNCHRONIZE)
Private Const FILE_ALL_ACCESS As Long = (STANDARD_RIGHTS_REQUIRED Or SYNCHRONIZE Or &H1FF&)

我想如果在API常数定义里找不到的话,网上找找就可以找的到了。

5 楼

在立即窗口中键入:Debug.Print Hex(KEY_ALL_ACCESS)
立即显示:F003F

键入:Debug.Print Hex(FILE_ALL_ACCESS)
显示:1F01FF

上网找一个常数有点类似于从法国去英国,偏要先来到中国,再穿过太平洋到达美国,然后经过大西洋到达目的地--英国.不信你试一下找SECURITY_ANONYMOUS这个常数的值?

附图:
[img]http://blog.programfan.com/upfile/200704/20070410193910.gif[/img]

6 楼

谢谢楼主!!

7 楼

好长啊!我眼睛都看花了!
一个字“猛”

8 楼

听楼主的一席话,胜读一年书啊,啥了不说了,就说谢谢!!!
[em1]

9 楼

能教教我们怎么自己做类型库吗?

10 楼

~{G?#!~}

我来回复

您尚未登录,请登录后再回复。点此登录或注册