VFP向Win32函数传递参数

VFP向Win32函数传递参数


2003年10月15日 News2News
摘要:学习如何在VFP3到8里向Win32函数传递参数,包括值传递和引用传递,生成和传递结构,和更多的内容。

目录
值传递
引用传递
用API函数分配内存
传递NULL的LPCTSTR参数
用不同的接口调用Win32函数
生成结构
使用结构的指针
回调(CallBack)函数作为输入参数

值传递     

传递变量或值

DECLARE INTEGER FindWindow IN user32;
        STRING lpClassName, STRING lpWindowName
LOCAL hWnd, lcWindowName
lcWindowName = "Calculator"
hWnd = FindWindow (.Null., lcWindowName)  && a variable
hWnd = FindWindow (.Null., "Calculator")  && a direct value

很多Api函数要求以“/0”结尾的字符串作为参数,你不需要真的用chr(0)结束字符串参数,但如果你想这样做,也不会有问题。

引用传递    

如果下面情况以引用传递参数:

a) Win32函数要修改参数的值
b) 用作Win32结构缓冲的Foxpro字符串

要知道那个参数将要用引用传递是不难的。如果你还是不肯定,就在MSDN的网站查一下那个函数。通常在每个参数描述前包含了[in],[out]或者 [in/out]。

类别(a)的例子是ReadFile函数,它的声明如下:

DECLARE INTEGER ReadFile IN kernel32;
        INTEGER   hFile,;
        STRING  @ lpBuffer,;
        INTEGER   nNumberOfBytesToRead,;
        INTEGER @ lpNumberOfBytesRead,;
        INTEGER   lpOverlapped

注意:lpNumberOfBytesRead是声明为一个引用的参数,因为ReadFile将用从读取文件的实际字节数赋给这个变量。

在调用这个函数前必须用一个数值初始化lpNumberOfBytesRead参数。把没有初始化的变量的引用传递到外部函数是非常普遍的错误。

LOCAL lnBytesRead, lcBuffer
lcBuffer = SPACE(250)
lnBytesRead = 0
= ReadFile(hFile, @lcBuffer, Len(lcBuffer), @lnBytesRead, 0)

On return from the ReadFile, if no error occured, lcBuffer and lnBytesRead contain new values.
如果没有错误发生,ReadFile返回后,lcBufferlnBytesRead包含了一个新的值。

下面是类别(b)的例子。GetClipCursor函数重新得到限制鼠标移动范围的矩形区域的屏幕坐标。

BOOL GetClipCursor(
   LPRECT lpRect  // address of structure for rectangle
);

这个函数要求RECT结构已分配内存和用矩形的坐标填充它。

typedef struct _RECT { 
   LONG left; 
   LONG top; 
   LONG right; 
   LONG bottom; 
} RECT, *PRECT;

你能看到这个结构包含了4个LONG(4字节)的值,所以它总的字节数是16。

最容易的方法是生成一个16个字符长度的VFP字符串,并用它作为这个结构的缓冲。这种正确传递RECT结构的方法的定义了这个函数的如何被声明:

DECLARE INTEGER GetClipCursor IN user32 STRING @lpRect
LOCAL lcBuffer
lcBuffer = Repli(Chr(0), 16)
= GetClipCursor (@lcBuffer)

你甚至可以用SPACE(16)代替REPLICATE。可是有些其他Win32函数要求用填满零的真空串而不是空格。

API函数分配内存

内存也可以用API函数GlobalAlloc,LocalAlloc,HeapAlloc分配的。

它们之间的重大差别是相应释放分配了的内存的释放函数:GlobalAlloc, LocalAlloc, HeapAlloc和HeapFree。很多情况里当你不要需要它时你必须释放缓冲去防止内存漏洞。

有些函数只要求全局的内存对象。例如,一个缓冲在内存的分配的地址传递到SetClipboardData函数必须是一个全局的地址。另外,微软推荐使用堆(heap)函数因为是较快速的函数和可得的较多的特征。

下面是另外一个例子。注意现在用不同的方法声明GetClipCursor函数。这个参数现在是作为值传递,这是一个分配了的内存块的地址,这个地址将不被子函数修改。GetClipCursor函数将在这个地址写一些数据来代替,你的任务提供了充够数量的内存。

#DEFINE GMEM_FIXED  0  && the memory block is not movable
DECLARE INTEGER GetClipCursor IN user32 LONG lpRect
DECLARE INTEGER GlobalAlloc IN kernel32 INTEGER wFlags, INTEGER dwBytes
DECLARE INTEGER GlobalFree IN kernel32 INTEGER hMem
LOCAL lnBuffer
lnBuffer = GlobalAlloc(GMEM_FIXED, 16)
= GetClipCursor(lnBuffer)
. . .
= GlobalFree(lnBuffer)

传递NULLPCTSTR参数

实例:

HDC CreateDC(
   LPCTSTR lpszDriver,        // driver name
   LPCTSTR lpszDevice,        // device name
   LPCTSTR lpszOutput,        // not used; should be NULL
   CONST DEVMODE* lpInitData  // optional printer data
);

两种传递NULL值的方法:

a)声明参数为STRING并传递空串或Chr(0)
b)声明参数为INTEGER并传递零值

用不同的接口调用Win32函数

RtlMoveMemory (或者CopyMemory)函数是用来从一个内存中的地址复制数据到VFP字符串和在相反方向的操作。

VOID CopyMemory( 
   PVOID Destination,   // copy destination 
   CONST VOID* Source,  // memory block 
   SIZE_T Length        // size of memory block 
);

内存地址总是一个数值,它是用作一个源(MemToString)或一个目标(StringToMem)。

VFP字符串,当是一个源时,可以值传递或引用传递。当用作一个目标时,它必须严格被引用传递。

这个函数至少有两种声明:

DECLARE RtlMoveMemory IN kernel32 As StringToMem;
        INTEGER, STRING @, INTEGER
DECLARE RtlMoveMemory IN kernel32 As MemToString;
        STRING @, INTEGER, INTEGER

注意:一个Win32函数任何新的声明丢弃前面声明的一个。那意味你必须在使用一个不同的接口之前声明如此的 Win32函数。

修正:VFP8支持每个外部函数多于一种声明。所以例如,CopyMemor可以一次声明用来从内存地址到FoxPro字符串复制数据,同时用不同的别名和参数用秋从FoxPro字符串到内存地址复制数据。

组装结构 

组装结构不是个普通的工作。没有直接的方法。我只能梦想一些类似TYPE…EDNTYPE。

首先生成一个空的字符串和继续向它追加子串,陆续地,直到它到达或者超过结构的必需大小。下面有一个例子——在调用PrintDlg里的作为参数使用的PRINTDLG结构:

typedef struct tagPD { 
   DWORD           lStructSize; 
   HWND            hwndOwner; 
   HGLOBAL         hDevMode; 
   HGLOBAL         hDevNames; 
   HDC             hDC; 
   DWORD           Flags; 
   WORD            nFromPage; 
   WORD            nToPage; 
   WORD            nMinPage; 
   WORD            nMaxPage; 
   WORD            nCopies; 
   HINSTANCE       hInstance; 
   LPARAM          lCustData; 
   LPPRINTHOOKPROC lpfnPrintHook; 
   LPSETUPHOOKPROC lpfnSetupHook; 
   LPCTSTR         lpPrintTemplateName; 
   LPCTSTR         lpSetupTemplateName; 
   HGLOBAL         hPrintTemplate; 
   HGLOBAL         hSetupTemplate; 
} PRINTDLG, *LPPRINTDLG; 

传递一个66个空格的字符串是不足够的。

LOCAL lcBuffer
lcBuffer = Repli(Chr(0), 66) && will not work将不工作

在这个结构里有几个字段必须被组装的,开始的lStructSize它必须设为66——结构的大小。

现在,让我们讨论DWORD,DWORD(同样可用于HWND,HDC,HINSTANCE等等)在内存占用4个连续的字节,用低字节存贮在左边。LStructSize是如何装配的(再次提醒它最低的字节是来自开始):

lcBuffer = Chr(66) + Chr(0) + Chr(0) + Chr(0) &&创建DWORD缓冲 

接下来两个函数可以用来转换数值到4字节和2字节的字符串缓冲:

FUNCTION  num2dword (lnValue)
#DEFINE m0       256
#DEFINE m1     65536
#DEFINE m2  16777216
        LOCAL b0, b1, b2, b3
        b3 = Int(lnValue/m2)
        b2 = Int((lnValue - b3 * m2)/m1)
        b1 = Int((lnValue - b3*m2 - b2*m1)/m0)
        b0 = Mod(lnValue, m0)
RETURN Chr(b0)+Chr(b1)+Chr(b2)+Chr(b3)
FUNCTION  num2word (lnValue)
RETURN Chr(Mod(m.lnValue,256)) + Chr(Int(m.lnValue/256)) 

PRINTDLG正被用num2dwordnum2word函数组装:

lcBuffer = num2dword(66) +;
               num2dword(0) +;
               num2dword(0) +;
               num2dword(0) +;
               num2dword(0) +;
        num2dword(PD_RETURNDC) +;
        num2word(65535) +;
        num2word(65535) +;
        num2word(1) +;
        num2word(65535) +;
        num2word(1) +;
               num2dword(0) +;
               num2dword(0) +;
               num2dword(0) +;
               num2dword(0) +;
               num2dword(0) +;
               num2dword(0) +;
               num2dword(0) +;
               num2dword(0) +;
               num2dword(0)

乃至更精简的版本:

lcBuffer = num2dword(66) +;
               Repli(Chr(0), 16) +;
        num2dword(PD_RETURNDC) +;
        num2word(65535) +;
        num2word(65535) +;
        num2word(1) +;
        num2word(65535) +;
        num2word(1) +;
               Repli(Chr(0), 36)

使用用指针的结构 

有时一个结构成员是一个分配了的内存块地址。一个例子是OPENFILENAME结构:

typedef struct tagOFN { 
   DWORD         lStructSize; 
   HWND          hwndOwner; 
   HINSTANCE     hInstance; 
   LPCTSTR       lpstrFilter; 
   LPTSTR        lpstrCustomFilter; 
   DWORD         nMaxCustFilter; 
   DWORD         nFilterIndex; 
   LPTSTR        lpstrFile; 
   DWORD         nMaxFile; 
   LPTSTR        lpstrFileTitle; 
   DWORD         nMaxFileTitle; 
   LPCTSTR       lpstrInitialDir; 
   LPCTSTR       lpstrTitle; 
   DWORD         Flags; 
   WORD          nFileOffset; 
   WORD          nFileExtension; 
   LPCTSTR       lpstrDefExt; 
   LPARAM        lCustData; 
   LPOFNHOOKPROC lpfnHook; 
   LPCTSTR       lpTemplateName; 
#if (_WIN32_WINNT >= 0x0500)
   void *        pvReserved;
   DWORD         dwReserved;
   DWORD         FlagsEx;
#endif // (_WIN32_WINNT >= 0x0500)
} OPENFILENAME, *LPOPENFILENAME; 

LpstrFilter lpstrCustomFilterlpstrFile和一些其他不是字符串而是4字节的字符串指针。先组成这个结构的几个内存缓冲必须分配。然后适当的OPENFILENAME成员“填满”这些地址。

LOCAL lcFilter, lnFilter
lcFilter = 'Text Files' + Chr(0) + '*.txt;*.bak' + Chr(0)+Chr(0)
DECLARE INTEGER GlobalAlloc IN kernel32;
        INTEGER wFlags, INTEGER dwBytes
DECLARE RtlMoveMemory IN kernel32 As Str2Mem;
        INTEGER, STRING @, INTEGER 
* allocating memory block
lnFilter = GlobalAlloc(GMEM_FIXED, Len(lcFilter))
* copying string data to the memory block
= Str2Mem(lnFilter, @lcFilter, Len(lcFilter))

现在lnFilter包含适当的内存地址。记得在外部函数返回后释放它(GlobalFree)。

回调(CallBack)函数作为输入参数  

很多Win32函数要求一个回收函数作为一个输入参数。CopyFileEx函数是一个例子。

BOOL CopyFileEx(
   LPCTSTR lpExistingFileName,           // name of existing file 存在的文件名
   LPCTSTR lpNewFileName,                // name of new file 新的文件名
   LPPROGRESS_ROUTINE lpProgressRoutine, // callback function回调函数
   LPVOID lpData,                        // callback parameter回调参数
   LPBOOL pbCancel,                      // cancel status 取消状态
   DWORD dwCopyFlags                     // copy options 复制选项
);

你可能想在复制文件时用一个回调函数显示一个进度条。

在类似C的语言里,Delphi和VB这部分是相当容易的:你只创建一个带有一个预先确定接口和获得一个引用给它的函数。在Visual Foxpro里是不同的:没有资料说明做这个的方法,换句话说,当调用CopyFileEx例程时你不能传递一个FoxPro函数的引用,至少你要创单独的DLL或FLL模块。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值