CreatePipe 成功但没有生成有效的句柄对

问题描述 投票:0回答:1

我正在编写一些 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 的其余部分)

winapi pipe vba7
1个回答
0
投票

默认情况下,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变量的地址。

© www.soinside.com 2019 - 2024. All rights reserved.