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返回后,lcBuffer和lnBytesRead包含了一个新的值。
下面是类别(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)
传递NUL的LPCTSTR参数
实例:
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正被用num2dword和num2word函数组装:
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, lpstrCustomFilter,lpstrFile和一些其他不是字符串而是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模块。