C#调用API函数详细说明

C#调用API函数详细说明
C#调用API函数详细说明

C#:[DllImport("kernel32.dll")]是什么意思??

这叫引入kernel32.dll这个动态连接库。

这个动态连接库里面包含了很多WindowsAPI函数,如果你想使用这面的函数,就需要这么引入。举个例子:

[DllImport("kernel32.dll")]

private static extern void 函数名(参数,[参数]);

函数名就是一个属于kernel32.dll里的一个函数。完了你就可以用那个函数了。

kernel32.dll调用kernel32.dll这个DLL里面的API接口!

系统API

例如

[DllImport("user32.dll")]//--引入API

public static extern ReturnT ype FunctionName(type arg1,type arg2,...);//--声明方法

调用该方法是和调用普通方法没区别

DLL Import 属性

现在是更深入地进行探讨的时候了。在对托管代码进行P/Invoke 调用时,DllImportAttribute 类型扮演着重要的角色。DllImportAttribute 的主要作用是给CLR 指示哪个DLL 导出您想要调用的函数。相关DLL 的名称被作为一个构造函数参数传递给DllImportAttribute。

如果您无法肯定哪个DLL 定义了您要使用的Windows API 函数,Platform SDK 文档将为您提供最好的帮助资源。在Windows API 函数主题文字临近结尾的位置,SDK 文档指定了 C 应用程序要使用该函数必须链接的.lib 文件。在几乎所有的情况下,该.lib 文件具有与定义该函数的系统DLL 文件相同的名称。例如,如果该函数需要 C 应用程序链接到Kernel32.lib,则该函数就定义在Kernel32.dll 中。您可以在MessageBeep 中找到有关MessageBeep 的Platform SDK 文档主题。在该主题结尾处,您会注意到它指出库文件是User32.lib;这表明MessageBeep 是从User32.dll 中导出的。

可选的DllImportAttribute 属性

除了指出宿主DLL 外,DllImportAttribute 还包含了一些可选属性,其中四个特别有趣:EntryPoint、CharSet、SetLastError 和CallingConvention。

EntryPoint 在不希望外部托管方法具有与DLL 导出相同的名称的情况下,可以设置该属性来指示导出的DLL 函数的入口点名称。当您定义两个调用相同非托管函数的外部方法时,这特别有用。另外,在Windows 中还可以通过它们的序号值绑定到导出的DLL 函数。如果您需要这样做,则诸如“#1”或“#129”的EntryPoint 值指示DLL 中非托管函数的序号值而不是函数名。

CharSet 对于字符集,并非所有版本的Windows 都是同样创建的。Windows 9x 系列产品缺少重要的Unicode 支持,而Windows NT 和Windows CE 系列则一开始就使用Unicode。

在这些操作系统上运行的CLR 将Unicode 用于String 和Char 数据的内部表示。但也不必担心—当调用Windows 9x API 函数时,CLR 会自动进行必要的转换,将其从Unicode转换为ANSI。

如果DLL 函数不以任何方式处理文本,则可以忽略DllImportAttribute 的CharSet 属性。然而,当Char 或String 数据是等式的一部分时,应该将CharSet 属性设置为CharSet.Auto。这样可以使CLR 根据宿主OS 使用适当的字符集。如果没有显式地设置CharSet 属性,则其默认值为CharSet.Ansi。这个默认值是有缺点的,因为对于在Windows 2000、Windows X P 和Windows NT? 上进行的interop 调用,它会消极地影响文本参数封送处理的性能。

应该显式地选择CharSet.Ansi 或CharSet.Unicode 的CharSet 值而不是使用CharSet.Auto 的唯一情况是:您显式地指定了一个导出函数,而该函数特定于这两种Win32 OS 中的某一种。ReadDirectoryChangesW API 函数就是这样的一个例子,它只存在于基于Windows NT 的操作系统中,并且只支持Unicode;在这种情况下,您应该显式地使用CharSet.Unicode。

有时,Windows API 是否有字符集关系并不明显。一种决不会有错的确认方法是在Platform SDK 中检查该函数的 C 语言头文件。(如果您无法肯定要看哪个头文件,则可以查看Platform SDK 文档中列出的每个API 函数的头文件。)如果您发现该API 函数确实定义为一个映射到以 A 或W 结尾的函数名的宏,则字符集与您尝试调用的函数有关系。Windows API 函数的一个例子是在WinUser.h 中声明的GetMessage API,您也许会惊讶地发现它有 A 和W 两种版本。

SetLastError 错误处理非常重要,但在编程时经常被遗忘。当您进行P/Invoke 调用时,也会面临其他的挑战—处理托管代码中Windows API 错误处理和异常之间的区别。我可以给您一点建议。

如果您正在使用P/Invoke 调用Windows API 函数,而对于该函数,您使用GetLastError 来查找扩展的错误信息,则应该在外部方法的DllImportAttribute 中将SetLastError 属性设置为true。这适用于大多数外部方法。

这会导致CLR 在每次调用外部方法之后缓存由API 函数设置的错误。然后,在包装方法中,可以通过调用类库的System.Runtime.InteropServices.Marshal 类型中定义的Marshal.GetLastWin32Error 方法来获取缓存的错误值。我的建议是检查这些期望来自API 函数的错误值,并为这些值引发一个可感知的异常。对于其他所有失败情况(包括根本就没意料到的失败情况),则引发在https://www.360docs.net/doc/203239976.html,ponentModel 命名空间中定义的Win32Exception,并将Marshal.GetLastWin32Error 返回的值传递给它。如果您回头看一下图 1 中的代码,您会看到我在extern MessageBeep 方法的公共包装中就采用了这种方法。

CallingConvention 我将在此介绍的最后也可能是最不重要的一个DllImportAttribute 属性是CallingConvention。通过此属性,可以给CLR 指示应该将哪种函数调用约定用于堆栈中的参数。CallingConvention.Winapi 的默认值是最好的选择,它在大多数情况下都可行。然而,如果该调用不起作用,则可以检查Platform SDK 中的声明头文件,看看您调用的API 函数是否是一个不符合调用约定标准的异常API。

通常,本机函数(例如Windows API 函数或C- 运行时DLL 函数)的调用约定描述了如何将参数推入线程堆栈或从线程堆栈中清除。大多数Windows API 函数都是首先将函数的最后一个参数推入堆栈,然后由被调用的函数负责清理该堆栈。相反,许多C-运行时DLL 函数都被定义为按照方法参数在方法签名中出现的顺序将其推入堆栈,将堆栈清理工作交给调用者。

幸运的是,要让P/Invoke 调用工作只需要让外围设备理解调用约定即可。通常,从默认值CallingConvention.Winapi 开始是最好的选择。然后,在 C 运行时DLL 函数和少数函数中,可能需要将约定更改为CallingConvention.Cdecl。

C# API 大全

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

作者:不详加入时间:4/17/2008 9:41:49 AM

C# API

C:\ProgramFiles\MicrosoftVisual Studio .NET\ FrameworkSDK\Samples\ T echnologies\ Interop\PlatformInvoke\ WinAPIs\CS目录下有大量的调用API的例子。

一、调用格式

using System.Runtime.InteropServices; //引用此名称空间,简化后面的代码

//使用DllImportAttribute特性来引入api函数,注意声明的是空方法,即方法体为空。[DllImport("user32.dll")]

public static extern ReturnT ype FunctionName(type arg1,type arg2,...);

//调用时与调用其他方法并无区别

可以使用字段进一步说明特性,用逗号隔开,如:

[ DllImport( "kernel32", EntryPoint="GetVersionEx" )]

DllImportAttribute特性的公共字段如下:

1、CallingConvention 指示向非托管实现传递方法参数时所用的CallingConvention 值。CallingConvention.Cdecl : 调用方清理堆栈。它使您能够调用具有varargs 的函数。CallingConvention.StdCall : 被调用方清理堆栈。它是从托管代码调用非托管函数的默认约定。

2、CharSet 控制调用函数的名称版本及指示如何向方法封送String 参数。

此字段被设置为CharSet 值之一。如果CharSet 字段设置为Unicode,则所有字符串参数在传递到非托管实现之前都转换成Unicode 字符。这还导致向DLL EntryPoint 的名称中追加字母“W”。如果此字段设置为Ansi,则字符串将转换成ANSI 字符串,同时向DLL EntryPoint 的名称中追加字母“A”。大多数Win32 API 使用这种追加“W”或“A”的约定。如果CharSet 设置为Auto,则这种转换就是与平台有关的(在Windows NT 上为Unicode,在Windows 98 上为Ansi)。CharSet 的默认值为Ansi。CharSet 字段也用于确定将从指定的DLL 导入哪个版本的函数。CharSet.Ansi 和CharSet.Unicode 的名称匹配规则大不相同。对于Ansi 来说,如果将EntryPoint 设置为“MyMethod”且它存在的话,则返回“MyMethod”。如果DLL 中没有“MyMethod”,但存在“MyMethodA”,则返回“MyMethodA”。对于Unicode 来说则正好相反。如果将EntryPoint 设置为“MyMethod”且它存在的话,则返回“MyMethodW”。如果DLL 中不存在“MyMethodW”,但存在“MyMethod”,则返回“MyMethod”。如果使用的是Auto,则匹配规则与

平台有关(在Windows NT 上为Unicode,在Windows 98 上为Ansi)。如果ExactSpelling 设置为true,则只有当DLL 中存在“MyMethod”时才返回“MyMethod”。

3、EntryPoint 指示要调用的DLL 入口点的名称或序号。

如果你的方法名不想与api函数同名的话,一定要指定此参数,例如:

[DllImport("user32.dll",CharSet="CharSet.Auto",EntryPoint="MessageBox")]

public static extern int MsgBox(IntPtr hWnd,string txt,string caption, int type);

4、ExactSpelling 指示是否应修改非托管DLL 中的入口点的名称,以与CharSet 字段中指定的CharSet 值相对应。如果为true,则当DllImportAttribute.CharSet 字段设置为CharSet 的Ansi 值时,向方法名称中追加字母A,当DllImportAttribute.CharSet 字段设置为CharSet 的Unicode 值时,向方法的名称中追加字母W。此字段的默认值是false。

5、PreserveSig 指示托管方法签名不应转换成返回HRESULT、并且可能有一个对应于返回值的附加[out, retval] 参数的非托管签名。

6、SetLastError 指示被调用方在从属性化方法返回之前将调用Win32 API SetLastError。true 指示调用方将调用SetLastError,默认为false。运行时封送拆收器将调用GetLastError 并缓存返回的值,以防其被其他API 调用重写。用户可通过调用GetLastWin32Error 来检索错误代码。

二、参数类型:

1、数值型直接用对应的就可。(DWORD -> int , WORD -> Int16)

2、API中字符串指针类型-> .net中string

3、API中句柄(dWord) -> .net中IntPtr

4、API中结构-> .net中结构或者类。注意这种情况下,要先用StructLayout特性限定声明结构或类

公共语言运行库利用StructLayoutAttribute控制类或结构的数据字段在托管内存中的物理布局,即类或结构需要按某种方式排列。如果要将类传递给需要指定布局的非托管代码,则显式控制类布局是重要的。它的构造函数中用LayoutKind值初始化StructLayoutAttribute 类的新实例。LayoutKind.Sequential 用于强制将成员按其出现的顺序进行顺序布局。

LayoutKind.Explicit 用于控制每个数据成员的精确位置。利用Explicit,每个成员必须使用FieldOffsetAttribute 指示此字段在类型中的位置。如:

[StructLayout(LayoutKind.Explicit, Size=16, CharSet=CharSet.Ansi)]

public class MySystemTime

{

[FieldOffset(0)]public ushort wYear;

[FieldOffset(2)]public ushort wMonth;

[FieldOffset(4)]public ushort wDayOfWeek;

[FieldOffset(6)]public ushort wDay;

[FieldOffset(8)]public ushort wHour;

[FieldOffset(10)]public ushort wMinute;

[FieldOffset(12)]public ushort wSecond;

[FieldOffset(14)]public ushort wMilliseconds;

}

下面是针对API中OSVERSIONINFO结构,在.net中定义对应类或结构的例子:/**********************************************

* API中定义原结构声明

* OSVERSIONINFOA STRUCT

* dwOSVersionInfoSize DWORD ?

* dwMajorVersion DWORD ?

* dwMinorVersion DWORD ?

* dwBuildNumber DWORD ?

* dwPlatformId DWORD ?

* szCSDVersion BYTE 128 dup (?)

* OSVERSIONINFOA ENDS

*

* OSVERSIONINFO equ

*********************************************/

//.net中声明为类

[ StructLayout( LayoutKind.Sequential )]

public class OSVersionInfo

{

public int OSVersionInfoSize;

public int majorVersion;

public int minorVersion;

public int buildNumber;

public int platformId;

[ MarshalAs( UnmanagedT ype.ByValTStr, SizeConst=128 )]

public String versionString;

}

//或者

//.net中声明为结构

[ StructLayout( LayoutKind.Sequential )]

public struct OSVersionInfo2

{

public int OSVersionInfoSize;

public int majorVersion;

public int minorVersion;

public int buildNumber;

public int platformId;

[ MarshalAs( UnmanagedT ype.ByValTStr, SizeConst=128 )]

public String versionString;

}

此例中用到MashalAs特性,它用于描述字段、方法或参数的封送处理格式。用它作为参数前缀并指定目标需要的数据类型。例如,以下代码将两个参数作为数据类型长指针封送给Windows API 函数的字符串(LPStr):

[MarshalAs(UnmanagedT ype.LPStr)]

String existingfile;

[MarshalAs(UnmanagedT ype.LPStr)]

String newfile;

注意结构作为参数时候,一般前面要加上ref修饰符,否则会出现错误:对象的引用没有指定对象的实例。

[ DllImport( "kernel32", EntryPoint="GetVersionEx" )]

public static extern bool GetVersionEx2( ref OSVersionInfo2 osvi );

三、如何保证使用托管对象的平台调用成功?

如果在调用平台invoke 后的任何位置都未引用托管对象,则垃圾回收器可能将完成该托管对象。这将释放资源并使句柄无效,从而导致平台invoke 调用失败。用HandleRef 包装句柄可保证在平台invoke 调用完成前,不对托管对象进行垃圾回收。

例如下面:

FileStream fs = new FileStream( "a.txt", FileMode.Open );

StringBuilder buffer = new StringBuilder( 5 );

int read = 0;

ReadFile(fs.Handle, buffer, 5, out read, 0 ); //调用Win API中的ReadFile函数

由于fs是托管对象,所以有可能在平台调用还未完成时候被垃圾回收站回收。将文件流的句柄用HandleRef包装后,就能避免被垃圾站回收:

[ DllImport( "Kernel32.dll" )]

public static extern bool ReadFile(

HandleRef hndRef,

StringBuilder buffer,

int numberOfBytesToRead,

out int numberOfBytesRead,

ref Overlapped flag );

......

......

FileStream fs = new FileStream( "HandleRef.txt", FileMode.Open );

HandleRef hr = new HandleRef( fs, fs.Handle );

StringBuilder buffer = new StringBuilder( 5 );

int read = 0;

// platform invoke will hold reference to HandleRef until call ends

ReadFile( hr, buffer, 5, out read, 0 );

我在自己最近的编程中注意到一个趋势,正是这个趋势才引出本月的专栏主题。最近,我在基于Microsoft? .NET Framework 的应用程序中完成了大量的Win32? Interop。我并不是要说我的应用程序充满了自定义的interop 代码,但有时我会在.NET Framework 类库中碰到一些次要但又繁絮、不充分的内容,通过调用该Windows? API,可以快速减少这样的麻烦。

因此我认为,.NET Framework 1.0 或 1.1 版类库中存在任何Windows 所没有的功能限制都不足为怪。毕竟,32 位的Windows(不管何种版本)是一个成熟的操作系统,为广大客户服务了十多年。相比之下,.NET Framework 却是一个新事物。

随着越来越多的开发人员将生产应用程序转到托管代码,开发人员更频繁地研究底层操作系统以图找出一些关键功能显得很自然—至少目前是如此。

值得庆幸的是,公共语言运行库(CLR) 的interop 功能(称为平台调用(P/Invoke))非常完善。在本专栏中,我将重点介绍如何实际使用P/Invoke 来调用Windows API 函数。当指CLR 的COM Interop 功能时,P/Invoke 当作名词使用;当指该功能的使用时,则将其当作动词使用。我并不打算直接介绍COM Interop,因为它比P/Invoke 具有更好的可访问性,却更加复杂,这有点自相矛盾,这使得将COM Interop 作为专栏主题来讨论不太简明扼要。

走进P/Invoke

首先从考察一个简单的P/Invoke 示例开始。让我们看一看如何调用Win32 MessageBeep 函数,它的非托管声明如以下代码所示:

BOOL MessageBeep(

UINT uT ype // beep type

);

为了调用MessageBeep,您需要在C# 中将以下代码添加到一个类或结构定义中:

[DllImport("User32.dll")]

static extern Boolean MessageBeep(UInt32 beepT ype);

令人惊讶的是,只需要这段代码就可以使托管代码调用非托管的MessageBeep API。它不是一个方法调用,而是一个外部方法定义。(另外,它接近于一个来自 C 而C# 允许的直接端口,因此以它为起点来介绍一些概念是有帮助的。)来自托管代码的可能调用如下所示:

MessageBeep(0);

请注意,现在MessageBeep 方法被声明为static。这是P/Invoke 方法所要求的,因为在该Windows API 中没有一致的实例概念。接下来,还要注意该方法被标记为extern。这是提示编译器该方法是通过一个从DLL 导出的函数实现的,因此不需要提供方法体。

说到缺少方法体,您是否注意到MessageBeep 声明并没有包含一个方法体?与大多数算法由中间语言(IL) 指令组成的托管方法不同,P/Invoke 方法只是元数据,实时(JIT) 编译器在运行时通过它将托管代码与非托管的DLL 函数连接起来。执行这种到非托管世界的连接所需的一个重要信息就是导出非托管方法的DLL 的名称。这一信息是由MessageBeep 方法声明之前的DllImport 自定义属性提供的。在本例中,可以看到,MessageBeep 非托管API 是由Windows 中的User32.dll 导出的。

到现在为止,关于调用MessageBeep 就剩两个话题没有介绍,请回顾一下,调用的代码与以下所示代码片段非常相似:

[DllImport("User32.dll")]

static extern Boolean MessageBeep(UInt32 beepT ype);

最后这两个话题是与数据封送处理(data marshaling) 和从托管代码到非托管函数的实际方法调用有关的话题。调用非托管MessageBeep 函数可以由找到作用域内的extern MessageBeep 声明的任何托管代码执行。该调用类似于任何其他对静态方法的调用。它与其他任何托管方法调用的共同之处在于带来了数据封送处理的需要。

C# 的规则之一是它的调用语法只能访问CLR 数据类型,例如System.UInt32 和System.Boolean。C# 显然不识别Windows API 中使用的基于 C 的数据类型(例如UINT 和BOOL),这些类型只是 C 语言类型的类型定义而已。所以当Windows API 函数MessageBeep 按以下方式编写时

BOOL MessageBeep( UINT uT ype )

外部方法就必须使用CLR 类型来定义,如您在前面的代码片段中所看到的。需要使用与基础API 函数类型不同但与之兼容的CLR 类型是P/Invoke 较难使用的一个方面。因此,在本专栏的后面我将用完整的章节来介绍数据封送处理。

样式

在C# 中对Windows API 进行P/Invoke 调用是很简单的。但如果类库拒绝使您的应用程序发出嘟声,应该想方设法调用Windows 使它进行这项工作,是吗?

是的。但是与选择的方法有关,而且关系甚大!通常,如果类库提供某种途径来实现您的意图,则最好使用API 而不要直接调用非托管代码,因为CLR 类型和Win32 之间在样式上有很大的不同。我可以将关于这个问题的建议归结为一句话。当您进行P/Invoke 时,不要使应用程序逻辑直接属于任何外部方法或其中的构件。如果您遵循这个小规则,从长远看经常会省去许多的麻烦。

图 1 中的代码显示了我所讨论的MessageBeep 外部方法的最少附加代码。图 1 中并没有任何显著的变化,而只是对无包装的外部方法进行一些普通的改进,这可以使工作更加轻松一些。从顶部开始,您会注意到一个名为Sound 的完整类型,它专用于MessageBeep。如果我需要使用Windows API 函数PlaySound 来添加对播放波形的支持,则可以重用Sound 类型。然

而,我不会因公开单个公共静态方法的类型而生气。毕竟这只是应用程序代码而已。还应该注意到,Sound 是密封的,并定义了一个空的私有构造函数。这些只是一些细节,目的是使用户不会错误地从Sound 派生类或者创建它的实例。

图 1 中的代码的下一个特征是,P/Invoke 出现位置的实际外部方法是Sound 的私有方法。这个方法只是由公共MessageBeep 方法间接公开,后者接受BeepT ypes 类型的参数。这个间接的额外层是一个很关键的细节,它提供了以下好处。首先,应该在类库中引入一个未来的beep 托管方法,可以重复地通过公共MessageBeep 方法来使用托管API,而不必更改应用程序中的其余代码。

该包装方法的第二个好处是:当您进行P/Invoke 调用时,您放弃了免受访问冲突和其他低级破坏的权利,这通常是由CLR 提供的。缓冲方法可以保护您的应用程序的其余部分免受访问冲突及类似问题的影响(即使它不做任何事而只是传递参数)。该缓冲方法将由P/Invoke 调用引入的任何潜在的错误本地化。

将私有外部方法隐藏在公共包装后面的第三同时也是最后的一个好处是,提供了向该方法添加一些最小的CLR 样式的机会。例如,在图 1 中,我将Windows API 函数返回的Boolean 失败转换成更像CLR 的异常。我还定义了一个名为BeepT ypes 的枚举类型,它的成员对应于同该Windows API 一起使用的定义值。由于C# 不支持定义,因此可以使用托管枚举类型来避免幻数向整个应用程序代码扩散。

包装方法的最后一个好处对于简单的Windows API 函数(如MessageBeep)诚然是微不足道的。但是当您开始调用更复杂的非托管函数时,您会发现,手动将Windows API 样式转换成对CLR 更加友好的方法所带来的好处会越来越多。越是打算在整个应用程序中重用interop 功能,越是应该认真地考虑包装的设计。同时我认为,在非面向对象的静态包装方法中使用对CLR 友好的参数也并非不可以。

DLL Import 属性

现在是更深入地进行探讨的时候了。在对托管代码进行P/Invoke 调用时,DllImportAttribute 类型扮演着重要的角色。DllImportAttribute 的主要作用是给CLR 指示哪个DLL 导出您想要调用的函数。相关DLL 的名称被作为一个构造函数参数传递给DllImportAttribute。

如果您无法肯定哪个DLL 定义了您要使用的Windows API 函数,Platform SDK 文档将为您提供最好的帮助资源。在Windows API 函数主题文字临近结尾的位置,SDK 文档指定了 C 应用程序要使用该函数必须链接的.lib 文件。在几乎所有的情况下,该.lib 文件具有与定义该函数的系统DLL 文件相同的名称。例如,如果该函数需要 C 应用程序链接到Kernel32.lib,则该函数就定义在Kernel32.dll 中。您可以在MessageBeep 中找到有关MessageBeep 的Platform SDK 文档主题。在该主题结尾处,您会注意到它指出库文件是User32.lib;这表明MessageBeep 是从User32.dll 中导出的。

可选的DllImportAttribute 属性

除了指出宿主DLL 外,DllImportAttribute 还包含了一些可选属性,其中四个特别有趣:

EntryPoint、CharSet、SetLastError 和CallingConvention。

EntryPoint 在不希望外部托管方法具有与DLL 导出相同的名称的情况下,可以设置该属性来指示导出的DLL 函数的入口点名称。当您定义两个调用相同非托管函数的外部方法时,这特别有用。另外,在Windows 中还可以通过它们的序号值绑定到导出的DLL 函数。如果您需要这样做,则诸如“#1”或“#129”的EntryPoint 值指示DLL 中非托管函数的序号值而不是函数名。

CharSet 对于字符集,并非所有版本的Windows 都是同样创建的。Windows 9x 系列产品缺少重要的Unicode 支持,而Windows NT 和Windows CE 系列则一开始就使用Unicode。在这些操作系统上运行的CLR 将Unicode 用于String 和Char 数据的内部表示。但也不必担心—当调用Windows 9x API 函数时,CLR 会自动进行必要的转换,将其从Unicode转换为ANSI。

如果DLL 函数不以任何方式处理文本,则可以忽略DllImportAttribute 的CharSet 属性。然而,当Char 或String 数据是等式的一部分时,应该将CharSet 属性设置为CharSet.Auto。这样可以使CLR 根据宿主OS 使用适当的字符集。如果没有显式地设置CharSet 属性,则其默认值为CharSet.Ansi。这个默认值是有缺点的,因为对于在Windows 2000、Windows X P 和Windows NT? 上进行的interop 调用,它会消极地影响文本参数封送处理的性能。

应该显式地选择CharSet.Ansi 或CharSet.Unicode 的CharSet 值而不是使用CharSet.Auto 的唯一情况是:您显式地指定了一个导出函数,而该函数特定于这两种Win32 OS 中的某一种。ReadDirectoryChangesW API 函数就是这样的一个例子,它只存在于基于Windows NT 的操作系统中,并且只支持Unicode;在这种情况下,您应该显式地使用CharSet.Unicode。

有时,Windows API 是否有字符集关系并不明显。一种决不会有错的确认方法是在Platform SDK 中检查该函数的 C 语言头文件。(如果您无法肯定要看哪个头文件,则可以查看Platform SDK 文档中列出的每个API 函数的头文件。)如果您发现该API 函数确实定义为一个映射到以 A 或W 结尾的函数名的宏,则字符集与您尝试调用的函数有关系。Windows API 函数的一个例子是在WinUser.h 中声明的GetMessage API,您也许会惊讶地发现它有 A 和W 两种版本。

SetLastError 错误处理非常重要,但在编程时经常被遗忘。当您进行P/Invoke 调用时,也会面临其他的挑战—处理托管代码中Windows API 错误处理和异常之间的区别。我可以给您一点建议。

如果您正在使用P/Invoke 调用Windows API 函数,而对于该函数,您使用GetLastError 来查找扩展的错误信息,则应该在外部方法的DllImportAttribute 中将SetLastError 属性设置为true。这适用于大多数外部方法。

这会导致CLR 在每次调用外部方法之后缓存由API 函数设置的错误。然后,在包装方法中,可以通过调用类库的System.Runtime.InteropServices.Marshal 类型中定义的Marshal.GetLastWin32Error 方法来获取缓存的错误值。我的建议是检查这些期望来自API 函数的错误值,并为这些值引发一个可感知的异常。对于其他所有失败情况(包括根本就没意料到

的失败情况),则引发在https://www.360docs.net/doc/203239976.html,ponentModel 命名空间中定义的Win32Exception,并将Marshal.GetLastWin32Error 返回的值传递给它。如果您回头看一下图 1 中的代码,您会看到我在extern MessageBeep 方法的公共包装中就采用了这种方法。

CallingConvention 我将在此介绍的最后也可能是最不重要的一个DllImportAttribute 属性是CallingConvention。通过此属性,可以给CLR 指示应该将哪种函数调用约定用于堆栈中的参数。CallingConvention.Winapi 的默认值是最好的选择,它在大多数情况下都可行。然而,如果该调用不起作用,则可以检查Platform SDK 中的声明头文件,看看您调用的API 函数是否是一个不符合调用约定标准的异常API。

通常,本机函数(例如Windows API 函数或C- 运行时DLL 函数)的调用约定描述了如何将参数推入线程堆栈或从线程堆栈中清除。大多数Windows API 函数都是首先将函数的最后一个参数推入堆栈,然后由被调用的函数负责清理该堆栈。相反,许多C-运行时DLL 函数都被定义为按照方法参数在方法签名中出现的顺序将其推入堆栈,将堆栈清理工作交给调用者。

幸运的是,要让P/Invoke 调用工作只需要让外围设备理解调用约定即可。通常,从默认值CallingConvention.Winapi 开始是最好的选择。然后,在 C 运行时DLL 函数和少数函数中,可能需要将约定更改为CallingConvention.Cdecl。

数据封送处理

数据封送处理是P/Invoke 具有挑战性的方面。当在托管和非托管代码之间传递数据时,CLR 遵循许多规则,很少有开发人员会经常遇到它们直至可将这些规则记住。除非您是一名类库开发人员,否则在通常情况下没有必要掌握其细节。为了最有效地在CLR 上使用P/Invoke,即使只偶尔需要interop 的应用程序开发人员仍然应该理解数据封送处理的一些基础知识。

在本月专栏的剩余部分中,我将讨论简单数字和字符串数据的数据封送处理。我将从最基本的数字数据封送处理开始,然后介绍简单的指针封送处理和字符串封送处理。

封送数字和逻辑标量

Windows OS 大部分是用 C 编写的。因此,Windows API 所用到的数据类型要么是 C 类型,要么是通过类型定义或宏定义重新标记的 C 类型。让我们看看没有指针的数据封送处理。简单起见,首先重点讨论的是数字和布尔值。

当通过值向Windows API 函数传递参数时,需要知道以下问题的答案:

? 数据从根本上讲是整型的还是浮点型的?

? 如果数据是整型的,则它是有符号的还是无符号的?

? 如果数据是整型的,则它的位数是多少?

? 如果数据是浮点型的,则它是单精度的还是双精度的?

有时答案很明显,但有时却不明显。Windows API 以各种方式重新定义了基本的 C 数据类型。图 2 列出了 C 和Win32 的一些公共数据类型及其规范,以及一个具有匹配规范的公共语言运行库类型。

通常,只要您选择一个其规范与该参数的Win32 类型相匹配的CLR 类型,您的代码就能够正常工作。不过也有一些特例。例如,在Windows API 中定义的BOOL 类型是一个有符号的32 位整型。然而,BOOL 用于指示Boolean 值true 或false。虽然您不用将BOOL 参数作为System.Int32 值封送,但是如果使用System.Boolean 类型,就会获得更合适的映射。字符类型的映射类似于BOOL,因为有一个特定的CLR 类型(System.Char) 指出字符的含义。

在了解这些信息之后,逐步介绍示例可能是有帮助的。依然采用beep 主题作为例子,让我们来试一下Kernel32.dll 低级Beep,它会通过计算机的扬声器发生嘟声。这个方法的Platform SDK 文档可以在Beep 中找到。本机API 按以下方式进行记录:

BOOL Beep(

DWORD dwFreq, // Frequency

DWORD dwDuration // Duration in milliseconds

);

在参数封送处理方面,您的工作是了解什么CLR 数据类型与Beep API 函数所使用的DWORD 和BOOL 数据类型相兼容。回顾一下图 2 中的图表,您将看到DWORD 是一个32 位的无符号整数值,如同CLR 类型System.UInt32。这意味着您可以使用UInt32 值作为送往Beep 的两个参数。BOOL 返回值是一个非常有趣的情况,因为该图表告诉我们,在Win32 中,BOOL 是一个32 位的有符号整数。因此,您可以使用System.Int32 值作为来自Beep 的返回值。然而,CLR 也定义了System.Boolean 类型作为Boolean 值的语义,所以应该使用它来替代。CLR 默认将System.Boolean 值封送为32 位的有符号整数。此处所显示的外部方法定义是用于Beep 的结果P/Invoke 方法:

[DllImport("Kernel32.dll", SetLastError=true)]

static extern Boolean Beep(

UInt32 frequency, UInt32 duration);

指针参数

许多Windows API 函数将指针作为它们的一个或多个参数。指针增加了封送数据的复杂性,因为它们增加了一个间接层。如果没有指针,您可以通过值在线程堆栈中传递数据。有了指针,则可以通过引用传递数据,方法是将该数据的内存地址推入线程堆栈中。然后,函数通过内存地址间接访问数据。使用托管代码表示此附加间接层的方式有多种。

在C# 中,如果将方法参数定义为ref 或out,则数据通过引用而不是通过值传递。即使您没有使用Interop 也是这样,但只是从一个托管方法调用到另一个托管方法。例如,如果通过ref 传递System.Int32 参数,则在线程堆栈中传递的是该数据的地址,而不是整数值本身。下面是

一个定义为通过引用接收整数值的方法的示例:

void FlipInt32(ref Int32 num){

num = -num;

}

这里,FlipInt32 方法获取一个Int32 值的地址、访问数据、对它求反,然后将求反过的值赋给原始变量。在以下代码中,FlipInt32 方法会将调用程序的变量x 的值从10 更改为-10:

Int32 x = 10;

FlipInt32(ref x);

在托管代码中可以重用这种能力,将指针传递给非托管代码。例如,FileEncryptionStatus API 函数以32 位无符号位掩码的形式返回文件加密状态。该API 按以下所示方式进行记录:

BOOL FileEncryptionStatus(

LPCTSTR lpFileName, // file name

LPDWORD lpStatus // encryption status

);

请注意,该函数并不使用它的返回值返回状态,而是返回一个Boolean 值,指示调用是否成功。在成功的情况下,实际的状态值是通过第二个参数返回的。它的工作方式是调用程序向该函数传递指向一个DWORD 变量的指针,而该API 函数用状态值填充指向的内存位置。以下代码片段显示了一个调用非托管FileEncryptionStatus 函数的可能外部方法定义:

[DllImport("Advapi32.dll", CharSet=CharSet.Auto)]

static extern Boolean FileEncryptionStatus(String filename,

out UInt32 status);

该定义使用out 关键字来为UInt32 状态值指示by-ref 参数。这里我也可以选择ref 关键字,实际上在运行时会产生相同的机器码。out 关键字只是一个by-ref 参数的规范,它向C# 编译器指示所传递的数据只在被调用的函数外部传递。相反,如果使用ref 关键字,则编译器会假定数据可以在被调用的函数的内部和外部传递。

托管代码中out 和ref 参数的另一个很好的方面是,地址作为by-ref 参数传递的变量可以是线程堆栈中的一个本地变量、一个类或结构的元素,也可以是具有合适数据类型的数组中的一个元素引用。调用程序的这种灵活性使得by-ref 参数成为封送缓冲区指针以及单数值指针的一个很好的起点。只有在我发现ref 或out 参数不符合我的需要的情况下,我才会考虑将指针封送为更复杂的CLR 类型(例如类或数组对象)。

如果您不熟悉 C 语法或者调用Windows API 函数,有时很难知道一个方法参数是否需要指针。一个常见的指示符是看参数类型是否是以字母P 或LP 开头的,例如LPDWORD 或PINT。在这两个例子中,LP 和P 指示参数是一个指针,而它们指向的数据类型分别为DWORD 或INT。然而,在有些情况下,可以直接使用 C 语言语法中的星号(*) 将API 函数定义为指

针。以下代码片段展示了这方面的示例:

void T akesAPointer(DWORD* pNum);

可以看到,上述函数的唯一一个参数是指向DWORD 变量的指针。

当通过P/Invoke 封送指针时,ref 和out 只用于托管代码中的值类型。当一个参数的CLR 类型使用struct 关键字定义时,可以认为该参数是一个值类型。Out 和ref 用于封送指向这些数据类型的指针,因为通常值类型变量是对象或数据,而在托管代码中并没有对值类型的引用。相反,当封送引用类型对象时,并不需要ref 和out 关键字,因为变量已经是对象的引用了。

如果您对引用类型和值类型之间的差别不是很熟悉,请查阅2000 年12 月发行的MSDN? Magazine,在.NET 专栏的主题中可以找到更多信息。大多数CLR 类型都是引用类型;然而,除了System.String 和System.Object,所有的基元类型(例如System.Int32 和System.Boolean)都是值类型。

封送不透明(Opaque) 指针:一种特殊情况

有时在Windows API 中,方法传递或返回的指针是不透明的,这意味着该指针值从技术角度讲是一个指针,但代码却不直接使用它。相反,代码将该指针返回给Windows 以便随后进行重用。

一个非常常见的例子就是句柄的概念。在Windows 中,内部数据结构(从文件到屏幕上的按钮)在应用程序代码中都表示为句柄。句柄其实就是不透明的指针或有着指针宽度的数值,应用程序用它来表示内部的OS 构造。

少数情况下,API 函数也将不透明指针定义为PVOID 或LPVOID 类型。在Windows API 的定义中,这些类型意思就是说该指针没有类型。

当一个不透明指针返回给您的应用程序(或者您的应用程序期望得到一个不透明指针)时,您应该将参数或返回值封送为CLR 中的一种特殊类型— System.IntPtr。当您使用IntPtr 类型时,通常不使用out 或ref 参数,因为IntPtr 意为直接持有指针。不过,如果您将一个指针封送为一个指针,则对IntPtr 使用by-ref 参数是合适的。

在CLR 类型系统中,System.IntPtr 类型有一个特殊的属性。不像系统中的其他基类型,IntPtr 并没有固定的大小。相反,它在运行时的大小是依底层操作系统的正常指针大小而定的。这意味着在32 位的Windows 中,IntPtr 变量的宽度是32 位的,而在64 位的Windows 中,实时编译器编译的代码会将IntPtr 值看作64 位的值。当在托管代码和非托管代码之间封送不透明指针时,这种自动调节大小的特点十分有用。

请记住,任何返回或接受句柄的API 函数其实操作的就是不透明指针。您的代码应该将Windows 中的句柄封送成System.IntPtr 值。

您可以在托管代码中将IntPtr 值强制转换为32 位或64 位的整数值,或将后者强制转换为前

者。然而,当使用Windows API 函数时,因为指针应是不透明的,所以除了存储和传递给外部方法外,不能将它们另做它用。这种“只限存储和传递”规则的两个特例是当您需要向外部方法传递null 指针值和需要比较IntPtr 值与null 值的情况。为了做到这一点,您不能将零强制转换为System.IntPtr,而应该在IntPtr 类型上使用Int32.Zero 静态公共字段,以便获得用于比较或赋值的null 值。

封送文本

在编程时经常要对文本数据进行处理。文本为interop 制造了一些麻烦,这有两个原因。首先,底层操作系统可能使用Unicode 来表示字符串,也可能使用ANSI。在极少数情况下,例如MultiByteT oWideChar API 函数的两个参数在字符集上是不一致的。

第二个原因是,当需要进行P/Invoke 时,要处理文本还需要特别了解到 C 和CLR 处理文本的方式是不同的。在 C 中,字符串实际上只是一个字符值数组,通常以null 作为结束符。大多数Windows API 函数是按照以下条件处理字符串的:对于ANSI,将其作为字符值数组;对于Unicode,将其作为宽字符值数组。

幸运的是,CLR 被设计得相当灵活,当封送文本时问题得以轻松解决,而不用在意Windows API 函数期望从您的应用程序得到的是什么。这里是一些需要记住的主要考虑事项:

? 是您的应用程序向API 函数传递文本数据,还是API 函数向您的应用程序返回字符串数据?或者二者兼有?

? 您的外部方法应该使用什么托管类型?

? API 函数期望得到的是什么格式的非托管字符串?

我们首先解答最后一个问题。大多数Windows API 函数都带有LPTSTR 或LPCTSTR 值。(从函数角度看)它们分别是可修改和不可修改的缓冲区,包含以null 结束的字符数组。“C”代表常数,意味着使用该参数信息不会传递到函数外部。LPTSTR 中的“T”表明该参数可以是Unicode 或ANSI,取决于您选择的字符集和底层操作系统的字符集。因为在Windows API 中大多数字符串参数都是这两种类型之一,所以只要在DllImportAttribute 中选择CharSet.Auto,CLR 就按默认的方式工作。

然而,有些API 函数或自定义的DLL 函数采用不同的方式表示字符串。如果您要用到一个这样的函数,就可以采用MarshalAsAttribute 修饰外部方法的字符串参数,并指明一种不同于默认LPTSTR 的字符串格式。有关MarshalAsAttribute 的更多信息,请参阅位于MarshalAsAttribute Class 的Platform SDK 文档主题。

现在让我们看一下字符串信息在您的代码和非托管函数之间传递的方向。有两种方式可以知道处理字符串时信息的传递方向。第一个也是最可靠的一个方法就是首先理解参数的用途。例如,您正调用一个参数,它的名称类似CreateMutex 并带有一个字符串,则可以想像该字符串信息是从应用程序向API 函数传递的。同时,如果您调用GetUserName,则该函数的名称表明字符

串信息是从该函数向您的应用程序传递的。

除了这种比较合理的方法外,第二种查找信息传递方向的方式就是查找API 参数类型中的字母“C”。例如,GetUserName API 函数的第一个参数被定义为LPTSTR 类型,它代表一个指向Unicode 或ANSI 字符串缓冲区的长指针。但是CreateMutex 的名称参数被类型化为LTCTSTR。请注意,这里的类型定义是一样的,但增加一个字母“C”来表明缓冲区为常数,API 函数不能写入。

一旦明确了文本参数是只用作输入还是用作输入/输出,就可以确定使用哪种CLR 类型作为参数类型。这里有一些规则。如果字符串参数只用作输入,则使用System.String 类型。在托管代码中,字符串是不变的,适合用于不会被本机API 函数更改的缓冲区。

如果字符串参数可以用作输入和/或输出,则使用System.StringBuilder 类型。StringBuilder 类型是一个很有用的类库类型,它可以帮助您有效地构建字符串,也正好可以将缓冲区传递给本机函数,由本机函数为您填充字符串数据。一旦函数调用返回,您只需要调用StringBuilder 对象的T oString 就可以得到一个String 对象。

GetShortPathName API 函数能很好地用于显示什么时候使用String、什么时候使用StringBuilder,因为它只带有三个参数:一个输入字符串、一个输出字符串和一个指明输出缓冲区的字符长度的参数。

图 3 所示为加注释的非托管GetShortPathName 函数文档,它同时指出了输入和输出字符串参数。它引出了托管的外部方法定义,也如图 3 所示。请注意第一个参数被封送为System.String,因为它是一个只用作输入的参数。第二个参数代表一个输出缓冲区,它使用了System.StringBuilder。

小结

本月专栏所介绍的P/Invoke 功能足够调用Windows 中的许多API 函数。然而,如果您大量用到interop,则会最终发现自己封送了很复杂的数据结构,甚至可能需要在托管代码中通过指针直接访问内存。实际上,本机代码中的interop 可以是一个将细节和低级比特藏在里面的真正的潘多拉盒子。CLR、C# 和托管C++ 提供了许多有用的功能;也许以后我会在本专栏介绍高级的P/Invoke 话题。

同时,只要您觉得.NET Framework 类库无法播放您的声音或者为您执行其他一些功能,您可以知道如何向原始而优秀的Windows API 寻求一些帮助。

API(应用编程接口)是程序与处理器接口的命令集。最常用的就是在外部调用微软WINDOWS内部的进程。WINDOWS API包括成千的你可以使用的函数、结构、常量。这些函数是用C语言写的,在使用他们之前,你必须声明。定义Dll的进程将相当的复杂,甚至比VB还复杂。你可以使用API Viewer工具得到API函数的声明,但是必须注意的是,它的参数类型跟C#的不一样。

大部分的高级语言都支持API,微软函数类库(MFC)封装了大部分的Win32 API。ODBC API对提高数据库的操作速度大有好处。使用API,可以请求更底层的系统服务。API从简单的对话框

到复杂的加密运算都提供支持。开发者应该知道如何在他们程序中使用API

API有许多类型,(针对不同的操作系统、处理器…………)

OS specific API:

操作系统特有API:

每种操作系统都有一套公用API和专有API。比如:Windows NT 支持MS-DOS, Win16, Win32, POSIX (便携式操作系统接口),OS/2 console API ;同时Windows 95 supports MS-DOS, Win16 和Win32 API。

Win16 和Win32 API:

WIN16 是基于16位的处理器,并使用16位的值,它是一个独立的平台。比如:你可以运行TSR 程序在MS-DOS环境下。

WIN32 是基于32位的处理器,并使用32位的值。他可用于任何操作系统,它的使用范围更广。Win32 API has 32 prefix after the library name e.g. KERNEL32, USER32 etc?

Win32 API的DLL一般都具有32的后缀,比如:KERNEL32, USER32等。

所有的API都在下面3个DLL中实现的。

Kernel

User

GDI

1. KERNEL

它的库名是:KERNEL32.DLL,它是操作系统管理的API集

Process loading. 加载进程

Context switching.

File I/O. 文件操作

Memory management. 内存管理

比如:GlobalMemoryStatus 函数获得目前系统物理虚拟内存的使用信息。

2. USER

在WIN32下,它的库名是USER32.DLL

This allows managing the entire user interfaces such as

它管理全部的用户界面,比如:

Windows 窗口

Menus 菜单

Dialog Boxes 对话框

Icons etc., 图标等

比如:DrawIcon 画一个图标在指定的设备上。

3. GDI (Graphical Device Interface)

这个DLL是GDI32.dll,它负责图像的输出,使用GDI绘出窗口,菜单,对话框

It can create Graphical Output. 输出图像

比如:CreateBitmap 函数创建一个指定宽度、高度和颜色格式的位图。

C#中API的工具对初学者是相当不错的。在C#使用中使用API之前,你应该先知道C#中如何使用结构、类型转换,安全与不安全代码等。

使用复杂的api之前,我们先用一个简单的MessageBox API作为列子。打开一个C#工程,增加一个按钮,在按钮的点击事件中,我们将显示一个信息框。

增加使用外部库的命名空间:

using System.Runtime.InteropServices;

下面声明API

[DllImport("User32.dll")]

DllImport属性用来指定包含外部方法的动态连接库的位置。"User32.dll"指出了库名,static 指明它不属于特定的对象。extern 指明是一个外部的方法。带有DllImport 属性的方法必须带有修饰符extern 。

MessageBox 是一个汉数名,带四个参数返回一个int型值。

许多API使用结构来传递、返回参数,这样可以减少复杂度。它也允许使用象MessageBox 函数那样,使用固定的参数。

在按钮的点击事件中增加下面代码:

protected void button1_Click (object sender, System.EventArgs e)

{

MessageBox (0,"API Message Box","API Demo",0);

}

编译并运行程序,点击按钮以后,你就可以看到一个由API调用的信息框。

Using structure 使用结构

API中经常使用复杂的结构。不过一旦你明白了他们,将是很简单的。

In next example we will use GetSystemInfo API which returns information about the current system.

下面的列子,我们用GetSystemInfo API得到当前系统的信息。

第一步:增加一个C#窗口,并在上面增加一个按钮,在窗口代码页增加一个命名空间:

using System.Runtime.InteropServices;

声明GetSystemInfo 的参数结构:

[StructLayout(LayoutKind.Sequential)]

public struct SYSTEM_INFO {

public uint dwOemId;

public uint dwPageSize;

public uint lpMinimumApplicationAddress;

public uint lpMaximumApplicationAddress;

public uint dwActiveProcessorMask;

public uint dwNumberOfProcessors;

public uint dwProcessorT ype;

public uint dwAllocationGranularity;

public uint dwProcessorLevel;

public uint dwProcessorRevision;

}

声明API函数:

[DllImport("kernel32")]

static extern void GetSystemInfo(ref SYSTEM_INFO pSI);

ref是一个标志参量传递形式的关键字,它使传入传出的变量指向同一个变量(传址传递)

在按钮点击事件中增加下面的代码,

protected void button1_Click (object sender, System.EventArgs e)

{

try

{

SYSTEM_INFO pSI = new SYSTEM_INFO();

GetSystemInfo(ref pSI);

Once you retrieve the structure perform operations on required parameter

比如:

listBox1.Items.Insert(0,pSI.dwActiveProcessorMask.T oString());

}

catch(Exception er)

{

MessageBox.Show (er.Message);

}

}

用Visual C#调用Windows API函数

北京机械工业学院研00级(100085)冉林仓

Api函数是构筑Windws应用程序的基石,每一种Windows应用程序开发工具,它提供的底层函数都间接或直接地调用了Windows API函数,同时为了实现功能扩展,一般也都提供了调用WindowsAPI函数的接口,也就是说具备调用动态连接库的能力。Visual C#和其它开发工具一样也能够调用动态链接库的API函数。.NET框架本身提供了这样一种服务,允许受管辖的代码调用动态链接库中实现的非受管辖函数,包括操作系统提供的Windows API函数。它能够定位和调用输出函数,根据需要,组织其各个参数(整型、字符串类型、数组、和结构等等)跨越互操作边界。

下面以C#为例简单介绍调用API的基本过程:

动态链接库函数的声明

动态链接库函数使用前必须声明,相对于VB,C#函数声明显得更加罗嗦,前者通过Api Viewer 粘贴以后,可以直接使用,而后者则需要对参数作些额外的变化工作。

动态链接库函数声明部分一般由下列两部分组成,一是函数名或索引号,二是动态链接库的文件名。

譬如,你想调用User32.DLL中的MessageBox函数,我们必须指明函数的名字MessageBoxA 或MessageBoxW,以及库名字User32.dll,我们知道Win32 API对每一个涉及字符串和字符的函数一般都存在两个版本,单字节字符的ANSI版本和双字节字符的UNICODE版本。

下面是一个调用API函数的例子:

[DllImport("KERNEL32.DLL", EntryPoint="MoveFileW", SetLastError=true,

CharSet=CharSet.Unicode, ExactSpelling=true,

CallingConvention=CallingConvention.StdCall)]

public static extern bool MoveFile(String src, String dst);

其中入口点EntryPoint标识函数在动态链接库的入口位置,在一个受管辖的工程中,目标函数的原始名字和序号入口点不仅标识一个跨越互操作界限的函数。而且,你还可以把这个入口点映射为一个不同的名字,也就是对函数进行重命名。重命名可以给调用函数带来种种便利,通过

重命名,一方面我们不用为函数的大小写伤透脑筋,同时它也可以保证与已有的命名规则保持一致,允许带有不同参数类型的函数共存,更重要的是它简化了对ANSI和Unicode版本的调用。CharSet用于标识函数调用所采用的是Unicode或是ANSI版本,ExactSpelling=false将告诉编译器,让编译器决定使用Unicode或者是Ansi版本。其它的参数请参考MSDN在线帮助.

在C#中,你可以在EntryPoint域通过名字和序号声明一个动态链接库函数,如果在方法定义中使用的函数名与DLL入口点相同,你不需要在EntryPoint域显示声明函数。否则,你必须使用下列属性格式指示一个名字和序号。

[DllImport("dllname", EntryPoint="Functionname")]

[DllImport("dllname", EntryPoint="#123")]

值得注意的是,你必须在数字序号前加“#”

下面是一个用MsgBox替换MessageBox名字的例子:

[C#]

using System.Runtime.InteropServices;

public class Win32 {

[DllImport("user32.dll", EntryPoint="MessageBox")]

public static extern int MsgBox(int hWnd, String text, String caption, uint ty pe);

}

许多受管辖的动态链接库函数期望你能够传递一个复杂的参数类型给函数,譬如一个用户定义的结构类型成员或者受管辖代码定义的一个类成员,这时你必须提供额外的信息格式化这个类型,以保持参数原有的布局和对齐。

C#提供了一个StructLayoutAttribute类,通过它你可以定义自己的格式化类型,在受管辖代码中,格式化类型是一个用StructLayoutAttribute说明的结构或类成员,通过它能够保证其内部成员预期的布局信息。布局的选项共有三种:

布局选项

描述

LayoutKind.Automatic

为了提高效率允许运行态对类型成员重新排序。

注意:永远不要使用这个选项来调用不受管辖的动态链接库函数。

LayoutKind.Explicit

对每个域按照FieldOffset属性对类型成员排序

LayoutKind.Sequential

对出现在受管辖类型定义地方的不受管辖内存中的类型成员进行排序。

传递结构成员

下面的例子说明如何在受管辖代码中定义一个点和矩形类型,并作为一个参数传递给User32.dll 库中的PtInRect函数,

函数的不受管辖原型声明如下:

BOOL PtInRect(const RECT *lprc, POINT pt);

注意你必须通过引用传递Rect结构参数,因为函数需要一个Rect的结构指针。

[C#]

VFP常见编程实例(免费下载)

VFP常见编程实例 1、1-100的累加 ** 求1到100的累加值 CLEAR s=0 i=1 DO WHILE i<=100 s=s+i i=i+1 &&一定记住先累加后循环ENDDO ?s RETURN 2、阶乘 ** 求5的阶乘 CLEAR j=1 i=1 DO WHILE i<=5 j=j*i

i=i+1 ENDDO ?j RETURN 3、求阶乘和 ** 求1至5的阶乘和CLEAR s=0 i=1 j=1 DO WHILE i<=5 j=j*i s=s+j i=i+1 ENDDO ?s RETURN 4、条件定位

** 查询定位记录指针在赵伟上 CLEAR USE 学生 LOCATE FOR 姓名="赵伟" IF FOUND() DISPLAY ELSE CANCEL ENDIF CONTINUE 5、用scan语句查询定位 ** 用scan语句查询定位记录在赵伟上 CLEAR SCAN FOR 姓名="赵伟" DISPLAY ENDSCAN RETURN 6、用scan语句扫描符合条件的记录,并显示出来** 把年龄小于30岁的人显示出来

SCAN FOR 年龄<30 DISPLAY ENDSCAN RETURN 7、查找男性职工 ** 查找男性职工CLEAR LOCATE FOR 性别="男" DO WHILE FOUND() DISPLAY CONTINUE ENDDO RETURN ** 查找男性职工CLEAR LOCATE FOR 性别="男" DO WHILE NOT EOF() DISPLAY CONTINUE

VFP函数大全

VFP函数大全 %运算符用于计算并返回两个数值表达式相除之后的余数 $(包含)运算符用于进行字符表达式之间的包含关系运算,如果一个字符表达式包含在另一个字符表达式之中,则函数返回真 ABS() 计算并返回指定数值表达式的绝对值 ACLASS() 用于将一个对象的父类名放置于一个内存数组中 ACOPY() 把一个数组的元素拷贝到另一个数组中 ACOS() 计算并返回一个指定数值表达式的余弦值 ADATABASES() 用于将所有打开的数据库名和它的路径存入一个内在变量数组中 ADB OBJECT S() 用于把当前数据库中的连接、表或SQL视图的名存入内存变量数组中 ADEL() 用于从一维数据中删除一个元素,或从二维数组中删除一行或者一列元素 ADIR() 将文件的有关信息存入指定的数组中,然后返回文件数 AELEMENT() 通过元素的下标,返回元素号 AFIELDS 将当前的结构信息存入数组中,然后返回表中的字段数 AFONT() 将可用字体的信息存入数组中 AERROR() 用于创建包含VFP或ODBC错误信息的内存变量 AINS() 在一维数组中插入一个元素或在二维数组中插入一行或一列元素 AINSTANCE() 用于将类的所有实例存入内存变量数组中,然后返回数组中存放的实例数 ALEN() 返回数组中元素、行或者列数 ALIAS() 返回当前工作区或指定工作区内表的别名 ALLTRIM() 从指定字符表达式的首尾两端删除前导和尾随的空格字符,然后返回截去空格后的字符串 AMEMBERS() 用于将对象的属性、过程和成员对象存入内存变量数组中

ANSITOOEM() 将指定字符表达式中的每个字符转换为MS-DOS(OEM)字符集中对应字符 APRINTERS() 将Print Manager中安装的当前打印机名存入内存变量数组中 ASC() 用于返回指定字符表达式中最左字符的ASCII码值 ASCAN() 搜索一个指定的数组,寻找一个与表达式中数据和数据类型相同的数组元素 ASELOBJ() 将活动的Form设计器当前控件的对象引用存储到内存变量数组中 ASIN() 计算并返回指定数值表达式反正弦值 ASORT() 按升序或降序排列数组中的元素 ASUBSCRIPT() 计算并返回指定元素号的行或者列坐标 AT() 寻找字符串或备注字段在另一字符串或备注字段中的第一次出现,并返回位置 ATAN() 计算并返回指定数值表达式的反正切值 ATC() 寻找字符串或备注字段中的第一次出现,并返回位置,将不考虑表达式中字母的大小写 ATCLINE() 寻找并返回一个字符串表达式或备注字段在另一字符表达式或备注字段中第一次出现的行号。不区分字符大小写 ATLINE() 寻找并返回一个字符表达式或备注字段在另一字符表达式或备注字段中第一次出现的行号ATN2() 根据指定的值返回所有4个像限内的反正切值 AUSED() 用于将一次会话期间的所有表别名和工作区存入变量数组之中 BAR() 从用DEFINE POPUP命令定义的菜单中返回最近所选择的菜单项的编号,或返回一个从VFP菜单所选择的一个菜单命令 BARCOUNT() 返回DEFINE POPUP命令所定义的菜单中的菜单项数,或返回VFP系统菜单上的菜单项数 BARPROMPT() 返回一个菜单项的有关正文 BETWEEN() 确定指定的表达式是否介于两个相同类型的表达式之间 BITAND() 返回两个数值表达式之间执行逐位与(AND)运算的结果 BITCLEAR() 清除数值表达式中的指定位,然后再返回结果值 BITLSHIFT() 返回将数值表达式左移若干位后的结果值

vfp中api函数调用实例

vfp中api函数调用实例 我们在vfp编程时,为了弥补vfp本身功能的不足,常常会用到win32api函数来对vfp 的功能进行扩展,例如取得系统信息、对注册表进行操作等,但由于win32api函数是c++的数据结构形式,在调用时与vfp的数据格式有所不同,因此在调用时往往会产生问题。下面,笔者就在vfp如何调用win32api函数,并就一些常用的win32api函数调用实例作一具体介绍。 一、vfp中调用win32api函数的格式 vfp中调用win32api函数的命令主要是declare-dll和clear dlls两个。 1.declare-dll 它用于注册外部windows 32位动态链接库(.dll)中的一个函数。 语法: declare [cfunctiontype] functionname in libraryname [as aliasname] [cparamtype1 [@] paramname1, cparamtype2 [@] paramname2, ...] 说明: cfunctiontype 指定该函数返回值的类型,其类型代码如下: short16位整数 integer32位整数 single32位浮点数 double64位浮点数 string字符串 functionname指定在要注册使用的32位函数名。注意functionname区分大小写。 如果该函数名与visual foxpro的保留字相同,应使用as参数另起别名。 in libraryname 指定该函数所在的外部动态链接库文件名,如果libraryname为win32api时,vfp将依次查找:kernel32.dll、gdi32.dll、user32.dll、mpr.dll及advapi32.dll 等动态链接库文件名。 as aliasname 为当api函数的名称与visual foxpro的保留字相同时,另取aliasname别名,另取的别名是不区分大小写的。 cparametertype1 [@] paramname1, cparametertype2 [@] paramname2, ... 指定传递给被调用的32位.dll函数的参数类型及参数名称。其类型代码如cfunctiontype,如果要以传址方法传递参数则应在参数名前加上@。 2. clear dlls 释放所有利用declare-dll命令所装入的动态链接库。 一旦申明win32api函数成功,就可以和一般函数一样用?xxx()、=xxx() 来使用。 二、vfp中常用win32api函数调用实例 1.利用win32api函数获取指定磁盘的卷标: stor 0 to c—var,c—cd,c—qf c—disk=′c:\′ &&指定磁盘 declare integer getvolumeinformation in win32api string @,string @,integer,; integer @, integer @, integer @, string @, integer xx=getvolumeinformation(c—disk,″ ″,20,@c—var,@c—cd,@c—qf,0,0) clear dlls 返回值: xx〈〉0 获取指定磁盘的卷标成功,反之则未成功。

vfP 函数练习题

第二章练习题 一、选择题。 1. 就关系数据描述语言来说,Visual FoxPro属于______数据库管理系统。 A)问答式B)语言描述式 C)域描述语句式D)关系描述语句式 2. 下列______符号不能作为Visual FoxPro中的变量名。 A)ABCDEFG B)K1234 C)12ASDFG D)xyz 3. 日期型数据长度固定为______个字符。 A)10B)12 C)8 D)6 4. 在Visual FoxPro数据库文件中,逻辑型、日期型、备注型数据的宽度分别是______。 A)1,8,128 B)2,8,10 C)1,8,10 D)1,8,任意 5. 以下4个符号中,______是Visual FoxPro中的一个常量。 A)T B)TOP C).T. D)TO 6. Visual FoxPro中,表达式: 2*3^2+2*8/4+3^2的值为______。 A)64 B)31 C)49 D)22 7. 下列4个式子中,______不是Visual FoxPro中的表达式。 A)05/23/88 B)"2002" C)X+Y D)XYZ='5' AND ABC=5 8. 将逻辑值赋给内存变量X的正确方法是______。 A)X="T" B)STORE "T" TO X C)X=TRUE D)STORE .T. TO X 9. 设M="324.2",?43+&M的结果是______。 A)43 B)324.2 C)43324.2 D)367.2 10. 设A=123,B=456,K="A+B",则表达式l+&K的值是______。 A)1123456 B)580 C)1+A+B D)579 11. 顺序键入以下命令,显示结果为______。 STORE "14.31" TO X Y=INT(&X+5) ?Y A)20 B)14.315 C)14.31 D)19 12. Visual FoxPro中的索引文件分为单索引文件和复合索引文件,其中复合索引文件的扩展名为______。A)IDX B)EDX C)CDX D)NDX 13. 用于存储内存变量的文件扩展名为______。 A)FPT B)PRG C)FMT D)MEM 14. Visual FoxPro函数INT(RAND()*10)的值是在______范围内的整数。 A)(0,1)B)(1,10)C)(0,10)D)(1,9) 15. 数学式子sin25°写成Visual FoxPro表达式是______。 A)SIN25 B)SIN(25)C)SIN(25°)D)SIN(25*PI/180) 16. 设某数据库有100条记录。用函数EOF ( ) 测试结果为.T.,此时,当前记录号为______。 A)100 B)101 C)99D)1 17. 在下列表达式中,结果为日期类型的正确表达式是______。 A)DATE() + 25 B)DA TE() + TIME() C)DATE() – CTOD("06/11/2002") D)365 – DATE() 18. 执行如下命令: ?LEN(TRIM("北京市□□□")) 结果是______(其中□表示一个空格)。 A)6 B)3 C)9 D)10 19. 执行下列命令: M="ABCDabcd" ?SUBSTR(M,INT(LEN(M)/2+1),2) 输出结果是______。 A)AB B)ab C)bc D)abcd 20. Visual FoxPro数据库文件中,函数ROUND(1234.567,2)的值是______。 A)1234.57 B)1234.56 C)1235 D)1234 21. 执行下列命令后显示的结果是______。 A=CTOD("05/23/89") B=CTOD("05/03/89") ?B-A A)20 B)-20 C)“05/20/89” D)“05/-20/89” 22. 执行下列命令后显示的结果是______。 STR="VFoxPr数据库" ?SUBSTR(STR,LEN(STR)/2+1,6) A)VFoxPrB)数据库C)数据D)Pr数据 23. 执行下列命令后显示的结果是______。 X="Visual FoxPro is OK" ?AT("Fox",X) A)8B)6 C)FoxPro D)Visual 24. 函数STUFF("数据库",5,6,"管理系统")的结果是______。 A)数据库管理系统B)数据管理系统C)管理系统D)库系统 25. FoxPro数据库管理系统的数据模型是______。 A)概念型B)网状型C)关系型D)层次型 26. 下面4条叙述中,正确的叙述是______。 A)在命令窗口中被赋值的变量都是全局变量 B)在上级程序定义的变量在被调用的下级程序中无效 C)在程序中,用DECLARE定义的数组为全局变量 D)在程序中,用DIMENSION定义的数组为全局变量 27. 下面4条叙述中,正确的叙述是______。 A)在命令窗口中被赋值的变量均为局部变量 B)在命令窗口中用PRIV ATE命令说明的变量均为局部变量 C)在被调用的下级程序中用PUBLIC命令说明的变量都是全局变量 D)在程序中用PRIVA TE命令说明的变量均为全局变量 28. 下面4条叙述中,正确的叙述是______。 A)局部变量不可以与全局变量同名,但可以与上级程序中的局部变量同名 1

vfp调用word OR excel

vfp调用word OR excel(二) 10.从VFP中导出数据的几种方法! 答:当VFP处理好数据后,这时你可以用多种方法将这些数据导出,以便被其它应用程序(如Excel等)所用。下面向你简单介绍几种导出数据的方法: 1.用“导出数据”对话框。你可以从VFP主菜单中选择“文件/导出…”打开 “导出”对话框,在该对话框中的“来源”框中输入表名(c:\vfp\students.dbf ),在“类型”中指定要导出数据的类型(Microsoft Excel),在“到”框中输入 导出的路径和文件名(C:\excel\aa.xls),然后按“确定”即可。这时你如果查看command窗口,可发现以下代码: COPY TO C:\excel\aa.XLS TYPE XL5 2.用COPY TO命令。COPY TO命令的工作对象是VFP中的表,它要依据“TYPE”项才知道要产生哪一类导出文件。对于上面的导出过程,可用以下方法实现:USE students COPY TO C:\excel\aa.XLS TYPE XL5 12.在VFP6中,如何调用已经存在的WORD文件? oW = CREATEOBJECT([word.basic]) with oW .Apprestore() .Appmaximize() .fileopen("路径+文件名.扩展名") ENDWITH 13.打开word文件的控件? 用RUN /N3 C:\WORD.EXE 文档路径 C:\WORD.EXE是我自己写的,你用时换成WORD的真正路径就行了!

比如用WORD 打开SD.DOC 就写 RUN /N3 C:\WORD.EXE C:\SD.DOC 另:TXLS_EOLE = CREATEOBJECT('word.application') TXLS_EOLE.Documents.Open('文件名.doc') TXLS_EOLE.Visible=.t. 注意:别忘了关闭哦 TXLS_EOLE.Documents.close &&关闭文档 TXLS_EOLE.quit &&关闭word RELEASE TXLS_EOLE &&释放对象 再者: fw=alltrim(this.value) if file('&fw') *documents.open filename('&fw') wrdapp = createobject("word.application") wrdapp.visible=.t. *wrdapp.documents.add wrdapp.documents.open(alltrim(this.value)) else messagebox(fw+chr(13)+'文件不存在,请确认',0+16,'文件路径出错') RETURN(.F.) ENDIF 14.在表单中创建一个编辑框,用来显示WORD文档! 做一个按扭,在CLICK中写:THISFORM.EDIT1.value=FILETOSTR(GETFILE('DOC')) 这个不也行,而且还简单 15.强烈建议:直接使用ole可以在表单中打开word,建议不使用这种办法,改为利用api 用windows默认的程序打开word,数据库只存储word文档所在的位置和名字。

vfp中地常用函数与命令

VFP常用的命令和函数 1.数据库操作语言 使用可以CLOSE 命令关闭各种类型的文件。 DBC( ) 函数返回当前数据库的名称和路径。SQLCANCEL( ) 函数请求取消一条正在执行的 SQL 语句。SQLCOMMIT( ) 函数提交一个事务处理。SQLCONNECT( ) 函数建立一个对数据源的连接。SQLDISCONNECT( ) 函数结束一个对数据源的连接。SQLEXEC( ) 函数发送一条 SQL 语句到数据源中处理。SQLGETPROP( ) 函数获取一个活动连接的当前设置。 SQLMORERESULTS( ) 函数如果存在多个可用的结果集,则将另一个结果集复制到Visual FoxPro 临时表(cursor)中。 SQLROLLBACK( ) 函数取消当前事务处理期间所做的任何更改。SQLSETPROP( ) 函数指定一个活动连接的设置。SQLSTRINGCONNECT( ) 函数使用一个连接字符串建立与数据源的连接。TABLEREVERT( ) 函数放弃对缓冲行、缓冲表或临时表(cursor)的修改。 TABLEUPDATE( ) 函数提交对缓冲行、缓冲表或临时表(cursor)适配器的修改。 字段操作语言 使用可以BLANK 命令清除字段中的数据。 DELETED( ) 函数指出一个记录是否被标记为删除。FCOUNT( ) 函数返回一个表中的字段数量。 FIELD( ) 函数根据编号返回表中的字段名。 GATHER 命令用来自指定源的数据替换记录中的数据。SCATTER 命令从当前记录中复制数据到指定的目标。SET EXACT 命令指定比较不同长度两个串时的规则。

VFP中调用其他应用程序的方法

VFP中调用其他应用程序的方法 在用VFP进行应用程序设计中,经常需要调用其他的应用程序。这时候我们立即会想到RUN命令。使用RUN命令只需知道应用程序的文件名及其路径即可。 下面的示例是以最大化方式启动Windows目录下的图画板程序并同时打开c:\windows\Bubbles.bmp文件: RUN/n3 C:\Windows\Pbrush.exe c:\windows\Bubbles.bmp RUN命令的参数用法可以参考Visual FoxPro的帮助文件。 但是令人失望的是,在实际开发时我们常常无法确定所需要启动的应用程序的文件名及其安装的路径。比如我们需要启动用户机器上的浏览器并连接到某一个网址,该机器可能安装的是IE,也可能是NetScape,还有可能是其他的浏览器软件,安装的路径更是无从确定。下面给出我在工作中找到的两种解决方法,这两种方法都是通过Win32 API函数来解决问题,因此其解决思路也可以用到其他编程语言中去。 (一)从注册表中获取被调用软件的名称及路径。 我们只需要提供被调用的软件所处理的文件类型(由文件扩展名来确定),即可通过查找注册表中该软件注册的可以处理的文件类型的数据项来得到该软件的文件名及安装路径。 *主函数 PROCEDURE lookreg

Parameter filetype && 文件类型 * 申明要用到的API 函数 DECLARE Integer RegOpenKey IN Win32API ; Integer nHKey, String @cSubKey, Integer @nResult DECLARE Integer RegCloseKey IN Win32API ; Integer nHKey DECLARE Integer RegQueryvalueEx IN Win32API ; Integer nHKey, String lpszvalueName, Integer dwReserved,; Integer @lpdwType, String @lpbData, Integer @lpcbData #DEFINE HKEY_CLASSES_ROOT -2147483648 #DEFINE C_EXTNOFOUND_LOC "查找失败!" LOCAL cExtn,cAppKey,cAppName,nErrNum LOCAL oReg,regfile cAppKey = "" cAppName = "" nCurrentKey = 0 nErrNum =GetAppPath (filetype,@cAppKey,@cAppName)

VFP命令、函数及程序语句大全

VFP命令、函数及程序语句大全.txt16生活,就是面对现实微笑,就是越过障碍注视未来;生活,就是用心灵之剪,在人生之路上裁出叶绿的枝头;生活,就是面对困惑或黑暗时,灵魂深处燃起豆大却明亮且微笑的灯展。17过去与未来,都离自己很遥远,关键是抓住现在,抓住当前。VFP命令、函数及程序语句大全(1) 学过VFP(Microsoft Visual FoxPro)的朋友都知道它是微软公司开发的数据库管理系统。它 经历了从dBASE、FoxBASE、FoxPro直到VFP的发展过程。本文便以VFP 6.0为例介绍它的命令、函数 及程序语句,为学习VFP的朋友提供必要的学习参考,也为已经精通VFP的朋友提供复习的好机会。 一、主要命令: 1、CREATE 作用:建立一个新的表。 格式:CREATE [<文件>|?](注意,命令字符可取前面四个字符,后面可省略不写,即CREA,下 同;中括号表示其中的参数是可选的;|管道符号表示两个参数只能选择一个,不能同时选择,下 同。) 说明:文件指建立以.dbf为扩展名的VFP数据库文件,在命令后面加上一个问号表示系统会弹出 对话框,要求用户输入想要建立的表名。在命令中不加文件名或问号系统也会弹出对话框,要求用 户输入想要建立的表名。 2、MODIFY STRUCTURE 作用:显示表文件结构,并允许修改此表的结构。 格式:MODIFY STRUCTURE 说明:只有在用USE命令打开表文件以后,才能显示或修改表文件的结构。 3、APPEND 作用:在当前表的尾部(无论表中有无记录)追加记录(在当前表指当前正使用的表)。 格式:APPEND [BLANK] 说明:在APPEND命令后面加上BLANK参数表示在当前表的尾部添加一条空白记录。无BLANK 参数 时,表示在当前表的尾部添加一条记录并要求用户输入相应的字段内容。 4、INSERT 作用:在表文件中间插入一个新记录。 格式:INSERT [BEFORE] [BLANK] 说明:INSERT 在当前记录后插入一记录; INSERT BEFORE 在当前记录前插入一记录; INSERT BEFORE BLANK 在当前记录前插入一空记录。 INSERT - SQL命令追加记录 INSERT INTO 表名[(字段1[,字段2…])];

VFP常用命令详解

VFP常用命令详解 VFP命令、函数及程序语句大全(1) VFP命令是一种实现对数据库系统进行操作的动词或短语。VFP命令可在VFP命令窗口中单独运行,也可编制成程序成批运行,由命令和函数编制的程序可对数据库系统进行自动、复杂的操作,使数据库系统应用起来更加友好、方便。 ?VFP常用命令分类表 类别命令 建立文件类CREATE,MODIFY,COPY,SAVE等 数据库操作类USE,CLOSE,COPY,MODIFY STRUCTURE等 数据编辑类BROWSE,EDIT,CHANG,REPLACE,UPDATE等 记录指针操作类GOTO,LOCATE,SEEK,FIND,SKIP等 记录操作类APPEND,DELETE,INSERT等 数据显示类BROWSE,LIST,DISPLAY等 数据统计类SUM,COUNT,AVERAGE,TOTAL等 存变量操作类ACCEPT,INPUT,READ,SAVE等 程序控制类DO,DO WHILE…ENDDO,IF…ENDIF等 系统设置类SET TALK,SET DEFAULT TO等 ?命令格式 VFP命令一般由命令动词和控制短语构成。 一般格式: 命令动词[FROM<源文件>][TO<目标文件>][记录范围][FOR/WHILE<条件>] [FIELDS<字段名表>] [ ]表示可选,<>表示必选, / 表示或选

例如: (1)APPEND FROM C:\RSDA.DBF FOR 基本工资>100 FIELDS 编号,姓名,职称,基本工资。 从C:\RSDA.DBF 表中把基本工资大于100元的记录添加到当前表添加字段为:编号,姓名,职称,基本工资。 (2)COUNT FOR 职称="高工"TO GG 统计高工的记录数到内存变量GG。 控制短语规定了对数据库操作的限制条件: 1.指向输入输出目标 FROM/TO APPEND FROM,COPY FILE TO <目标文件> 2.限制记录范围(SCOPE) ALL 所有记录 NEXT 本记录后的第几个记录 RECORD<1> 第1个记录 REST 当前记录之后所有记录 3.选择字段 FIELDS<字段表>选择输入/输出的字段。 4.限制条件 FOR/WHILE<条件> FOR 基本工资>100 .AND.基本工资<200 命令格式规则 命令的行为单位,一行书写不完,在未尾加上“;”第二行接着写。

vfp函数大全

%运算符用于计算并返回两个数值表达式相除之后的余数 $(包含)运算符用于进行字符表达式之间的包含关系运算,如果一个字符表达式包含在另一个字符表达式之中,则函数返回真 ABS() 计算并返回指定数值表达式的绝对值 ACLASS() 用于将一个对象的父类名放置于一个内存数组中 ACOPY() 把一个数组的元素拷贝到另一个数组中 ACOS() 计算并返回一个指定数值表达式的余弦值 ADATABASES() 用于将所有打开的数据库名和它的路径存入一个内在变量数组中ADBOBJECTS() 用于把当前数据库中的连接、表或SQL视图的名存入内存变量数组中ADEL() 用于从一维数据中删除一个元素,或从二维数组中删除一行或者一列元素 ADIR() 将文件的有关信息存入指定的数组中,然后返回文件数 AELEMENT() 通过元素的下标,返回元素号 AFIELDS 将当前的结构信息存入数组中,然后返回表中的字段数 AFONT() 将可用字体的信息存入数组中 AERROR() 用于创建包含VFP或ODBC错误信息的内存变量 AINS() 在一维数组中插入一个元素或在二维数组中插入一行或一列元素 AINSTANCE() 用于将类的所有实例存入内存变量数组中,然后返回数组中存放的实例数ALEN() 返回数组中元素、行或者列数 ALIAS() 返回当前工作区或指定工作区内表的别名 ALLTRIM() 从指定字符表达式的首尾两端删除前导和尾随的空格字符,然后返回截去空格后的字符串 AMEMBERS() 用于将对象的属性、过程和成员对象存入内存变量数组中 ANSITOOEM() 将指定字符表达式中的每个字符转换为MS-DOS(OEM)字符集中对应字符APRINTERS() 将Print Manager中安装的当前打印机名存入内存变量数组中 ASC() 用于返回指定字符表达式中最左字符的ASCII码值 ASCAN() 搜索一个指定的数组,寻找一个与表达式中数据和数据类型相同的数组元素ASELOBJ() 将活动的Form设计器当前控件的对象引用存储到内存变量数组中 ASIN() 计算并返回指定数值表达式反正弦值 ASORT() 按升序或降序排列数组中的元素 ASUB script () 计算并返回指定元素号的行或者列坐标 AT() 寻找字符串或备注字段在另一字符串或备注字段中的第一次出现,并返回位置 ATAN() 计算并返回指定数值表达式的反正切值 ATC() 寻找字符串或备注字段中的第一次出现,并返回位置,将不考虑表达式中字母的大小写 ATCLINE() 寻找并返回一个字符串表达式或备注字段在另一字符表达式或备注字段中第一次出现的行号。不区分字符大小写 ATLINE() 寻找并返回一个字符表达式或备注字段在另一字符表达式或备注字段中第一次出现的行号 ATN2() 根据指定的值返回所有4个像限内的反正切值 AUSED() 用于将一次会话期间的所有表别名和工作区存入变量数组之中 BAR() 从用DEFINE POPUP命令定义的菜单中返回最近所选择的菜单项的编号,或返回一个从VFP菜单所选择的一个菜单命令 BARCOUNT() 返回DEFINE POPUP命令所定义的菜单中的菜单项数,或返回VFP系统菜单上的菜单项数

VFP程序设计典型例题汇总

Visual FxoPro程序设计题 1.编写程序,从键盘输入一个日期,查询STUDENT表中该日期以后出生的学生信息并显示在浏览窗口中。 use student &&运行前单击教学管理数据库 input “请输入某日期:” to r q *输入时有提示,或input to rq输入时无提示,输{^1987-01-01} browse for 出生日期>rq 2.有一函数: x (x<1) y= 2x-1 (1≤x<10) 3x-11 (x≥10) 编写程序,输入x,输出y的值。 clear input "Enter x please!" to x if x<1 y=x else if x<10 y=2*x-1 else y=3*x-11 endif endif 'x=',x,'y=',y 3.编写程序,从键盘输入一个年份,判断它是否闰年。 clear input "Enter year number please!" to y if y%4=0 and y%100<>0 or y%400=0 y,"is a leap year!" else y,"is not leap year." Endif

4.编写程序,输入一个百分制成绩,要求输出成绩等级“A”、“B”、“C”、“D”、“E”。90分以上为“A”,80~89分为“B”,70~79分为“C”,60~69分为“D”,60分以下为“E”。 clear input “请输入一百分制成绩” to cj do case case cj>=90 ”A” case cj>=80 ”B” case cj>=70 ”C” case cj>=60 ”D” otherwise ”E” endcase 5.编写程序,从键盘输入一个正整数M,输出M内(包括M)所有偶数。 clear input to m for i=0 to m step 2 str(i,5) Endfor 6.编写程序,从键盘输入一个正整数M,计算M内(包括M)所有偶数之和。 clear input to m s=0 for i=2 to m step 2 s=s+i Endfor s

VFP实验 过程文件与自定义函数

实验4-4 过程文件与自定义函数 一、实验目的 加强对过程文件与自定义函数概念的理解,掌握过程文件与自定义函数在程序设计中的运用,并掌握程序之间数据传递的方法。 二、实验准备 复习教材中有关子程序与自定义函数的内容,重点掌握子程序、过程与过程文件、自定义函数等概念,理解本节给出的实例。 三、实验内容 1.用过程文件的形式编写程序,求1!+3!+5!+ + n!的值,n从键盘输入,阶乘计算用过程。 *1.用过程文件的形式编写程序,求1!+ 3!+ 5!+ + n!的值,n从键盘输入,阶乘计算用过程。 SET TALK OFF CLEAR INPUT "n=" TO n

S=0 Q="" FOR J=1 TO n STEP 2 T=1 DO jc WITH J S=S+T Q=Q+ALLTRIM(STR(J,19))+"!+" ENDFOR ?LEFT(Q,LEN(Q)-1)+"="+ALLTRIM(STR(S,19,2)) SET TALK ON PROCEDURE JC PARAMETERS X FOR I=1 TO X T=T*I ENDFOR RETURN ENDPROC 2.编写自定义函数,判断X是否是素数,返回结果为逻辑值.T. 或 .F.。 *2.编写自定义函数,判断X是否是素数,返回结果为逻辑值.T. 或 .F.。CLEAR PROCEDURE SS

PARAMETERS Q FOR N=2 TO Q-1 IF INT(Q/N)=Q/N EXIT ENDIF ENDFOR IF N>Q-1 RETURN .T. ELSE RETURN .F. ENDIF ENDPROC 3.编写程序,要求利用参数传递和过程文件求解:(m! + n!) ((m - n)!+ m^n + m^m )的值。M,N从键盘上输入且M>N,要求使用子程序和过程文件两种方式实现。 *3.编写程序,要求利用参数传递和过程文件求解:(m! + n!) ((m - n)!+ m^n + m^m )的值。 *M,N从键盘上输入且M>N,要求使用子程序和过程文件两种方式实现。

vfp编程实例

1、1-100的累加 2、阶乘 3、求阶乘和 4、条件定位 5、用scan语句查询定位 6、用scan语句扫描符合条件的记录,并显示出来 7、查找男性职工 ** 查找男性职工** 查找男性职工 8、新值换旧值 9、多位整数各个位数相加 10、找100-999之间的“水仙花数” 11、判断一个大于3的数是否为素数 12、求一个班级每个学生的平均成绩 13、计算圆的面积 14、将小写字母转换为大写字母 15、找出最大数和最小数 16、绝对值函数的实现 17、绝对值函数实现的另一种方法 18、输出成绩等级 19、求一名学生的平均成绩 20、用数组的方法,计算一个学生的平均成绩 21、用do case语句实现符号函数的功能 22、用一般选择语句实现符号函数的功能 23、用简单选择语句实现符号函数功能 24、打开数据库和数据表 25、显示表中指定信息 26、询问后再由用户按(Y/N)键执行的程序实现 27、根据exact的设置进行字符串匹配比较 28、使一个多位正整数逆序排列 29、按编号查找并列出一条记录 30、验证密码程序: 31、编程求税金 32、用三角形列出乘法口诀表: 33、将变量A、B值互换: 34、找出已知三个数的中间数: 35、调用子程序: 36、用参数传递计算圆的面积: 37、我国古代数学家张丘建

VFP常见编程实例 1、1-100的累加 ** 求1到100的累加值 CLEAR s=0 i=1 DO WHILE i<=100 s=s+i i=i+1 &&一定记住先累加后循环ENDDO ?s RETURN 2、阶乘 ** 求5的阶乘 CLEAR j=1 i=1 DO WHILE i<=5 j=j*i

VFP与文件操作相关的函数

VFP与文件操作相关的函数 VFP提供了一些对文件进行低级操作的函数,这些函数的功能及用法为: (1)GETFILE()函数。 格式:GETFILE([])。 功能:显示“打开”对话框,供用户选定一个文件并返回文件名。其中用于指定文件的扩展名。 例如,若显示一文件打开对话框,允许用户选择打开一个扩展名为.jpg类型的图形文件,则调用方法为: myfile=getfile("jpg") 若要允许用户选择的文件类型有多种,此时可用分号将各文件扩展名进行分隔,例如,若允许用户选择打开jpg和gif类型的文件,则调用方法为: myfile=getfile("jpg;gif") (2)PUTFILE()函数。 格式:PUTFILE([])。 功能:显示“另存为”对话框,供用户指定一个文件名并返回文件名。其中用于指定文件的扩展名。 (3) FOPEN()函数。 格式:FOPEN(<文件名>)。 功能:打开指定文件,返回文件句柄(控制号)。 (4) FCREATE()函数。 格式:FCREATE(<文件名>)。 功能:建立一个新文件,返回文件句柄(控制号)。 (5) FCLOSE()函数。 格式:FCLOSE(<文件句柄>)。 功能:将文件缓冲区的内容写入文件句柄所指定的文件中,并关闭该文件。 (6) FREAD()函数。 格式:FREAD(<文件句柄>,<字节数>)。 功能:从文件句柄所指定的文件中读取指定字节数的字符数据。 (7)FWRITE()函数。 格式:FWRITE(<文件句柄>,)。 功能:把表示的数据写入文件句柄所指定的文件中。 (8) FSEEK()函数。 格式:FSEEK(<文件句柄>,<移动字节数>[,])。 功能:在文件句柄所指定的打开的文件中移动文件指针,其中n表示移动的方式或方向: n=0为向文件首移动,n=1为相对位置移动,n=2为向文件尾移动。

如何在VFP中调用默认关联的程序打开文件

如何在VFP中调用默认关联的程序打开文件? (2007-01-20 17:13:12) 转载 分类:『数据库-DB』 如何在VFP中调用默认关联的程序打开文件? 怎样在一个菜单中建立一个程序或命令打开一个网页或word,excel等文件??? --------------------------------------------------------------- 打开一个网页与word,excel是不一样. 1.word,excel要用ACTIVEX的OLECONTROL控件. 2.打开一个网页要用到HYPERLINK控件. 可以在表单上加一个TEXT一个COMMANG控件,还有一个超级链接HYPERLINK控件. COMMAND.CLICK事件: THISFORM.HYPERLINK.NA VIGATETO(THISFORM.TEXT.V ALUE) 在vfp的例子中找到了一个这个问题的解决方法,使用api函数来解决,如下: 1、声明函数 DECLARE INTEGER ShellExecute ; IN SHELL32.DLL ; INTEGER nWinHandle,; STRING cOperation,; STRING cFileName,; STRING cParameters,; STRING cDirectory,; INTEGER nShowWindow 2、在程序中任何地方调用,包括菜单中,打开一个e:\a.htm文件的调用方法如下, =ShellExecute(0,"Open","e:\a.htm","","e:\",1) --------------------------------------------------------------- 在VFP中我们可以用run来调用由VFP自身生成的.exe文件。命令格式: run c:\..\*.exe 当调用非VFP自身生成的.exe文件时,需要加上绝对路径和参数。 一般来说,用VFP运行非VFP自身生成的可执行文件时,会有一个黑色的窗口显示一下。可以通过加上/n来隐藏这个窗口。例如运行Windows下的扫雷游戏,命令格式如下:RUN/n C:\Windows\Winmine.exe 下面运行一个更复杂一点的程序。用word打开c:\下的一个名为mlx.doc的word文件,命令格式如下: run /n3 C:\Program Files\Microsoft Office\Office\Winword.exe C:\mlx.doc 参数:n 在运行时隐藏VFP的黑色窗口。 3 表示以最大化的方式运行程序。0隐藏窗口、1正常方式、2最小化方式、3最大化方式在这种情况下,虽然可以通过加上参数使程序程序能够流畅运行,但还是要指定绝对路径,

1-精选VFP常见编程实例-答案

精选VFP常见编程实例-答案 1、1-100的累加 求1到100的累加值 CLEAR s=0 i=1 DO WHILE i<=100 s=s+i i=i+1 &&一定记住先累加后循环ENDDO ?s RETURN 2、阶乘 求5的阶乘 CLEAR j=1 i=1 DO WHILE i<=5 j=j*i i=i+1 ENDDO ?j RETURN 3、求阶乘和 求1至5的阶乘和 CLEAR s=0 i=1 j=1 DO WHILE i<=5 j=j*i s=s+j i=i+1 ENDDO ?s RETURN 4、条件定位 查询定位记录指针在赵伟上CLEAR USE 学生 LOCATE FOR 姓名="赵伟"

IF FOUND() DISPLAY ELSE CANCEL ENDIF CONTINUE 5、用scan语句查询定位 用scan语句查询定位记录在赵伟上 CLEAR SCAN FOR 姓名="赵伟" DISPLAY ENDSCAN RETURN 6、用scan语句扫描符合条件的记录,并显示出来把年龄小于30岁的人显示出来 CLEAR SCAN FOR 年龄<30 DISPLAY ENDSCAN RETURN 7、查找男性职工 查找男性职工 CLEAR LOCATE FOR 性别="男" DO WHILE FOUND() DISPLAY CONTINUE ENDDO RETURN 查找男性职工 CLEAR LOCATE FOR 性别="男" DO WHILE NOT EOF() DISPLAY CONTINUE ENDDO RETURN 查找男性职工 CLEAR

VFP调用Windows API函数

VFP调用Windows API函数 正式开始: 我想你可能经常看过如下这种语句,我不得不在这里重复地说一遍 DECLARE INTEGER SetWindowText IN user32; INTEGER hWnd,; STRING lpString Hwnd=thisform.hwnd customtext=space(250) lnlen=len(customtext) SetWindowText(Hwnd,customtext, lnlen) 第一个语句就是一般的api定义,意思就是注册一个user32.dll动态库中的函数SetWindowText ,接受参数integer类型hwnd, string 类型h的sWindowText 参数,,返回值为intger ,这是参数按值传递的一部分,第三个语句就是调用注册的api函数了,看起来和我们用普通的foxpro函数并无区别,只是多了一步注册的步骤. 我们再看看另一种定义,按引用传递的的方式,看到什么不同了吗? 第一个语句就是一般的api定义,意思就是注册一个user32.dll动态库中的函数SetWindowText ,接受的参数是integer类型hwnd, string 类型h的sWindowText , INTEGER 类型的cch,返回值为intger类型 DECLARE INTEGER GetWindowText IN user32; INTEGER hwnd,; STRING @lpString,; INTEGER cch Hwnd=thisform.hwnd Hwnd=thisform.hwnd Stext =space(250) lnlen=250 SetWindowText(Hwnd,@customtext, lnlen) 没错,多了一个伊妹儿的符号:@,注意调用的时候也得加入该符号,此符号的意思是说参数sWidowText是按引用传递的,为什么要这么用呢,原因是我们要用函数GetwindowText 的得到一个窗体(Form)标 题.GetwindowText 的职责就是将我们要的值填入sWidowText中,如果调用成功你就会看到sWidowText的值已经改变.此处你要明白所谓的按引用传递有如C中的指针,我们只是该内容的地址为参数传给GetwindowText,它直接在地址指向的内存单元上修改数据.所以我们才可能看到sWidowText值在调用GetwindowText()函数就发生了改变。 以上两种就是最基本的windows api定义了.对于简单的函数一般都可以这样解决. 也许此时你有疑问,你是在哪里知道这些函数的详细信息的?很好很好,有问题是好的.那么请你打开msdn,什么?你不知道MSDN,我倒..^_^简单而说msdn是涵盖了大量内容的帮助文档,大部分的api在其中都有讲述,有些还有小实例.在这里并不要求你要记住所有windows的api函数,这不可行也不可能,只要知道有它的存在,大概的功能,用时查msdn就可以,更重要的是知道怎么去把它纠出来,然后理解它. 如果你现在没有安装MSDN,我强烈建议你马上下载一个msdn2005.该帮助文档大部分都是E文,你得有点心理准备.

相关文档
最新文档