VFP向Win32函数传递参数

来源:互联网 发布:服装搭配网站知乎 编辑:程序博客网 时间:2024/05/16 08:18

VFP向Win32函数传递参数


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

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

值传递     

传递变量或值

DECLARE INTEGER FindWindow IN user32;        STRING lpClassName, STRING lpWindowNameLOCAL hWnd, lcWindowNamelcWindowName = "Calculator"hWnd = FindWindow (.Null., lcWindowName)  && a variablehWnd = 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, lcBufferlcBuffer = 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 @lpRectLOCAL lcBufferlcBuffer = 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 movableDECLARE INTEGER GetClipCursor IN user32 LONG lpRectDECLARE INTEGER GlobalAlloc IN kernel32 INTEGER wFlags, INTEGER dwBytesDECLARE INTEGER GlobalFree IN kernel32 INTEGER hMemLOCAL lnBufferlnBuffer = 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 @, INTEGERDECLARE 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 lcBufferlcBuffer = 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, lnFilterlcFilter = 'Text Files' + Chr(0) + '*.txt;*.bak' + Chr(0)+Chr(0)DECLARE INTEGER GlobalAlloc IN kernel32;        INTEGER wFlags, INTEGER dwBytesDECLARE RtlMoveMemory IN kernel32 As Str2Mem;        INTEGER, STRING @, INTEGER * allocating memory blocklnFilter = 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模块。

原创粉丝点击