VB 中使用Unicode API速度快几倍

介绍

  也许大家都知道,Visual Basic 内部的所有和字符串相关的函数使用的都是 Unicode

   Unicode 是一套字符集。与其他传统单一字节的字符集不同的是,它使用两个字节来表示一个字符,这使得可用字符的数量大大增加(理论上说,一个字节可以包含最多 256 个字符,而两个字节可以包含 65536 个字符)。在这里请注意,不要把 GB2312 之类的双字节字符集和 Unicode 混淆:前者既有单字节字符(如英文)也有双字节字符(如中文),这样使得管理十分麻烦,并且它只支持一种代码页;而 Unicode 的字符都是双字节的,使得管理、转换和使用字符串变得十分容易。并且它支持世界上的所有的常用文字,使得一个程序可以同时在屏幕上显示多种语言的文字,而不用关心当前的代码页。

  好了在简单地介绍了 Unicode 之后,现在来介绍 Unicode API(什么?不知道什么是 API ?(汗)如果是那样的话这篇文章可能不太适合你)。

  Windows NT 从一开始就在其内部使用 Unicode ,以至于其后续产品(Windows 2000、XP)都一直沿用它。而 Windows 95、98和 ME 就没有那么幸运,它们都一直使用单(双)字节的字符集。这样就使得同一个有关字符串的 API 有两个不同字符集的版本: Unicode 版和非 Unicode 版。在函数名上, Unicode 版的 API 具有一个 'W' 后缀代表 wide ,如:MessageBoxW;而非 Unicode 版的 API 具有一个 'A' 后缀代表 ANSI ,如:MessageBoxA。
  在 Windows NT/2000/XP 上这两种版本的 API 都有,也就是说 Windows NT 支持 Unicode 和非 Unicode 字符集。而在 Windows 95/98/ME 上几乎所有 API 都只有其非 Unicode 版本,意味着它们之支持单(双)字节字符集。


VB 和 Unicode API

  现在言归正传。 VB 在使用了 Unicode 之后,可以享受到 Unicode 所带来的各种好处。不过,麻烦也随之而来了……
  因为 Windows 95/98/ME 不支持 Unicode ,而 VB 只支持 Unicode ,所以当从 VB 里调用一个非 Unicode API 时, VB 先要把所有字符串都转换成非 Unicode 字符串,然后调用 API,最后把所有字符串再转换回 Unicode(见图1)。这样使得 API 的调用速度变得十分缓慢,而且效率很低。


┌─────────┐  ┌──────────────┐  ┌────────┐
│         │->│转换为非 Unicode(A)│->│        │
│         │  └──────────────┘  │  API(A) │
│  VB程序(W) │                    │        │
│         │  ┌─────────────┐   └────────┘
│         │<-│转换为 Unicode(W)│<───────┘   
└─────────┘  └─────────────┘          
图1:调用非 Unicode API :需要转换来转换去


  看到这里,也许有的人会问了“那为什么不用 Unicode API 呢?”在 API 查看器中的 API 都是非 Unicode 的(Alias 以 'A' 结尾),是因为兼容性问题。要让程序在 Windows 9x 和 NT 里都能执行,就必须要调用非 Unicode 的 API ,要不然的话会很麻烦;不过这是以性能为代价的。
  如果程序需要高性能,经常使用需要传递字符串为参数的 API,而且只运行在 Windows NT/2000/XP 环境下的话(如服务器程序),完全可以用 Unicode API 来替换效率超低的非 Unicode API (见图2)。


┌─────────┐                    ┌────────┐
│         │───────────────────>│        │
│         │                    │  API(W) │
│  VB程序(W) │                    │        │
│         │                    └────────┘
│         │<-───────────────────────┘   
└─────────┘                           
图2:调用 Unicode API :不需要转换



实践

对于刚接触 API 的程序员来说,知道怎样把现有的非 Unicode API 声明转化为 Unicode API 声明就已经足够了:


以下是一个简单的 API 声明:(作用是获得一个窗体的标题)

Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
'GetWindowTextA' 指明这是一个非 Unicode API

以下是修改后的 API 声明:

Declare Function GetWindowText Lib "user32" Alias "GetWindowTextW" (ByVal hwnd As Long, ByVal lpString As Long, ByVal cch As Long) As Long
'GetWindowTextW' 指明这是一个 Unicode API

由此可见,修改一个非 Unicode API 成为一个 Unicode API 的要点是:
    * 把 Alias 里函数名的结尾 'A' 换成 'W'。
    * 把所有 ByVal *** As String ,改为 ByVal *** As Long 。


以上是声明方法,以下是调用方法:


原来的调用方法:  rc = GetWindowText(hwnd, str, strlen)

修改后的调用方法: rc = GetWindowText(hwnd, StrPtr(str), strlen)


由此可见,调用一个 Unicode API 的要点是把原来应为  字符串变量/常量  的地方改为  StrPtr(字符串变量/常量) 。



对于接触 API 有一段时间的程序员来说,有必要了解以上声明方法和调用方法的原理。


首先,把 Alias 里函数名的结尾 'A' 换成 'W' 是因为我们需要调用 Windows NT/2000/XP Unicode 版本的 API 。

其次先介绍一下 VB 可变长度字符串变量。 VB 可变长度字符串变量其实是一个指向其 Unicode 字符串的指针(见图3)。


┌───────┐    ┌───────┐
│  ...  │    │  ...  │
│┈┈┈┈┈┈┈│    │┈┈┈┈┈┈┈│
│  变量a  │    │ 字符串长度 │
│┈┈┈┈┈┈┈│    │┈┈┈┈┈┈┈│
│  变量b  │  ->│  字符1  │
│┈┈┈┈┈┈┈│ /  │┈┈┈┈┈┈┈│
│ 字符串变量 │─   │  字符2  │
│┈┈┈┈┈┈┈│    │┈┈┈┈┈┈┈│
│  变量c  │    │  字符3  │
│┈┈┈┈┈┈┈│    │┈┈┈┈┈┈┈│
│  变量d  │    │  字符4  │
│┈┈┈┈┈┈┈│    │┈┈┈┈┈┈┈│
│  ...  │    │  ...  │
└───────┘    └───────┘
图3: VB 可变长度字符串变量


把所有 ByVal *** As String ,改为 ByVal *** As Long 是因为我们要传递 VB Unicode 字符串的地址,而不是字符串转换成 ANSI 后的地址。

把原来应为  字符串变量/常量  的地方改为  StrPtr(字符串变量/常量) 是因为我们可以通过 StrPtr 函数来获得 Unicode 字符串的地址(既图3中‘字符1’的地址)。


例子

  在知道怎样转换和调用 Unicode API 之后,让我们来看一个例子:

' 在 VB 中使用 Unicode API
' 选择 lstrcpy 这个函数完全是因为所有除了 Windows 95 的 Windows 系统都支持它的 Unicode 版本,包括 WIndows 98 和 ME。
' 使得大家可以在任何 WIndows 95 以上的系统中都可以调试此例子。(事实上, Windows 95/98/ME 只支持大约 16 个 API 的 Unicode 版本。)

' 此程序只是简单地用 API 来复制一个字符串到另一个字符串。该程序只需要一个 Module 就足够了。请务必把工程的启动窗口设为 Sub Main() 。

Option Explicit

' lstrcpy API 的作用是复制一个字符串到另一个字符串。 Unicode 和非 Unicode 版本分别为 lstrcpyW 和 lstrcpyA 。
Private Declare Function lstrcpyA Lib "kernel32" (ByVal lpString1 As String, ByVal lpString2 As String) As Long
Private Declare Function lstrcpyW Lib "kernel32" (ByVal lpString1 As Long, ByVal lpString2 As Long) As Long
' 用 timeGetTime API 来计算时间。
Private Declare Function timeGetTime Lib "winmm.dll" () As Long
Private Const COPY_TIMES = 10000& ' 要复制的次数。

Sub Main()
  Dim stra As String, strw As String, strx As String, i As Long
  Dim tm As Long, t1 As Long, t2 As Long

  strx = "Windows XP" ' 要复制的字符串。
  stra = "__________" ' 要用 lstrcpyA 复制到的字符串。
  strw = "__________" ' 要用 lstrcpyW 复制到的字符串。
  
  tm = timeGetTime
  For i = 1& To COPY_TIMES ' 用 lstrcpyA 复制 COPY_TIMES 次。
    lstrcpyA stra, strx
  Next
  t1 = timeGetTime - tm
  
  tm = timeGetTime
  For i = 1& To COPY_TIMES ' 用 lstrcpyW 复制 COPY_TIMES 次。
    lstrcpyW StrPtr(strw), StrPtr(strx)
  Next
  t2 = timeGetTime - tm
  
  MsgBox "ANSI:    复制 '" & stra & "' " & CStr(COPY_TIMES) & " 次用了 " & CStr(t1) & " 毫秒" & VBCrLf & _
         " Unicode: 复制 '" & strw & "' " & CStr(COPY_TIMES) & " 次用了 " & CStr(t2) & " 毫秒" ' 显示结果
End Sub

' 以上程序在 Windows 98 Second Edition + Visual Basic 6 Professional Service Pack 5 下调试通过。
' 结果:非 Unicode API 的运行时间大概是 Unicode API 的 15 倍左右。


结束语

  在 VB 中使用 Unicode API 能够显著提高应用程序用 API 处理字符串的效率。
  用此方法唯一不足的是兼容性问题。不过,微软已经停止了继续开发 Windows 95 系列操作系统,意味着其以后推出的操作系统都将基于 Windows NT 技术,也意味着它们都将支持 Unicode 。
  换句话说,使用 Unicode API 将成为一项大家都要掌握的技术(其实不应该叫“技术”,因为这很简单)。

标题“51单片机通过MPU6050-DMP获取姿态角例程”解析 “51单片机通过MPU6050-DMP获取姿态角例程”是一个基于51系列单片机(一种常见的8位微控制器)的程序示例,用于读取MPU6050传感器的数据,并通过其内置的数字运动处理器(DMP)计算设备的姿态角(如倾斜角度、旋转角度等)。MPU6050是一款集成三轴加速度计和三轴陀螺仪的六自由度传感器,广泛应用于运动控制和姿态检测领域。该例程利用MPU6050的DMP功能,由DMP处理复杂的运动学算法,例如姿态融合,将加速度计和陀螺仪的数据进行整合,从而提供稳定且实时的姿态估计,减轻主控MCU的计算负担。最终,姿态角数据通过LCD1602显示屏以字符形式可视化展示,为用户提供直观的反馈。 从标签“51单片机 6050”可知,该项目主要涉及51单片机和MPU6050传感器这两个关键硬件组件。51单片机基于8051内核,因编程简单、成本低而被广泛应用;MPU6050作为惯性测量单元(IMU),可测量设备的线性和角速度。文件名“51-DMP-NET”可能表示这是一个与51单片机及DMP相关的网络资源或代码库,其中可能包含C语言等适合51单片机的编程语言的源代码、配置文件、用户手册、示例程序,以及可能的调试工具或IDE项目文件。 实现该项目需以下步骤:首先是硬件连接,将51单片机与MPU6050通过I2C接口正确连接,同时将LCD1602连接到51单片机的串行数据线和控制线上;接着是初始化设置,配置51单片机的I/O端口,初始化I2C通信协议,设置MPU6050的工作模式和数据输出速率;然后是DMP配置,启用MPU6050的DMP功能,加载预编译的DMP固件,并设置DMP输出数据的中断;之后是数据读取,通过中断服务程序从DMP接收姿态角数据,数据通常以四元数或欧拉角形式呈现;再接着是数据显示,将姿态角数据转换为可读的度数格
MathorCup高校数学建模挑战赛是一项旨在提升学生数学应用、创新和团队协作能力的年度竞赛。参赛团队需在规定时间内解决实际问题,运用数学建模方法进行分析并提出解决方案。2021年第十一届比赛的D题就是一个典型例子。 MATLAB是解决这类问题的常用工具。它是一款强大的数值计算和编程软件,广泛应用于数学建模、数据分析和科学计算。MATLAB拥有丰富的函数库,涵盖线性代数、统计分析、优化算法、信号处理等多种数学操作,方便参赛者构建模型和实现算法。 在提供的文件列表中,有几个关键文件: d题论文(1).docx:这可能是参赛队伍对D题的解答报告,详细记录了他们对问题的理解、建模过程、求解方法和结果分析。 D_1.m、ratio.m、importfile.m、Untitled.m、changf.m、pailiezuhe.m、huitu.m:这些是MATLAB源代码文件,每个文件可能对应一个特定的计算步骤或功能。例如: D_1.m 可能是主要的建模代码; ratio.m 可能用于计算某种比例或比率; importfile.m 可能用于导入数据; Untitled.m 可能是未命名的脚本,包含临时或测试代码; changf.m 可能涉及函数变换; pailiezuhe.m 可能与矩阵的排列组合相关; huitu.m 可能用于绘制回路图或流程图。 matlab111.mat:这是一个MATLAB数据文件,存储了变量或矩阵等数据,可能用于后续计算或分析。 D-date.mat:这个文件可能包含与D题相关的特定日期数据,或是模拟过程中用到的时间序列数据。 从这些文件可以推测,参赛队伍可能利用MATLAB完成了数据预处理、模型构建、数值模拟和结果可视化等一系列工作。然而,具体的建模细节和解决方案需要查看解压后的文件内容才能深入了解。 在数学建模过程中,团队需深入理解问题本质,选择合适的数学模
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

专注VB编程开发20年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值