You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

32位转64位Office VBA:CopyMemory返回DEVMODE值异常(dmDuplex返回4)

解决64位VBA中CopyMemory处理DEVMODE结构体时dmDuplex值异常的问题

我之前在把32位Office VBA的打印机切换代码转64位时,也碰到过完全一样的坑!核心问题是32位和64位下DEVMODE结构体的内存布局、成员对齐规则不一样,直接照搬32位的结构体声明和CopyMemory调用,肯定会读错内存位置,导致dmDuplex这类成员返回奇怪的值(比如你遇到的4)。下面是我踩坑后总结的解决步骤:

1. 修正64位兼容的DEVMODE结构体声明

首先要把DEVMODE的声明改成完全适配64位的,重点注意:

  • 所有指针类型的成员要换成LongPtr(比如dmPelsWidth、dmDriverData这类内存地址相关的字段)
  • 保持结构体成员的内存对齐,64位下默认是8字节对齐,32位是4字节,所以有些成员之间会自动填充字节,不能手动硬写偏移
  • 必须给所有API声明加上PtrSafe关键字

正确的声明示例:

Private Type DEVMODE
    dmDeviceName As String * 32
    dmSpecVersion As Integer
    dmDriverVersion As Integer
    dmSize As Integer
    dmDriverExtra As Integer
    dmFields As Long
    dmOrientation As Integer
    dmPaperSize As Integer
    dmPaperLength As Integer
    dmPaperWidth As Integer
    dmScale As Integer
    dmCopies As Integer
    dmDefaultSource As Integer
    dmPrintQuality As Integer
    dmColor As Integer
    dmDuplex As Integer
    dmYResolution As Integer
    dmTTOption As Integer
    dmCollate As Integer
    dmFormName As String * 32
    dmLogPixels As Integer
    dmBitsPerPel As Long
    dmPelsWidth As LongPtr
    dmPelsHeight As LongPtr
    dmDisplayFlags As Long
    dmDisplayFrequency As Long
    dmICMMethod As Long
    dmICMIntent As Long
    dmMediaType As Long
    dmDitherType As Long
    dmReserved1 As Long
    dmReserved2 As Long
    dmPanningWidth As LongPtr
    dmPanningHeight As LongPtr
End Type

2. 正确调用CopyMemory函数

64位下CopyMemory的声明也要更新,而且调用时要注意:

  • 不要硬编码复制的字节数,应该用dmSize成员的值(这是DEVMODE结构体的标准大小,避免因对齐差异导致的复制长度错误)
  • 源指针必须是LongPtr类型,避免类型不匹配导致的内存越界

更新后的CopyMemory声明:

Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
    ByRef Destination As Any, _
    ByRef Source As Any, _
    ByVal Length As LongPtr _
)

调用示例(获取当前打印机的DEVMODE并读取dmDuplex):

Dim hPrinter As LongPtr
Dim dm As DEVMODE
Dim buffer() As Byte
Dim requiredSize As Long
Dim result As Long

' 先获取打印机句柄(确保64位下用LongPtr存储句柄)
' 这里省略获取hPrinter的代码,你可以用OpenPrinterAPI实现,注意参数也要适配64位
' ...

' 初始化DEVMODE的大小字段
dm.dmSize = Len(dm)
ReDim buffer(0 To dm.dmSize - 1) As Byte

' 获取打印机的DEVMODE数据
result = GetPrinter(hPrinter, 2, VarPtr(buffer(0)), dm.dmSize, requiredSize)

' 将字节数组复制到DEVMODE结构体
CopyMemory dm, buffer(0), dm.dmSize

' 现在读取dmDuplex应该就正常了
Debug.Print "当前打印模式:" & dm.dmDuplex
' 正常取值参考:1=单面,2=双面(长边翻转),3=双面(短边翻转)

3. 排查常见错误点

  • 如果还是返回异常值,检查GetPrinter的调用是否正确:64位下GetPrinter的参数也要全部适配LongPtr,尤其是句柄和指针类型的参数
  • 确保你的代码中所有和内存地址、句柄相关的变量都从Long改成了LongPtr,包括打印机句柄hPrinter、内存指针等
  • 不要手动修改结构体的填充字节,64位VBA会自动处理对齐,手动加字节反而会破坏内存布局

最后提醒一句:测试的时候最好多换几台打印机,不同厂商的驱动对DEVMODE的处理可能略有差异,但按照标准结构体声明来,基本都能解决问题。

内容的提问来源于stack exchange,提问作者William James

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:59:54