windowsapi:VB5.0和Windows API 间的呼叫窍门技巧

  般会使用WINDOW API情况实在是VB本身不提供某些功能但是程式所需又不得不然例如:读取Registry内资料VB只提供SaveSetting、Getting 等系列指令但是它只能读取特定地区要读、删、更动其他区域值时就无法使用再如:仔细看看Combo BoxEvents其中没有MouseMove但这是我们经常用上个Event那该如何呢?是那只有透过Winodow API而VB呼叫Window API般不都使用API检视员直接将相对应API COPY到我们程式中就好那还用什麽窍门技巧吗?其实不然VB资料格式问题又加上VB本身没有指标在许多地方需要些小窍门技巧才能解决而且我们经常因应区别需求将API 检视员宣告COPY过来後再做些修改最重要如果有个.DLL档它不在API 检视员中定义那时就只有自己想办法啦

  、 整数叁数

  Windows             API32位元VB

    =

  Int, INT            ByVal Long

  UNIT, DWORD           ByVal Long

  BOOL               ByVal Long  ture时为1

  WPARAM, LPARAM, LRESULT    ByVal Long

  Handle(如HKEY)          ByVal Long

  WORD, ATOM, SHORT       ByVal Integer

  BYTE, CHAR            ByVal Byte

  Eg.

  -----------------------------------------------------------------------------

  Windows API 宣告

  SHORT GetKeyState(  nVirtKey )

  对应VB宣告

  Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer

  -----------------------------------------------------------------------------

  这个API 可用来检视某些KEY (如Insert键、Num Lock、CapsLock等)是on/off程式如下:这个例子应该可十分楚看到各个整数间宣告对应

  -----------------------------------------------------------------------------
Dim InsertMode as Integer
InsertMode = GetKeyState(vbKeyInsert) And vbShtMask
If InsertMode = 1 then
  Debug.pr "表示 Insert Mode"
Else
  Debug.pr "表示 OverWrite Mode"
End If
-----------------------------------------------------------------------------


   2、 指向整数指标

  Windows API          32位元VB

   

  LPINT             (ByRef ) Long

  LPUNIT            (ByRef ) Long

  LPBOOL            (ByRef ) Long

  LPDWORD            (ByRef ) Long

  LPHANDLE (如:PHKEY)      (ByRef ) Long

  LPWORD            (ByRef ) Integer

  LPSHORT            (ByRef ) Integer

  LPBYTE            (ByRef ) Byte

  VB内定是使用传址呼叫所以ByRef 可以省略也就是说

  Func(ByRef param1 as type)

  和

  Func(param1 as type)

  是相同使用传址呼叫方式不外乎想将叁数传给API 後将结果传回来然而LONG型态传址呼叫在VB中又占了相当大份量32位元指标都是LONG型态而字串、自定型态Structure在Windows API中是以指标来传递而指标传递事实上也是Long值传递只不过传过去LONG值於WIN API中会将的当成Address而再配合指标运作而得指标所指内容这个观念在後面会很重要

  例如:

  -----------------------------------------------------------------------------
LONG RegOpenKeyEx(
   HKEY    hKey,      // handle of open key
   LPCTSTR   lpszSubKey,   // address of name of subkey to open
   DWORD    dwReserved,   // reserved
   REGSAM   samDesired,   // security access mask
   PHKEY    phkResult    // address of handle of open key
  );
相对应VB 宣告
Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" _
     (ByVal hKey As Long, _
     ByVal lpSubKey As String, _
     ByVal ulOptions As Long, _
     ByVal samDesired As Long, _
     phkResult As Long) As Long  '//最後个叁数是ByRef的宣告
-----------------------------------------------------------------------------


  我们经常会想要用程式来读取Registry中资料例如:我们想得知Win95Product ID该如何做呢?这里有几个观念要先清楚:首先:ProductId在何处呢?在 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVerson下ProductId 我们要取得便是KEY    为 HKEY_LOCAL_MACHINE

  SUBKEY  为 SOFTWARE\Microsoft\Windows\CurrentVerson

  ValueName 为 ProductId value

  然而要取得ProductIdvalue可没那麽直接要先取得SubKeyKeyHandle而KeyHandle取得便是利用RegQueryKeyExAPI 程式部份在介绍Win API字串传递时再并介绍

   3、 字串叁数

  凡是所有字串叁数指标都以 ByVal 叁数名称 As String 传如RegOpenKeyEx第 2叁数 ByVal lpSubKey As String,便是或许会问这个例子是把subkey值传给 Win API所以用ByVal没什麽大不了其实不然要Win API传回字串时定要用ByVal宣告这是VB5字串格式(BSTR)和WIN API标准字串格式(LPSTR)区别原因

  LPSTR 字串格式是NULL Terminate字串若有字串"HaHa !OK!"则格式如下:

  -----------------------------------------------------------------------------

  Address 0 1 2 3 4 5 6 7 8 9

  -- -- -- -- -- -- -- -- -- --

  内容   H a H a !   O K ! \0

  而BSTR则在字串前面还有个LONG值存字串长度格式如下:

  Address 0.. 3 4 5 6 7 8 9 10 11 12 13

  ------ -- -- -- -- -- -- -- -- -- --

  内容    9  H a H a !   O K ! \0

  -----------------------------------------------------------------------------

  所以了字串以ByVal方式来传像不像指到BSTR中第4个位置如此不就和LPSTR 可以相容了吗?我想也正如此以ByVal方式来传String可以取得Win API传回值(就算不是如此至少这麽想比较记得住String要用ByVal方式传)现在又有个问题Window95 API字串使用是ASCII Code但VB是用UnicodeUnicode占两个位元组那麽能和WinAPI字串相?所幸我们可以先不用管它vb本身做了转换即vb传给api时转了传回时又转回 Unicode所以如果我们用是Byte Array来传字串也可以但是要自己去转码

  然而32位元VB 中字串有种格式个是BSTR个是HLSTR如果我们宣告串是非固定长度者就会是BSTR反的则为HLSTR

  DIM BSTR5  AS STRING     劤 BSTR

  DIM HLSTR5 AS STRING(255)  劤 HLSTR

  VB5中WIN32 API呼叫请多多使用BSTR使用HLSTR结果是VB还得做HLSTR-> BSTR转换来呼叫WIN API若有传回STRING而後再做BSTR->HLSTR工作然而使用BSTR来工作时若处理有传回值STRING叁数则还要有额外动作:

  1.先给定字串初值且字串长度要够放传回值

  2.传回後去除传回值中多馀字元

  或

  例如:

  -----------------------------------------------------------------------------

   GetWindowText(
   HWND    hWnd,      // handle of window or control with text
   LPTSTR   lpString,    // address of buffer for text
        nMaxCount    // maximum number of characters to copy
  );


  该 API 取得WINDOW Title Bar文字而传回值是放入lpStringcharacter个数

  VB宣告如下:

  Decl are Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
    (ByVal hwnd As Long, _
     ByVal lpString As String, _
     ByVal cch As Long) As Long


  范例

  *****************************************************************************
Dim CharCnt As Long
Dim lpString As String
Dim tmpstr As String
Dim NullPos As Long
Form1.Caption = "这是个test"
lpString = String(255, 0) '设定初值
CharCnt = GetWindowText(Me.hwnd, lpString, 256) 'CharCnt = 12
tmpstr = Left(lpString, CharCnt) '如此做会有些问题
Debug.Pr Len(tmpstr)  '得12
Label1.Caption = Left(lpString, CharCnt)
Debug.Pr Len(Label1.Caption) '得8
*****************************************************************************


  以范例例子来看设定lpString= String(255,0)是设定255个字元空间给 lpString(加上最後null共256)CharCnt值是12明眼者可看到len("这是个test") 会是8但CharCnt是12 所以直接使用Left来取得子字串会有问题这是UniCode和ANSI String间关系所以了当您看到有些书范例用这种思路方法取子字串是不太完善所以改用范例 2方式比较正确

  范例 2

  *****************************************************************************
Form1.Caption = "这是个test"
lpString = String(255, 0) '设定初值
CharCnt = GetWindowText(Me.hwnd, lpString, 256) 'CharCnt = 12
NullPos = InStr(1, lpString, Chr(0), vbBinaryCompare)
tmpstr = Left(lpString, NullPos - 1)
lable1.Caption = tmpstr
*****************************************************************************


   4、 Null 值传递

  我们再回到求ProductId问题我们已知使用RegOpenKeyEx来取得subkeyHandle值紧接着便是用RegQueryValueEx来取值

  -----------------------------------------------------------------------------
LONG RegQueryValueEx(
   HKEY   hKey,       // handle of key to query
   LPTSTR  lpszValueName,   // address of name of value to query
   LPDWORD lpdwReserved,   // reserved
   LPDWORD lpdwType,     // address of buffer for value type
   LPBYTE  lpbData,      // address of data buffer
   LPDWORD lpcbData      // address of data buffer size
  );
VB宣告(由API检视员中Copy下来者)
Declare Function RegQueryValueEx Lib "advapi32.dll" Alias "RegQueryValueExA" _
   (ByVal hKey As Long, _
   ByVal lpValueName As String, _
   ByVal lpReserved As Long, _
   lpType As Long, _
   lpData As Any, _
   lpcbData As Long) As Long
-----------------------------------------------------------------------------


  仔细看下第 3个叁数WIN API中是LPDWORD可是VB中麽会是用ByVal方式传递呢?原因在於 lpReserved定要传Null进去VB在呼叫时便在 这叁数位置上填0(见范例 3)为何传Null就得这做?我们可以这麽想我们 在程式中下指令告诉VB要以ByVal 方式传0出去而WIN API里它可不管VB是ByVal或ByRefAPI 认定我们传进来就是它需要所以了第 3个叁数在API中认定我们传进个Address而VB传0进去那代表API若去取得它内容便会取得Address 0 内容或许WindowNull值便是指向Address 0呢!另个作法比较直接将VB宣告第 3个叁数宣告由ByVal lpReserved As Long改成 ByVal lpReserved as String而使用时固定传vbNullString 进去也可以这里在个观念那就是VB对Win API宣告纯粹是给VB自己看在API中定义了个指标叁数Api检视员会将的宣告成ByRef方式(字串 除外)但我们可随需要而更动它个原始应为ByRef叁数宣告我们可以将的改为ByVal方式只要我们能取得叁数位址而将这型态为Long位址以ByVal传出去Win API 端根本不知道VB端是用什麽方式传反正只要我们传了Long值进去Win API就会以这个Long值当作是Address来运作

  问题还没有解决RegQueryValueEx第 4个叁数lpType若为REG_SZ(= 1)那代表lpData是Null TerminateString若为REG_DWORD ( = 4)那代表lpData是Long值正是没有办法事先知道lpData真正型态所以VB就使用 ASAny型态它要VB放弃型态检查传什麽值进去都可以但是在这里有些问题如果lpType是REG_DWORD那麽lpData以ByRef方式没有问题但是如果lpType 是REG_SZSTRING是要以ByVal方式来宣告所以会有冲突而解决方式就是改写API检视员Copy进来宣告

  ----------------------------------------------------------------------------
Declare Function RegQueryLong Lib "advapi32.dll" Alias "RegQueryValueExA" _
   (ByVal hKey As Long, _
    ByVal lpValueName As String, _
    ByVal lpReserved As Long, _
    lpType As Long, _
    lpData As Long, _
    lpcbData As Long) As Long
Declare Function RegQueryString Lib "advapi32.dll" Alias "RegQueryValueExA" _
   (ByVal hKey As Long, _
    ByVal lpValueName As String, _
    ByVal lpReserved As Long, _
    lpType As Long, _
    Byval lpData As String, _
    lpcbData As Long) As Long
-----------------------------------------------------------------------------


  使用两个宣告来解决这个问题依区别lpType呼叫区别函式即lpType= REG_DWORD时呼叫RegQueryLong, lpType = REG_SZ时则为RegQueryString这也可以让我们了解为何VB API宣告为什麽要有Alias存在

  范例 3

  *****************************************************************************
Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Long) _
     As Long
Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA"
  (ByVal hKey As Long, ByVal lpSubKey As String, ByVal ulOptions As Long, _
  ByVal samDesired As Long, phkResult As Long) As Long
Declare Function RegQueryString Lib "advapi32.dll" Alias _
  "RegQueryValueExA" (ByVal hKey As Long, _
  ByVal lpValueName As String, ByVal lpReserved As Long, _
  lpType As Long, ByVal lpData As String, lpcbData As Long) As Long
Const REG_EXPAND_SZ = 2
Const HKEY_CLASSES_ROOT = &H80000000
Const READ_CONTROL = &H20000
Const STANDARD_RIGHTS_READ = (READ_CONTROL)
Const KEY_QUERY_VALUE = &H1
Const KEY_ENUMERATE_SUB_KEYS = &H8
Const KEY_NOTIFY = &H10
Const SYNCHRONIZE = &H100000
Const KEY_READ = ((STANDARD_RIGHTS_READ Or _
    KEY_QUERY_VALUE Or KEY_ENUMERATE_SUB_KEYS Or _
    KEY_NOTIFY) And (Not SYNCHRONIZE))
Dim key5 As String, ValueName as String, strBuff as String, ResultStr as String
Dim leng1 As Long, resul As Long, hkey As Long
Dim tp As Long, i As Long
key5 = " SOFTWARE\Microsoft\Windows\CurrentVerson "
resul = RegOpenKeyEx(HKEY_CLASSES_ROOT, key5, 0, KEY_READ, hkey)
  'hkey便是subkey (key5)KeyHandle先取得它才能存取Subkey内ValueName
ValueName= "ProDuctId "
tp = REG_SZ
strBuff = String(255, 0)
leng1 = Len(strBuff) + 1
resul = RegQueryString(hkey, ValueName, 0, tp, strBuff, leng1)
  '注意第 3个叁数传0leng1传回copy 到strBuff字元个数(anci)
leng1 = InStr(1, strBuff, Chr(0), vbBinaryCompare) '重新算个数(UniCode)
ResultStr = Left(StrBuff,leng1-1)  '这便是ProductId
*****************************************************************************


  在这里有另外件事要特别介绍说明范例 3程式中有行leng1=Len(strBuffer)+1这行可省不得很奇怪吧为什麽明明是个传回值定要设定给它个strBuff 大小呢?这是许多WIN API 不会聪明到找strBuffNull Char在哪里所以需要程式传进去而後它再依这个栏位传回填入strBuff 数目

   5、Array叁数传递

  我们知道Win API 阵列传递是传阵列起始位址所以了在VB中唯要注意是起始位置写法以另个取得Window目录所在路径API为例:

  -----------------------------------------------------------------------------

  UINT GetWindowsDirectory(
   LPTSTR   lpBuffer,    // address of buffer for Windows directory
   UINT    uSize      // size of directory buffer
  ); 
            // 若成功则传回目录字元数

  VB宣告(API检视员)

  Declare Function GetWindowsDirectory Lib "kernel32" Alias _

  "GetWindowsDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long) _  As Long

  我们将的更改为

  Declare Function GetWindowsDirectory Lib "kernel32" Alias _ "GetWindowsDirectoryA" ( lpBuffer As Byte, ByVal nSize As Long) As Long

  -----------------------------------------------------------------------------

  范例 4

  *****************************************************************************
Dim n as Long
Dim Buff as Byte
Dim StrA as String
Buff = space(256)
n=GetWindowsDirectory(Buff(0), 256)
Buff = Leftb(Buff, n)
StrA = StrConv(Buff, vbUniCode) 'StrA便是Windows所在目录
*****************************************************************************


  在范例 4中GetWindowsDirectory传入个叁数Buff(0)便是这阵列起始Byte 因VB 宣告成lpBuffer As Byte故传过去是ByRef Buff(0)位址当然了你也可以呼叫成n=GetWindowsDirectory(Buff(1), 256)只是传回值是填在Buff(1) to Buff(n)而Buff(0)则仍为起始Space Character(32)该API传回值是字元个数再加上存於Buff中是Byte Array故使用Leftb去除多出再用StrConv将Byte Array转成Unicode字串比照范例 2作法我们也可以将ByteArray 改成以String方式来做 2者可做比较谁比较好或比较顺畅那见人见智不过可以肯定如果传值是Binary那麽使用Byte Array来做才对因用String来传会经过转换成UniCode步骤这中间会发生什麽事没人知道

   6、CallBack Function作法

  VB使用者通常对於这个名词有着多多少少疑惑或称的为"哭爸"Function而VB5使用手册使用Window Procedure来介绍说明除非对Window 系统有些了解否则可能令人更不知所云;我使用另个例子来介绍说明那便是KeyBoard Hook什麽是KeyBoardHook 呢简言的便是按键盘时便会自动执行某段Function功能就好比Dos时代拦截中断向量让我们先看下设定Hook宣告吧

  -----------------------------------------------------------------------------
HHOOK SetWindowsHookEx(
        idHook,     // type of hook to
   HOOKPROC  hkprc,     // address of hook procedure
   HINSTANCE  hMod,      // handle of application instance
   DWORD    dwThreadID   // identity of thread to hook for
  );
Declare Function SetWindowsHookEx Lib "user32" Alias SetWindowsHookExA" _
     (ByVal idHook As Long, _
ByVal lpfn As Long, _
ByVal hmod As Long, _
ByVal dwThreadId As Long) As Long
-----------------------------------------------------------------------------


Tags:  windowsapi函数 windowsmobileapi windowsapi编程 windowsapi

延伸阅读

最新评论

发表评论