自定义函数

假定你想要为Excel编写自定义函数,但是这个自定义函数用vba写起来比较麻烦,还有一种做法就是编写一个dll。我们准备用熟悉的C/C++来编写dll,此时我们打开VS,写下了一个自定义函数Foo,头文件定义如下

#ifndef TEST_H
#define TEST_H#pragma once#include <Windows.h>#ifdef TESTDLL_EXPORT
# define TESTDLL_API __declspec(dllexport)
#else
# define TESTDLL_API __declspec(dllimport)
#endif#ifdef __cplusplus
extern "C" {#endifTESTDLL_API int WINAPI Foo();#ifdef __cplusplus
}
#endif#endif // !TEST_H

这里大部分都是模板代码,需要注意的是Foo的声明。Foo的calling convetion是WINAPI,也就是__stdcall,vba中自定义c函数必须是WINAPI

我们在再来看vba中如何声明该函数

Declare Function Foo Lib "libpath" () As Long

这里有3点需要注意

  1. Foo有返回值,所以声明为Function
  2. libpath为dll的路径,可以是绝对路径,也可以是相对路径,这到后面再介绍
  3. 返回值为Long不是Integer

C/C++导出函数名称

我们知道,c/c++编译器通常会对函数名进行修饰,其中两个原因是因为函数可以重载,还有为了区分不同的调用约定。这就导致dll中的导出函数名称和我们编写的函数名字不一样。为了能够让VBA找到导出函数,我们一般可以在声明VBA函数时指定Alias

假设c中有如下函数

void WINAPI Foo(int i);

对于32位和64位平台,导出函数名称不一样。[1]

32位

导出函数名字位_Foo@4,所以函数声明为

Declare Sub Foo Lib "libpath" Alias "_Foo@4" (ByVal i As Long)

64位

Declare PtrSafe Foo Lib "libpath" (ByVal i As Long)

注意,这里我们使用了PtrSafe关键字,因为在64位Office下面,函数声明需要PtrSafe关键字。

数据类型

下表给出VBAC/C++中常见的数据类型的对应关系

VBA C/C++ 说明
Boolean VARIANT_BOOL vba中的True=-1,False=0
VARIANT_BOOL实际为short的typedef
Integer short
Long int
Double double
String BSTR BSTR是OLE的数据类型,是一个带长度前缀的字符串
Variant VARIANT
Array SAFEARRAY

String

vba中的String是Unicode string,对于常见的Win32 API,通常接受LPCWSTRLPCSTR两种字符指针,分别是utf-16和ANSI的字符串。

String -> LPCWSTR、const wchar_t*或BSTR

这种比较简单,我们使用StrPtr函数,直接获取String的指针即可,因为String本质上是一个BSTR

Declare Sub Foo Lib "libpath" (ByVal str As Long)
void WINAPI Foo(BSTR);
// void WINAPI Foo(LPCWSTR);
// void WINAPI Foo(const wchar_t*);

注意,vba的文档中找不到StrPtr

String -> LPCSTR 或 const char*

String是utf-16编码的,如果我们声明vba中的函数接受String,且c中的函数接受const char*,那么vba就会根据系统当前的locale,将utf-16转换为ANSI字符串。显然这个过程可能是有损的,通常不建议这么做。还有中方法就是再vba中将字符串转换为utf-8数组后,再传递给c函数。

Declare Sub Foo Lib "libpath" (ByVal str As String)
void WINAPI Foo(const char*);
// void WINAPI Foo(LPCSTR);

ARRAY

要传递vba的数组到c/c++函数中,可以使用以下两种方法[2]

指向第一个元素的指针

Declare Sub Foo Lib "libpath" (ByRef anArray As Long, ByVal size As Long)Dim anArray() As Long
Foo anArray(0), UBound(anArray) + 1
void WINAPI Foo(const int* anArray, int size);

SAFEARRAY指针

Delcare Sub Foo Lib "libpath" (ByRef anArray() As Long)Dim anArray() Long
Foo anArray
void WINAPI Foo(SAFEARRAY** ppArray);

vba函数命名

vba函数的名字不能同Excel中已经存在的名字相冲突,比如单元格的名字。所以F2这样的名字是非法,在调用时你会得到#REF!错误。

Dll路径

Declare时我们需要指定dll的路径,显然我们不想要硬编码路径。一种方法是我们修改PATH变量,加入dll所在的目录。这个动作可以在打开插件的工作簿时完成,代码如下

Private Declare PtrSafe Function SetEnvironmentVariableW Lib "Kernel32" (ByVal name As LongPtr, ByVal value As LongPtr) As LongPrivate Sub Workbook_Open()Dim newPath As StringnewPath = "path to dll" & Environ("PATH")' TODO Warn if changing PATH failsDebug.Print SetEnvironmentVariableW(StrPtr("PATH"), StrPtr(newPath))
End Sub

References

[1] Decorated names
[2] How To Pass Arrays Between Visual Basic and C

Excel自定义函数相关推荐

  1. excel自定义函数添加和使用方法

    第一,excel自定义函数简介 Excel自带很多函数供使用,但有些问题用内置函数解决起来很复杂,甚至是无能为力,这时就可以利用VBA开发自定义函数. 第二,excel如何添加自定义函数 excel自 ...

  2. excel自定义函数的注释_打印Excel注释的自定义列表

    excel自定义函数的注释 If you've added comments to an Excel worksheet, you have a couple of built-in options ...

  3. VBA Excel 自定义函数不能调用成功的问题的解决方法

    VBA Excel 自定义函数不能调用成功的问题的解决方法 问题以及解决方案的列举(持续更新中) 问题以及解决方案的列举(持续更新中) 自定义函数是自动化各种Excel重复操作的重要函数,我们可以用它 ...

  4. Excel 自定义函数按单元格背景颜色/字体颜色实现计数或求和

    Excel 自定义函数按单元格背景颜色/字体颜色实现计数或求和 https://download.csdn.net/download/sea1_2/34881468

  5. EXCEL自定义函数无法运行的原因:可以在VBA里运行的函数,在EXCEL用自定义函数为什么报错?

    目录 1目标问题: 为什么VBA里,function可以运行的代码,在EXCEL用自定义函数,会返回错误值? 2 先说结论 2.1 最容易发生的原因 2.2 其他原因梳理 3  自定义函数返回值的情况 ...

  6. 语言建立一个学生籍贯管理簿_编写一个Excel自定义函数,身份证信息提取如探囊取物...

    观看视频更直观 我们建立信息表时不仅要输入性别.生日和年龄等信息,往往也需要输入身份证号码,而身份证号码中包含有籍贯.性别.生日和年龄等信息,从身份证号码中提取上述信息可以减少输入工作量,提高工作效率 ...

  7. 你所不知道的Excel自定义函数BUG

    使用VBA开发自定义函数,可以弥补Excel内置函数的不足,简化函数公式,但是需要注意实现同样功能,自定义函数的效率远远不如内置函数,所以不要试图重新发明轮子. 最近发现了一个自定义函数的BUG(也可 ...

  8. python自定义函数求差_[VBA]发布一个计算桩号之差的Excel自定义函数(VBA)

    这是一个可以计算桩号之差(也就是得到长度)的Excel(或WPS)扩展函数,可以减少工程师在统计工程量时的工作量. 该函数具有一定的通用性.可以在MS Office和金山WPS上使用. 文末会给出使用 ...

  9. Excel - 自定义函数

    1. 问题: 假设我们在Excel中有以下需求: 你可以嵌套使用IF函数,判断每一个分数属于哪一个区间. 但是,4层IF会不会很容易出错:如果是5个区间,甚至是10个区间呢?嵌套使用10个IF不太现实 ...

  10. 如何使用Excel自定义函数并且全局使用

    一.使用Excel自定义一个从末尾开始截取指定字符串后面的函数 1.选择文件->选项->自定义功能区:勾选开发工具. 2.选择开发工具->visual Basic 3.选择插入-&g ...

最新文章

  1. linux下安装libsvm_Linux下libsvm的安装及简单练习
  2. linux筛选之后备份到命令,linux find 命令使用备份
  3. github 档案馆(是不是那个把code放到北极的那个项目。。。)
  4. 树莓派debian配置lamp【解决apache不显示php】
  5. 机器学习 对模型进行惩罚_使用Streamlit对机器学习模型进行原型制作
  6. python 隐藏命令行窗口_python如何只执行cmd中的动作,但消除或隐藏cmd窗口 - 小众知识...
  7. mongodb副本集搭建
  8. 第五十三天:优化网站的常用方法
  9. TensorFlow精进之路(十一):反向传播BP
  10. java面试难吗_MBA提前面试考什么?MBA提前面试得到优秀的,复试还难吗?林晨陪你考研...
  11. Python中通过Image的open之后,去show结果打不开bmp图片,无法正常显示图片
  12. 电商数据分析的4大思维和8类指标
  13. Html Imput 的用法
  14. 浙江新曙光建设_中习集团大厦项目工地与姜太公签约,正在使用太公管工进行实名制考勤...
  15. 电脑录屏快捷键是什么?电脑录屏是什么键
  16. 苹果要求部分员工佩戴警用级随身摄像头
  17. android源代码目录详解
  18. C盘根目录下只能创建文件夹不能新建文件的解决办法
  19. 字节流与字符流(一)
  20. SD敢达服务器源码,《SD敢达OL》服务器账号数据合并规则

热门文章

  1. jsp的九大内置对象以及作用
  2. 除以用计算机按哪个键,键盘上除以号是哪个键
  3. 基于SSD1306的OLED的驱动学习(二):SSD1306的寻址方式
  4. springmvc整合UReport2
  5. AD将原理图转换成彩色或者黑白PDF
  6. 逆天的H3C Comware V7 RBAC
  7. 【微信小程序开发】第1篇:开发工具安装及程序配置
  8. 【Linux操作系统基础】第一章 Linux命令行使用技巧
  9. 信息系统项目管理师(2022年) —— 第 11 章 项目风险管理
  10. websockets_WebSockets简介