【问题标题】:When is using "undocumented" data types for VBA API calls safe? EG Currency 4 QueryPerformanceCounter or LongLong 4 GetSystemTimePreciseAsFileTime何时对 VBA API 调用安全使用“未记录”数据类型? EG Currency 4 QueryPerformanceCounter 或 LongLong 4 GetSystemTimePreciseAsFileTime
【发布时间】:2021-10-30 13:16:01
【问题描述】:

您可以将数据类型用于从 VBA 进行的任何特定 API 调用似乎具有灵活性,但我没有看到关于它们如何工作或如何选择一种的讨论,所以我如何知道哪些数据类型可以安全使用当它们似乎适用于小型测试样本时?假定为 64 位系统/软件。有很多“使用此代码”的示例,但我没有看到关于使用替代数据类型的讨论。

例如,我已经成功使用了三种数据类型...货币、UINT64 高/低(我猜 Long_Integer 中的正确术语?)和 LongLong 在 QueryPerformanceFrequency 的声明中...例如,在此声明中,您可以将 LongLong 更改为其他两个,并根据数据类型适当地使用它们。

Private Declare PtrSafe Function QueryPerformanceFrequency Lib "kernel32" (lpPerformanceFrequency As LongLong) As Long

请注意,使用货币,您确实得到频率……你得到频率除以 10,000。我没有看到任何关于您已将返回值更改为 MS 文档中说明的值的讨论。如果您在 QueryPerformanceFrequency 中使用 Currency,在 QueryPerformanceCounter 中使用 LongLong,您的计时器计算将出错。

FILETIME 的 LongLong 似乎也作为 API 调用的输入……我创建了最大的 SYSTEMTIME 日期/时间,用 LongLong (SystemTimeToFileTime) 转换了这个 FILTETIME,用 LongLong 数字将它转换回来作为 FILETIME (FileTimeToSystemTime)并获得了与我开始时相同的 SYSTEMTIME 日期/时间。但是,如果我在 FILETIME 中正确阅读了 MS,您不一定能够做到这一点。

我还偶然发现,如果你用 LongLong 高/低值定义一个 UNIT64 数据类型,那么返回的高位部分为零,低位部分包含所有数据。因此,考虑到 UNIT64 的两个部分,您不必将两个 High/Low 的 /form union 转换为 LongLong,只需使用 LowPartLL 中的值即可。

例如代码:

Private Declare PtrSafe Function GetSystemTimePreciseAsFileTime Lib "kernel32" (lpFileTime As UINT64LL) As Long

Public Type UINT64LL
    LowPartLL As LongLong
    HighPartLL As LongLong
End Type

Dim LLFTCounts as LongLong, u64LLFileTime as UINT64LL
GetSystemTimePreciseAsFile u64LLFileTime

LLFTCounts = UINT64LL.LowPartLL   ‘UINT64LL.HighPartLL is zero

那么我怎么知道哪种数据类型可以安全使用,假设它可以处理原始数据的最大大小?例如,如果有人告诉我,对返回无符号和有符号 64 位数据的 API 函数的所有 VBA 数据调用使用 LongLong 数据类型是安全的,并且 API 会给出正确的转换,那将会容易得多。当然,我可以尝试一种数据类型,看看是否适用于一些我当然没有技能水平进行完整测试的示例。

【问题讨论】:

    标签: vba types


    【解决方案1】:

    使用 API 调用时,数据类型的长度必须完全正确。 VBA 提供崩溃保护(每次调用后都会检查堆栈),因此使用错误的字节数不会导致 VBA 崩溃,但响应将是错误的/随机的,如果字节数不正确,则会设置错误条件。

    使用计算机时,必须是完全正确的。 1=1、2=2 等。使用意外的数据类型可能会使您更难以设置和理解值。

    除了这两个条件之外,您可以随意调用堆栈中设置和检索的字节,并且可以按您喜欢的方式对它们进行分组。 VBA 或 API 都不在乎。

    这与 COM 对象不同。当您使用 COM 库对象时,类型会被识别和命名,并且您必须使用相同的类型标识符。

    VBA 或 API 都没有提供确定变量类型的方法。这是“本机”API 的限制,也是 COM 等对象系统存在的原因。


    LongLong 和 Currency 都是 64 位类型。 LongLong 和 Currency 的 表示不同:这是使用不同数据表示可能导致的差异类型的示例。

    使用两个 UINT64 变量(128 位),其中需要 64 位是不安全的。如果您将声明转移到 Open Office,Open Office 就会崩溃。当您犯这样的错误时,VBA 会更正堆栈指针,而不是崩溃。您可能会在某些字节中获得正确的返回值,具体取决于您所犯的错误是什么,或者您可能不会。它是一台计算机,所以它是可预测的,但它不是定义的行为,所以它不安全。

    “安全”是计算机程序员的专用词。这并不意味着“在我的计算机上这个例子总是有效的”。由于堆栈对齐和 VBA 堆栈保护,您的不安全调用示例为您提供了正确答案和零并且没有使 VBA 崩溃。

    【讨论】:

    • 感谢您的快速响应,大卫。如果我正确理解您的答案,那么我创建的 UINT64LL 数据类型不会引起问题,因为我认为它是 2X64 位长,并且 API 调用使用 64 位长度的数据(它们的长度不完全相同)?再者,货币,与整数类型数据类型相比,为什么它实际上会通过除以 10,000 来改变答案?是因为货币被限制为小数点后 4 位,它只是采用 64 位整数并“粘贴”一个小数点吗?您的回答是否同意,我可以继续使用 LongLong 来处理任何返回 64 位数据的 API?
    • 感谢您抽出宝贵时间提供更多信息,我现在更了解该过程。我对这个网站还太陌生,无法投票赞成您的原始回复。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-03
    • 2013-10-22
    • 1970-01-01
    • 2018-06-14
    • 2021-02-18
    • 1970-01-01
    相关资源
    最近更新 更多