我正在编写一些 VBA 代码,我希望能够生成一个子进程并通过标准输入/标准输出与其通信。我在这里找到了如何在 C/C++ 中执行此操作的描述:
我正在使用 Microsoft 的 VBA 绑定内核函数,如下所示:
Office 2010 帮助文件:具有 64 位支持的 Win32API_PtrSafe
开始将 C/C++ 示例移植到 VBA 后,我有下面的代码,它只创建管道,仅此而已。它检查操作是否成功并打印
CreatePipe()
输出的句柄。
Sub test_pipe_creation()
' Security attribute structure
Dim secAttrStdHand As SECURITY_ATTRIBUTES
secAttrStdHand.nLength = Len(secAttrStdHand)
secAttrStdHand.bInheritHandle = True
secAttrStdHand.lpSecurityDescriptor = 0
' The handles
Dim stdout_r As Long
Dim stdout_w As Long
stdout_r = &HDEADBEEF ' initialised to an obviously incorrect handle
stdout_w = &HDEADBEEF
Dim res As Long
Dim e As Long
Debug.Print "About to create pipe, handles currently have dummy values", Hex(stdout_r), Hex(stdout_w)
' Try to create the pipe
res = CreatePipe(VarPtr(stdout_r), VarPtr(stdout_w), secAttrStdHand, 0)
If res = 0 Then
e = GetLastError
Debug.Print "Failed to create pipe"
Debug.Print "Error: ", e
Else
Debug.Print "Pipe created"
End If
Debug.Print "Pipe handle values are:", Hex(stdout_r), Hex(stdout_w)
End Sub
当我运行它时,我得到以下输出:
About to create pipe, handles currently have dummy values DEADBEEF DEADBEEF
Pipe created
Pipe handle values are: DEADBEEF DEADBEEF
所以这意味着
CreatePipe()
返回True
表示成功,但它没有更新句柄变量stdout_r
和stdout_w
。
为了确保不仅仅是返回码错误,我也尝试过使用
GetLastError()
检查错误代码,而不考虑返回码。报错值是0
表示成功,所以Windows肯定是报操作成功了
是什么导致了这种行为?如何从 VBA 代码正确创建管道?
(Windows 11,Word,不确定是哪个版本的 Word,因为如何检查并不明显,但我最近刚安装了它以及 Office 365 的其余部分)
默认情况下,VBA 通过引用 传递参数,除非指定
ByVal
(正如您在nSize
参数中所做的那样)。
VarPtr()
返回 LongPtr
,并且您声明 phReadPipe
和 phWritePipe
参数 CreatePipe()
作为采用 LongPtr
by reference。所以,你实际上传递了 CreatePipe()
LongPtr
返回的临时 VarPtr()
s 的地址,而不是你的 Long
变量 1 的地址 VarPtr()
指向。
1:顺便说一句,应该声明为
LongPtr
而不是 Long
,因为 Win32 HANDLE
是指针,而不是整数。
这就是为什么
CreatePipe()
不修改你的变量。相反,它正在修改来自 LongPtr
的临时 VarPtr()
s。您可以使用显式变量轻松验证,例如:
Dim temp1 as LongPtr
Dim temp2 as LongPtr
...
temp1 = VarPtr(stdout_r)
temp2 = VarPtr(stdout_w)
Debug.Print "Pipe handle addresses are:", Hex(temp1), Hex(temp2)
res = CreatePipe(temp1, temp2, secAttrStdHand, 0)
' temp1 and temp2 get modified!
Debug.Print "Pipe handle addresses are:", Hex(temp1), Hex(temp2)
要解决这个问题,请尝试将
ByVal
添加到第一个两个参数,例如:
Declare PtrSafe Function CreatePipe Lib "kernel32" (ByVal phReadPipe As LongPtr, ByVal phWritePipe As LongPtr, lpPipeAttributes As SECURITY_ATTRIBUTES, ByVal nSize As Long) As Long
或者,完全放弃
VarPtr()
并只使用 ByRef
代替:
Declare PtrSafe Function CreatePipe Lib "kernel32" (ByRef phReadPipe As Long, ByRef phWritePipe As Long, lpPipeAttributes As SECURITY_ATTRIBUTES, ByVal nSize As Long) As Long
...
res = CreatePipe(stdout_r, stdout_w, secAttrStdHand, 0)
无论哪种方式,
CreatePipe()
现在应该收到您的Long
1变量的地址。