有些事,Excel 自己做起来会绕很远。比如查一个 .csv 到底会被哪个程序打开,导出报表前看默认打印机端口,根据屏幕尺寸调整工具窗体,把上次导出的目录记下来。
我一般会让 VBA 去问 Windows。
说白了,Windows API 就是 Windows 暴露出来的一批系统函数。它们放在 DLL 里,比如 user32.dll、shell32.dll、kernel32.dll、advapi32.dll。VBA 要调用这些函数,得先用 Declare 写一条声明,告诉 VBA:函数在哪里、叫什么、参数怎么传、返回值怎么接。
这篇我用自己的案例重写一遍。你会看到这些内容:API 声明怎么看,PtrSafe 和 LongPtr 怎么用,#If VBA7 Then 为什么会出现,VBE 里 #Else 下面变红怎么处理,还有文件关联、默认打印机、屏幕信息、注册表读写这些常用场景。
API 声明像一张调用说明书

VBA 调 Windows API,最关键的一步就是声明。
我会把 Declare 看成一张调用说明书。Excel 读到这行以后,才知道要去哪个 DLL 找函数,函数在 DLL 里的名字是什么,要传几个参数,返回值用什么类型接。
一个声明大概长这样:
1 2 3 4 5
PrivateDeclare PtrSafe Function MessageBoxW Lib"user32" ( _ByVal hwnd As LongPtr, _ByVal lpText As LongPtr, _ByVal lpCaption As LongPtr, _ByVal uType AsLong) AsLong
这里面每个词都有用:
PrivatePublic | |
Declare Function | |
PtrSafe | |
Lib "user32" | |
Alias "xxx" | |
ByValByRef | |
As LongAs LongPtr |
声明的位置也很重要。我的习惯是把 Option Explicit 放最上面,API 声明紧跟着放在标准模块顶部,常量再往下放,过程写在后面。
像这样:
1 2 3 4 5 6 7 8
OptionExplicit' 这里放 API 声明' 这里放常量PublicSub Demo()' 这里写业务代码EndSub
不要把 Declare 塞进 Sub 里面。这个位置错了,宏还没运行就会报编译错误。
先把 MessageBoxW 跑起来
新手练 Windows API,我建议从系统提示框开始。代码短,效果直观,出错也容易排查。
这个版本适合现在常见的 Excel 2016、2019、2021、Microsoft 365。它不写 #If,看起来更清楚。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
OptionExplicitPrivateDeclare PtrSafe Function MessageBoxW Lib"user32" ( _ByVal hwnd As LongPtr, _ByVal lpText As LongPtr, _ByVal lpCaption As LongPtr, _ByVal uType AsLong) AsLongPrivateConst MB_OK AsLong = &H0PrivateConst MB_ICONINFORMATION AsLong = &H40PublicSub ApiHello()Dim msg AsStringDim title AsStringmsg = "我从 VBA 调到了 Windows API"title = "API 提醒"MessageBoxW 0, StrPtr(msg), StrPtr(title), MB_OK Or MB_ICONINFORMATIONEndSub
使用方法很简单:
.xlsm | |
Alt + F11 | |
插入 -> 模块 | |
F5,或回到 Excel 按 Alt + F8 选择 ApiHello |
跑起来以后,你会看到一个 Windows 风格的消息框。
这里有两个小点,我觉得一定要写进文章里。
MessageBoxW 结尾的 W 代表 Unicode 版本。中文标题、中文路径、中文文件名,优先考虑 W 版。StrPtr(msg) 的意思是把 VBA 字符串的地址传给 Windows API。
代码里的引号一定要用英文半角双引号。公众号编辑器、Word、某些笔记软件会把 "API 提醒" 变成中文弯引号。VBA 一看到弯引号,很容易直接报错。
看到 #If / #Else / #End If 别慌

很多人第一次看到下面这种写法,会被开头的 # 吓到:
1 2 3 4 5
#If VBA7 Then' 新版 VBA 用这里#Else' 老版 VBA 用这里#EndIf
这叫条件编译指令。它是给编译器看的开关。
#If VBA7 Then 成立时,VBA 会用上面那段声明。#Else 下面那段是备用声明,给很老的 VBA 用。你在 VBE 里看到 #Else 下面变红,很多时候只是编辑器把当前不会参与编译的分支标红了。真正要看的是:ApiHello 能不能跑,消息框能不能弹出来。
还有一个容易混淆的地方:VBA7 说的是新版 VBA,Office 2010 之后基本都属于这个范围。它不等于 64 位。32 位 Office 也可能是 VBA7。需要判断真 64 位时,再看 Win64。
如果你要把代码发给别人,想兼容老 Office,可以用下面这个版本。注意,简化版和兼容版二选一,别把两个 MessageBoxW 声明同时放到同一个模块里。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
OptionExplicit#If VBA7 ThenPrivateDeclare PtrSafe Function MessageBoxW Lib"user32" ( _ByVal hwnd As LongPtr, _ByVal lpText As LongPtr, _ByVal lpCaption As LongPtr, _ByVal uType AsLong) AsLong#ElsePrivateDeclareFunction MessageBoxW Lib"user32" ( _ByVal hwnd AsLong, _ByVal lpText AsLong, _ByVal lpCaption AsLong, _ByVal uType AsLong) AsLong#EndIfPrivateConst MB_OK AsLong = &H0PrivateConst MB_ICONINFORMATION AsLong = &H40PublicSub ApiHello()Dim msg AsStringDim title AsStringmsg = "我从 VBA 调到了 Windows API"title = "API 提醒"MessageBoxW 0, StrPtr(msg), StrPtr(title), MB_OK Or MB_ICONINFORMATIONEndSub
我排查这类代码时,会按这个顺序看:
#Else | Sub,能弹窗就不用管 |
PtrSafe | #Else 分支 |
32 位和 64 位,别把 Long 全改成 LongPtr

老 VBA 代码迁到 64 位 Office,最常见的问题就是 API 声明。
新版 VBA 需要 PtrSafe。窗口句柄、文件句柄、内存地址、字符串地址这类值,要用 LongPtr。普通数字、按钮类型、常量编号、长度、数量,很多时候还是 Long。
我写代码时会记住一句话:LongPtr 给地址类的值用,Long 继续接普通整数。
比如 MessageBoxW 里:
1 2 3 4
ByVal hwnd As LongPtr ' 窗口句柄ByVal lpText As LongPtr ' 字符串地址ByVal lpCaption As LongPtr ' 字符串地址ByVal uType AsLong' 按钮和图标类型
uType 只是样式编号,所以继续用 Long。这事看着小,实际很容易写错。
6 个案例,把用法串起来

下面这些案例都换成了新的写法。你可以按需复制,但我建议每次只拷一个案例到空模块里测试。多个案例放在一起时,同名常量和同名声明不要重复。
案例一:MessageBoxW 做连通测试
上面已经跑过 MessageBoxW。我在实际写工具时,也会用它做一个最小测试:确认 API 声明没问题,确认宏权限没问题,确认系统函数能被调用。
1 2 3 4 5 6 7 8 9
PublicSub Demo_MessageBox()Dim msg AsStringDim title AsStringmsg = "报表导出完成,可以检查文件了。"title = "报表助手"MessageBoxW 0, StrPtr(msg), StrPtr(title), MB_OK Or MB_ICONINFORMATIONEndSub
这个例子小,但把 DLL、函数、参数、返回值都跑了一遍。后面换成更复杂的 API,思路还是这几件事。
案例二:ShellExecuteW 打开文件夹或网页
ShellExecuteW 很适合做“交给 Windows 自己打开”的事。给它一个网址,系统会用默认浏览器打开。给它一个文件夹,会打开资源管理器。给它一个文件,会按系统关联打开。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
OptionExplicit#If VBA7 ThenPrivateDeclare PtrSafe Function ShellExecuteW Lib"shell32.dll" ( _ByVal hwnd As LongPtr, _ByVal lpOperation As LongPtr, _ByVal lpFile As LongPtr, _ByVal lpParameters As LongPtr, _ByVal lpDirectory As LongPtr, _ByVal nShowCmd AsLong) As LongPtr#ElsePrivateDeclareFunction ShellExecuteW Lib"shell32.dll" ( _ByVal hwnd AsLong, _ByVal lpOperation AsLong, _ByVal lpFile AsLong, _ByVal lpParameters AsLong, _ByVal lpDirectory AsLong, _ByVal nShowCmd AsLong) AsLong#EndIfPrivateConst SW_SHOWNORMAL AsLong = 1PublicSub OpenCurrentWorkbookFolder()#If VBA7 ThenDim ret As LongPtr#ElseDim ret AsLong#EndIfret = ShellExecuteW(0, StrPtr("open"), _StrPtr(ThisWorkbook.Path),0,0, SW_SHOWNORMAL)If ret <= 32ThenMsgBox "打开失败,返回值:" & ret, vbExclamationEndIfEndSubPublicSub OpenHelpPage()#If VBA7 ThenDim ret As LongPtr#ElseDim ret AsLong#EndIfret = ShellExecuteW(0, StrPtr("open"), _StrPtr("https://example.com/excel-api-help"), _0,0, SW_SHOWNORMAL)EndSub
我一般会检查返回值。ShellExecuteW 返回值小于等于 32 时,多半表示失败。每个 API 的返回规则都要单独看,别靠猜。
案例三:只查 .csv 会被哪个程序打开
有时我不想真的打开文件,只想知道某种扩展名会交给哪个程序处理。比如 .csv 在一台电脑上可能交给 Excel,在另一台电脑上可能交给文本编辑器。
这里我用 AssocQueryStringW。它来自 shlwapi.dll,可以查询文件关联信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
OptionExplicit#If VBA7 ThenPrivateDeclare PtrSafe Function AssocQueryStringW Lib"shlwapi.dll" ( _ByVal flags AsLong, _ByVal assocStr AsLong, _ByVal pszAssoc As LongPtr, _ByVal pszExtra As LongPtr, _ByVal pszOut As LongPtr, _ByRef pcchOut AsLong) AsLong#ElsePrivateDeclareFunction AssocQueryStringW Lib"shlwapi.dll" ( _ByVal flags AsLong, _ByVal assocStr AsLong, _ByVal pszAssoc AsLong, _ByVal pszExtra AsLong, _ByVal pszOut AsLong, _ByRef pcchOut AsLong) AsLong#EndIfPrivateConst ASSOCSTR_EXECUTABLE AsLong = 2PrivateConst S_OK AsLong = 0PublicFunction AssociatedExe(ByVal extName AsString) AsStringDim buf AsStringDim cch AsLongDim p AsLongcch = 520buf = String$(cch, vbNullChar)If AssocQueryStringW(0, ASSOCSTR_EXECUTABLE, _StrPtr(extName),0,StrPtr(buf),cch)=S_OK Thenp = InStr(buf, vbNullChar)Ifp>0ThenAssociatedExe=Left$(buf,p-1)EndIfEndFunctionPublicSub Demo_CsvAssociation()Dim exePath AsStringexePath = AssociatedExe(".csv")If Len(exePath) = 0ThenMsgBox "没查到 .csv 的关联程序。", vbExclamationElseMsgBox ".csv 当前关联程序:" & vbCrLf & exePath, vbInformationEndIfEndSub
这个案例的关键在缓冲区。很多 Windows API 会让你准备一段字符串空间,函数把结果写进去。VBA 里常用 String$(长度, vbNullChar) 先占位,再用 InStr 找到结尾的空字符。
案例四:导出报表前做默认打印机检查
Excel 的 Application.ActivePrinter 能看到当前打印机文本,但驱动名和端口信息拆得不够细。GetProfileStringW 可以读取 Windows 配置里的 Windows / Device,内容通常长这样:
1
打印机名称,驱动名称,端口我会把它包装成“打印前检查卡”。导出 PDF、打印报表前跑一下,能少踩环境差异带来的坑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
OptionExplicit#If VBA7 ThenPrivateDeclare PtrSafe Function GetProfileStringW Lib"kernel32" ( _ByVal lpAppName As LongPtr, _ByVal lpKeyName As LongPtr, _ByVal lpDefault As LongPtr, _ByVal lpReturnedString As LongPtr, _ByVal nSize AsLong) AsLong#ElsePrivateDeclareFunction GetProfileStringW Lib"kernel32" ( _ByVal lpAppName AsLong, _ByVal lpKeyName AsLong, _ByVal lpDefault AsLong, _ByVal lpReturnedString AsLong, _ByVal nSize AsLong) AsLong#EndIfPublicFunction DefaultPrinterCard() AsStringDim buf AsStringDim rawText AsStringDim parts() AsStringDim n AsLongDim p AsLongbuf = String$(512, vbNullChar)n = GetProfileStringW(StrPtr("Windows"), StrPtr("Device"), _StrPtr(""),StrPtr(buf), Len(buf))If n = 0ThenExitFunctionp = InStr(buf, vbNullChar)If p > 0ThenrawText = Left$(buf, p - 1)ElserawText = bufEndIfparts = Split(rawText, ",")If UBound(parts) >= 2ThenDefaultPrinterCard="打印机:"&Trim$(parts(0))&vbCrLf& _"驱动:"&Trim$(parts(1))&vbCrLf& _"端口:"&Trim$(parts(2))ElseDefaultPrinterCard = rawTextEndIfEndFunctionPublicSub CheckPrinterBeforeExport()Dim info AsStringinfo = DefaultPrinterCard()If Len(info) = 0ThenMsgBox "没读到默认打印机。", vbExclamationElseMsgBox info, vbInformation, "打印前检查"EndIfEndSub
这个例子也提醒我:API 返回的结果不一定是对象,很多时候就是一段字符串。拿到以后要自己拆、自己清洗、自己展示。
案例五:把屏幕数量和尺寸写进工作表
做自动化报表时,同一份表在不同电脑上显示差异可能很大。GetSystemMetrics 可以读取主屏宽高、显示器数量、虚拟屏幕宽高。
多屏场景下,虚拟屏幕指的是 Windows 把几块屏幕拼在一起后的整体范围。这个信息对 UserForm 位置、仪表板展示、截图区域判断都挺有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
OptionExplicit#If VBA7 ThenPrivateDeclare PtrSafe Function GetSystemMetrics Lib"user32" ( _ByVal nIndex AsLong) AsLong#ElsePrivateDeclareFunction GetSystemMetrics Lib"user32" ( _ByVal nIndex AsLong) AsLong#EndIfPrivateConst SM_CXSCREEN AsLong = 0PrivateConst SM_CYSCREEN AsLong = 1PrivateConst SM_CXVIRTUALSCREEN AsLong = 78PrivateConst SM_CYVIRTUALSCREEN AsLong = 79PrivateConst SM_CMONITORS AsLong = 80PublicSub WriteScreenReport()With ActiveSheet.Range("A1").Value = "项目".Range("B1").Value = "值".Range("A2").Value = "显示器数量".Range("B2").Value = GetSystemMetrics(SM_CMONITORS).Range("A3").Value = "主屏宽度".Range("B3").Value = GetSystemMetrics(SM_CXSCREEN).Range("A4").Value = "主屏高度".Range("B4").Value = GetSystemMetrics(SM_CYSCREEN).Range("A5").Value = "虚拟屏幕宽度".Range("B5").Value = GetSystemMetrics(SM_CXVIRTUALSCREEN).Range("A6").Value = "虚拟屏幕高度".Range("B6").Value = GetSystemMetrics(SM_CYVIRTUALSCREEN).Columns("A:B").AutoFitEndWithEndSub
我更喜欢把这些系统信息写到一个“环境检查”工作表里。以后别人说工具窗口显示异常,你就能先看屏幕数量和分辨率。
案例六:把上次导出目录存进注册表

很多 Windows 程序会把配置放在注册表里。VBA 也可以通过 advapi32.dll 读写。
我的习惯是只写当前用户范围,比如:
1
HKEY_CURRENT_USER\Software\ExcelApiDemo公司电脑上不要随便改 HKEY_LOCAL_MACHINE。系统级配置涉及权限,也可能影响别的软件。
常见根键可以这样理解:
HKEY_CLASSES_ROOT | |
HKEY_CURRENT_USER | |
HKEY_LOCAL_MACHINE | |
HKEY_USERS | |
HKEY_CURRENT_CONFIG |
下面这个例子会把“上次导出目录”写到当前用户的注册表里,再读出来。代码有点长,因为注册表 API 要打开键、写值、查值、关闭键。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
OptionExplicit#If VBA7 ThenPrivateDeclare PtrSafe Function RegCreateKeyExW Lib"advapi32.dll" ( _ByVal hKey As LongPtr, _ByVal lpSubKey As LongPtr, _ByVal Reserved AsLong, _ByVal lpClass As LongPtr, _ByVal dwOptions AsLong, _ByVal samDesired AsLong, _ByVal lpSecurityAttributes As LongPtr, _ByRef phkResult As LongPtr, _ByRef lpdwDisposition AsLong) AsLongPrivateDeclare PtrSafe Function RegOpenKeyExW Lib"advapi32.dll" ( _ByVal hKey As LongPtr, _ByVal lpSubKey As LongPtr, _ByVal ulOptions AsLong, _ByVal samDesired AsLong, _ByRef phkResult As LongPtr) AsLongPrivateDeclare PtrSafe Function RegSetValueExW Lib"advapi32.dll" ( _ByVal hKey As LongPtr, _ByVal lpValueName As LongPtr, _ByVal Reserved AsLong, _ByVal dwType AsLong, _ByVal lpData As LongPtr, _ByVal cbData AsLong) AsLongPrivateDeclare PtrSafe Function RegQueryValueExW Lib"advapi32.dll" ( _ByVal hKey As LongPtr, _ByVal lpValueName As LongPtr, _ByVal lpReserved As LongPtr, _ByRef lpType AsLong, _ByVal lpData As LongPtr, _ByRef lpcbData AsLong) AsLongPrivateDeclare PtrSafe Function RegCloseKey Lib"advapi32.dll" ( _ByVal hKey As LongPtr) AsLong#ElsePrivateDeclareFunction RegCreateKeyExW Lib"advapi32.dll" ( _ByVal hKey AsLong, _ByVal lpSubKey AsLong, _ByVal Reserved AsLong, _ByVal lpClass AsLong, _ByVal dwOptions AsLong, _ByVal samDesired AsLong, _ByVal lpSecurityAttributes AsLong, _ByRef phkResult AsLong, _ByRef lpdwDisposition AsLong) AsLongPrivateDeclareFunction RegOpenKeyExW Lib"advapi32.dll" ( _ByVal hKey AsLong, _ByVal lpSubKey AsLong, _ByVal ulOptions AsLong, _ByVal samDesired AsLong, _ByRef phkResult AsLong) AsLongPrivateDeclareFunction RegSetValueExW Lib"advapi32.dll" ( _ByVal hKey AsLong, _ByVal lpValueName AsLong, _ByVal Reserved AsLong, _ByVal dwType AsLong, _ByVal lpData AsLong, _ByVal cbData AsLong) AsLongPrivateDeclareFunction RegQueryValueExW Lib"advapi32.dll" ( _ByVal hKey AsLong, _ByVal lpValueName AsLong, _ByVal lpReserved AsLong, _ByRef lpType AsLong, _ByVal lpData AsLong, _ByRef lpcbData AsLong) AsLongPrivateDeclareFunction RegCloseKey Lib"advapi32.dll" ( _ByVal hKey AsLong) AsLong#EndIfPrivateConst HKEY_CURRENT_USER AsLong = &H80000001PrivateConst KEY_READ AsLong = &H20019PrivateConst KEY_WRITE AsLong = &H20006PrivateConst REG_SZ AsLong = 1PrivateConst ERROR_SUCCESS AsLong = 0PrivateConst DEMO_SUB_KEY AsString = "Software\ExcelApiDemo"PrivateConst DEMO_VALUE_NAME AsString = "LastExportFolder"PublicFunction SaveLastExportFolder(ByVal folderPath AsString) AsBoolean#If VBA7 ThenDim hKey As LongPtr#ElseDim hKey AsLong#EndIfDim opened AsLongDim bytes AsLongDim subKey AsStringDim valueName AsStringsubKey = DEMO_SUB_KEYvalueName = DEMO_VALUE_NAMEIf RegCreateKeyExW(HKEY_CURRENT_USER, StrPtr(subKey), 0, 0, 0, _KEY_WRITE,0,hKey,opened)=ERROR_SUCCESS Thenbytes = (Len(folderPath) + 1) * 2SaveLastExportFolder = (RegSetValueExW(hKey, StrPtr(valueName), 0, _REG_SZ,StrPtr(folderPath),bytes)= ERROR_SUCCESS)RegCloseKey hKeyEndIfEndFunctionPublicFunction ReadLastExportFolder() AsString#If VBA7 ThenDim hKey As LongPtr#ElseDim hKey AsLong#EndIfDim subKey AsStringDim valueName AsStringDim valueType AsLongDim bytes AsLongDim buf AsStringDim p AsLongsubKey = DEMO_SUB_KEYvalueName = DEMO_VALUE_NAMEIf RegOpenKeyExW(HKEY_CURRENT_USER, StrPtr(subKey), 0, _KEY_READ,hKey)<>ERROR_SUCCESSThenExitFunctionIf RegQueryValueExW(hKey, StrPtr(valueName), 0, _valueType,0,bytes)=ERROR_SUCCESS ThenIfvalueType=REG_SZAndbytes>2Thenbuf=String$(bytes\2, vbNullChar)IfRegQueryValueExW(hKey,StrPtr(valueName),0, _valueType,StrPtr(buf),bytes)=ERROR_SUCCESS Thenp=InStr(buf, vbNullChar)Ifp>0ThenReadLastExportFolder=Left$(buf,p-1)EndIfEndIfEndIfRegCloseKey hKeyEndFunctionPublicSub Demo_SaveExportFolder()If SaveLastExportFolder(ThisWorkbook.Path) ThenMsgBox "已保存导出目录:" & vbCrLf & ReadLastExportFolder(), vbInformationElseMsgBox "写入失败,请检查权限。", vbExclamationEndIfEndSub
注册表 API 里有几个函数我会固定记住:
RegCreateKeyExW | |
RegOpenKeyExW | |
RegSetValueExW | |
RegQueryValueExW | |
RegCloseKey |
RegCloseKey 特别容易被忘掉。只要你打开了注册表键,做完就关掉。这和打开文件后要关闭文件句柄是一个道理。
只存自己的小设置,GetSetting / SaveSetting 更省事
如果我只是想保存自己宏的窗口位置、上次选择的目录、某个开关状态,会优先用 VBA 自带的 GetSetting 和 SaveSetting。
1 2 3 4 5 6 7
OptionExplicitPublicSub Demo_BuiltInSetting()SaveSetting "ExcelApiDemo", "Export", "LastFolder", ThisWorkbook.PathMsgBox GetSetting("ExcelApiDemo", "Export", "LastFolder", "")EndSub
它们写入的位置固定在这里:
1
HKEY_CURRENT_USER\Software\VB and VBA Program Settings想读写别的位置,还是要用注册表 API。只存自己宏的小配置,用内置函数会轻很多。
我写 Windows API 前会看这张表
.xlsm,并允许宏运行 | |
Sub 里面 | |
#If | |
user32、shell32、kernel32 还是 advapi32 | |
W 版 | |
WStrPtr(字符串) 传地址 | |
LongPtr | |
Long | |
HKEY_CURRENT_USER 下自己的键 | |
RegCloseKey |
Windows API 看起来吓人,其实拆开以后就是三件事:声明写对,参数传对,返回值判断对。
我觉得 VBA 最舒服的地方就在这里:Excel 负责表格和业务,Windows API 负责问系统要信息。文件关联、打印机、屏幕、注册表这些功能,不一定每天用,但做成工具以后,省下的麻烦会很多。
夜雨聆风