我们知道,有些函数的功能用软件本身的自带函数是实现不了的,这时候就需要自己写函数或者调用WIN32的相关API函数才能实现某些功能,毕竟软件本身的内部函数实在是太少了,下面我们来讲讲如何写自定义函数和如何引用WINDOWS API函数。
一、 VB
l VB的自定义函数格式:
Private Function 函数名(变量名 数值类型)数值类型
....中间写过程
End Function结束
在窗口的代码区,随便找个地方,但不要和其它过程相交就行,如一个简单的进制转换例子:自定义一个将十进制转为二进制的函数DEC_to_BIN。
Public Function DEC_to_BIN(Dec As Long) As String
DEC_to_BIN = ""
Do While Dec > 0
DEC_to_BIN = Dec Mod 2 & DEC_to_BIN
Dec = Dec \ 2
Loop
End Function
l VB中使用API函数:
在VB中声明API函数有两种方法:如果我们只在某个窗体中使用API函数,我们可以在窗体代码的General部分声明它:
声明的语法是:
Private Declare Function ...
Private Declare Sub.....
这里必须采用Private声明,因为这个API函数只能被一个窗体内的程序所调用。
如果我们的程序有多个窗体构成,而且我们需要在多个窗体中使用同一个API函数,就需要在模块中声明了。先添加一个模块,然后采用如下语法声明:
Public Declare Function....
Public Declare Sub....
Public声明的含义是把API函数作为一个公共函数或过程,在一个工程中的任何位置(包括所有的窗体和模块)都能直接调用它。 声明完毕我们就能在程序中使用此API函数了。
可采用以下几种方式使用API函数,以SetWindowPos函数为例:
(1)忽略函数返回值的调用:
SetWindowPos Form1.hWnd, -2 ,0 ,0 ,0, 0, 3
注意此时函数的参数是不加括号的。
(2)Call方法调用:
Call SetWindowPos(Form1.hWnd, -2, 0, 0, 0, 3)
注意这里需要加上括号,但我们不取回函数的返回值。
(3)取得函数返回值的调用:
MyLng = SetWindowPos(Form1.hWnd, -2, 0, 0, 0, 3)
此时需要加上括号,而且我们必须事先定义一个变量(变量的类型与函数返回值类型相同)来存储API函数的返回值。
一个典型的例子,取硬盘序列号:
在模块中加入下列声明:
Public Declare Function GetVolumeInformation Lib "kernel32" _
Alias "GetVolumeInformationA" (ByVal lpRootPathName As String, _
ByVal lpVolumeNameBuffer As String, ByVal nVolumeNameSize As Long, _
lpVolumeSerialNumber As Long, lpMaximumComponentLength As Long, _
lpFileSystemFlags As Long, ByVal lpFileSystemNameBuffer As String, _
ByVal nFileSystemNameSize As Long) As Long
'得到某一磁盘分区的信息,如C:
窗体代码如下:
Option Explicit
Private Regid, Localid As Long
Private Sub CmdLocalID_Click()
'根据C盘序列号得到原ID
Dim Driver, VolName, Fsys As String
Dim volNumber, MCM, FSF As Long
Driver = "c:\"
Dim res As Long
res = GetVolumeInformation(Driver, VolName, 127, volNumber, MCM, FSF, Fsys, 127)
'volNumber是C盘序列号
End Sub
二、DELPHI
l 自定义函数
Procedure 函数名( 变量名,数值类型):数值类型 ;
var
//加入定义
Begin
//加入程序语句
End;
例:十进制转二进制
Function DecTobin(Value :Integer) : string;
Var
ST:String;
N:Integer;
Begin
ST:='';
n:=value;
While n>=2 Do
Begin
st:=st+IntToStr(mod_num(n,2));
n:=n div 2;
End;
st:=st+IntToStr(n);
Result:=reverse(st);
End;
l 调用API
在Delphi中调用Windows API 函数十分方便,只需在单元的uses段加入Windows 单元名即可(对于由Delphi自动创建的单元,该项工作已经完成)。单元Windows.pas 已经由Delphi编写并直接提供开发者引用。
取硬盘序列号:
function GetHDNumber(Drv : String): DWORD; //得到硬盘序列号
var
VolumeSerialNumber : DWORD;
MaximumComponentLength : DWORD;
FileSystemFlags : DWORD;
begin
if Drv[Length(Drv)] =':' then Drv := Drv + '\';
GetVolumeInformation(pChar(Drv),
nil,
0,
@VolumeSerialNumber,
MaximumComponentLength,
FileSystemFlags,
nil,
0);
Result:= (VolumeSerialNumber);
end;
l 调用API
在VC++(非MFC,MFC可以直接调用)中,一般只要先包含Windows.h文件就可以了,不需要额外的步骤,因为所有函数都是现成的。个别函数可能需要包含其他头文件或库文件,如果VC++报告找不到某个函数的定义,可以看一下MSDN Library,在每个API函数的说明的Requirements 部分列出了所需要的头文件、库文件,以及支持的操作系统。如巧nitCommonControlEx的相应说明如下:
Requirements
Version 4.70 and later of Comctl32.dll
Windows NT/2000: Requires Windows 2000 (or Windows NT 4.0 with Internet Explorer 3.0 and later).
Windows 95/98/Me: Requires Windows 98 (or Windows 95 with Internet Explorer 3.0 or later).
Header: Declared in commctrl.h.
Import Library: comctl32.lib.
这说明调用该函数需要包含commctrl.h,并加入comctl32.lib。
例:
char m_Volume[256];//卷标名
char m_FileSysName[256];
DWORD m_SerialNum;//序列号
DWORD m_FileNameLength;
DWORD m_FileSysFlag;
::GetVolumeInformation("c:\\", m_Volume, 256, &m_SerialNum, &m_FileNameLength, &m_FileSysFlag, m_FileSysName, 256);
在代码中直接引入汇编有时候可以省了不少事情,有的语句甚至可以直接抄原程序,只要稍做修改就行。而且执行速度比纯代码快了不少。
一、VB
VB不能直接引入汇编代码,不象VC,Delphi可以直接在程序中写汇编代码,这是非常可惜的事情。很多VB程序员通过各种转换的方法使VB支持直接写入汇编代码,效果都不是很好,比较实用的是用VbInLineASM插件和ThunderVB(可以让VB支持汇编和C代码)。本人也曾经发过相关的文章,所以这里就不再重复了。我用VbInLineASM有测试成功,ThunderVB由于要用英文版VB,没有做测试。很多人说用VbInLineASM都没有成功,这是因为这个插件有BUG,你只要注意几点就一定会成功的:一定要与MASM的ML.EXE连接上。另外,VB工程必须置于盘符根目录,比如C盘的PROJECT文件夹,文件名不能用中文(这点非常重要)。另外只有编译后汇编代码才能实现功能。不能用P-CODE编译。每次更改VbInLineASM设置后最好重新启动工程一次。
一个典型的例子:获取CPU主频。
Private Sub Command1_Click()
D0im myLong(2) As Long
Dim I As Long
Dim lngPointer1 As Long, lngPointer2 As Long, lngPointer3 As Long
Dim lngCPUFeatures(4) As Long, lngCacheInfo(4) As Long
lngPointer1 = VarPtr(myLong(0))
lngPointer2 = VarPtr(lngCPUFeatures(0))
lngPointer3 = VarPtr(lngCacheInfo(0))
Text1.Text = asmCPUID(lngPointer1, lngPointer2, lngPointer3)
End Sub
=================ASM Module================
Public Function asmCPUID(ByVal lngPointer1 As Long, ByVal lngPointer2 As Long, ByVal lngPointer3 As Long) As Long
'#ASM_START
'.686
' push ebp
' mov ebp, esp
' push ecx
' push edx
' push edi
' mov eax, 0
' CPUID
' mov edi, DWORD PTR [ebp+8]
' mov [edi], ebx
' mov edi, DWORD PTR [ebp+8]
' Add edi, 4
' mov [edi], edx
' mov edi, DWORD PTR [ebp+8]
' Add edi, 8
' mov [edi], ecx
' cmp eax, 0
' je ExitASM
' mov eax, 1
' CPUID
' mov edi, [ebp+12] ;//Pointer to array2
' mov [edi], eax
' Add edi, 4
' mov [edi], ebx
' Add edi, 4
' mov [edi], ecx
' Add edi, 4
' mov [edi], edx
' mov eax, 0
' CPUID
' cmp eax, 1
' jng ExitASM
' mov eax, 2
' CPUID
' mov edi, [ebp+16]
' mov [edi], eax
' Add edi, 4
' mov [edi], ebx
' Add edi, 4
' mov [edi], ecx
' Add edi, 4
' mov [edi], edx
'ExitASM:
' pop edi
' pop edx
' pop ecx
' mov esp, ebp
' pop ebp
' ret 12
'#ASM_END
End Function
二、 DELPHI
Delphi调用汇编,有两种方式。(引用Delphi+汇编例子)
一如---
procedure TForm1.DrawRain;
var
x1,y1,x2,y2,d,i:integer;
begin//delphi程序开始
for i:=0 to 100 do
begin
x1:=random(537);
y1:=random(280);
d:=random(7);
asm//内嵌汇编开始
push eax
mov eax,x1
sub eax,d
mov x2,eax
mov eax,y1
add eax,d
mov y2,eax
pop eax
end;;//内嵌汇编结束
times:=times+1;
drawLine2(x1,y1,x2,y2,clmedGray);
wait();
if (i div 2)=0 then drawLine2(x1,y1,x2,y2,clwindow);
end;
end;//delphi程序结束
还有一种,把外层的begin...end去掉,通过asm...end直接进入汇编。
procedure TForm1.Wait();
asm//内嵌汇编开始
push eax
mov eax,0
@loop:
add eax,1
cmp eax,1000000
jnz @loop
pop eax
end;//内嵌汇编结束