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
相关产品推荐
相关产品推荐

