Delphi中如何正确为FormatMessage传递array of const参数?
解决Delphi中FormatMessage传递array of const的问题
问题根源
Windows API的FormatMessage在使用FORMAT_MESSAGE_ARGUMENT_ARRAY标志时,要求传入的是**DWORD_PTR(对应Delphi的NativeUInt)类型的数组指针**,每个元素对应格式化字符串中的一个占位符。而Delphi的array of const本质是TVarRec数组,TVarRec是包含类型标识和值的变体结构,直接传递或仅提取部分字段都会导致类型不匹配,引发访问冲突或乱码。
错误代码分析
第一段代码问题
直接传递@Args[0]给Arguments参数,相当于把TVarRec数组的指针传给了期望DWORD_PTR数组的API。TVarRec的结构是VType(2字节)+ 联合值(4/8字节),和API要求的纯数值数组结构完全不符,必然触发访问冲突。
第二段代码问题
仅提取TVarRec.VPointer,忽略了vtInteger、vtBoolean等类型的VInteger字段,且未将值转换为DWORD_PTR类型,导致传入的参数值不符合API预期,出现乱码。
正确实现
需要遍历TVarRec数组,根据不同类型提取对应的值并转换为NativeUInt(匹配DWORD_PTR),构建符合API要求的参数数组后再调用FormatMessage:
function FormatMessageFromString(const AFormat: string; const Args: array of const): string; var Buffer: PChar; ArgArray: array of NativeUInt; I: Integer; begin // 构建符合FormatMessage要求的DWORD_PTR数组 SetLength(ArgArray, Length(Args)); for I := Low(Args) to High(Args) do begin case Args[I].VType of vtInteger: ArgArray[I] := NativeUInt(Args[I].VInteger); vtBoolean: ArgArray[I] := NativeUInt(Byte(Args[I].VBoolean)); vtChar: ArgArray[I] := NativeUInt(Args[I].VChar); vtExtended: ArgArray[I] := NativeUInt(@Args[I].VExtended^); // 浮点数需传指针 vtString: ArgArray[I] := NativeUInt(PChar(Args[I].VString^)); vtPChar: ArgArray[I] := NativeUInt(Args[I].VPChar); vtAnsiString: ArgArray[I] := NativeUInt(PAnsiChar(Args[I].VAnsiString^)); vtWideChar: ArgArray[I] := NativeUInt(Args[I].VWideChar); vtWideString: ArgArray[I] := NativeUInt(PWideChar(Args[I].VWideString^)); vtUnicodeString: ArgArray[I] := NativeUInt(PWideChar(Args[I].VUnicodeString^)); else raise Exception.CreateFmt('Unsupported argument type: %d', [Args[I].VType]); end; end; if FormatMessage( FORMAT_MESSAGE_FROM_STRING or FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_ARGUMENT_ARRAY, PWideChar(AFormat), 0, 0, @Buffer, 0, @ArgArray[0]) <> 0 then try Result := Buffer; // 替换换行符为Windows标准格式 Result := StringReplace(Result, #10, #13#10, [rfReplaceAll]); finally LocalFree(HLOCAL(Buffer)); end else RaiseLastOSError; end;
关键说明
- 类型转换:针对每种
TVarRec类型,将值转换为NativeUInt(32位系统为Cardinal,64位为UInt64),匹配DWORD_PTR的定义。 - 浮点数处理:
FormatMessage要求浮点数参数传递指针,因此需要传入VExtended的地址。 - 字符串类型:不同Delphi字符串类型(Ansi、Wide、Unicode)需转换为对应的C风格字符串指针后传入。
- 换行符处理:
FormatMessage默认输出\n(LF),替换为Windows标准的\r\n(CRLF)提升兼容性。
内容的提问来源于stack exchange,提问作者yonni
相关产品推荐
相关产品推荐

