Delphi 2009/2010与Windows API调用的Unicode问题

4

你好,我一直在Delphi 2006中使用这个函数,但是现在在D2010中它会报错。我认为这与Unicode的切换有关。

  Function TWinUtils.GetTempFile(Const Extension: STRING): STRING;
  Var
     Buffer: ARRAY [0 .. MAX_PATH] OF char;
  Begin
    Repeat
      GetTempPath(SizeOf(Buffer) - 1, Buffer);
      GetTempFileName(Buffer, '~~', 0, Buffer);
      Result := ChangeFileExt(Buffer, Extension);
    Until not FileExists(Result);
  End;

我该怎么做才能让它正常工作?

编辑

当调用ChangeFileExt时,我遇到了“访问冲突”的问题。


你应该编辑你的问题以包括错误信息(一个好习惯 - 有道理,对吧?) - Argalatyr
1
请查看以下链接,其中包括对问题的讨论(几乎相同的代码)以及更好的版本,该版本考虑了API文档关于最大缓冲区大小的一些说明。https://forums.embarcadero.com/thread.jspa?threadID=18246 - Rob Kennedy
这个链接已经失效了。按线程 ID 搜索的正确方法是什么? - user173399
4个回答

7

Windows.Pas

function GetTempFileName(lpPathName, lpPrefixString: PWideChar;
  uUnique: UINT; lpTempFileName: PWideChar): UINT; stdcall;

function GetTempPath(nBufferLength: DWORD; lpBuffer: PWideChar): DWORD; stdcall;

SysUtils.Pas

function ChangeFileExt(const FileName, Extension: string): string;

试试这个

  Function TWinUtils.GetTempFile(Const Extension: STRING): STRING;
  Var
     Buffer: ARRAY [0 .. MAX_PATH] OF WideChar;
  Begin
    Repeat
      GetTempPath(Length(Buffer), Buffer);
      GetTempFileName(Buffer, '~~', 0, Buffer);
      Result := ChangeFileExt(Buffer, Extension);
    Until not FileExists(Result);
  End;

或者是这样的

  Function GetTempFile(Const Extension: String): String;
  Var
     Buffer: String;
  Begin
      SetLength(Buffer,MAX_PATH);
    Repeat
      GetTempPath( MAX_PATH, PChar( Buffer) );
      GetTempFileName(PChar( Buffer), '~~', 0, PChar( Buffer));
      Result := ChangeFileExt(Buffer, Extension);
    Until not FileExists(Result);
  End;

对于Delphi而言,Char类型和PChar类型分别是WideChar类型和PWideChar类型。

如果您使用任何返回数据到char缓冲区的Windows API,那么这些缓冲区需要重新声明为字节数组或AnsiChar数组。

如果您正在调用这些Windows API并发送缓冲区,则在告诉API您的缓冲区长度时使用了sizeof函数。这些调用需要更改为Length函数,因为 Windows widechar API需要字符数,而不是字节数

再见。


4
旧代码的问题在于在GetTempPath()函数中的语句SizeOf(Buffer) - 1。在Delphi 2009之前,字符数组的大小(SizeOf)与长度(Length)相同,因为AnsiChar的大小(SizeOf)等于1。但是,在Delphi 2009及以后版本中,Char和WideChar的大小(SizeOf)都等于2,所以该代码让GetTempPath()函数尝试填充比缓冲区更大的字符数。 - jasonpenny
我在“Delphi 2010”中尝试了这两个选项,它们都可以正常工作。;) - RRUZ
Char 不是 Widechar!它是 UnicodeChar。有一点区别。 - Toon Krijthe
Gamecat在Delphi 2010帮助文档中的链接为ms-help://embarcadero.rs2010/rad/Simple_Types.html。其中提到:“通用字符类型是Char,由于默认字符串类型是UnicodeString,因此它等同于WideChar”。 - RRUZ
@Gamecat:在D2009+中,“Char”确实是“WideChar”的别名。并没有“UnicodeChar”类型,也从未有过。 - Remy Lebeau

0

使用GetTempPathAGetTempFileNameA,它们是GetTempPathGetTempFileName的Ansi版本。它们仍然在Delphi 2009中可用,并在Delphi 2009帮助文件中提到,但没有广告。


在Windows NT操作系统版本中,大多数(如果不是全部)API函数的A版本只是存根(stubs),用于转换参数并调用相同函数的W版本。因此,在Delphi 2009+中,您将会将内部宽字符数据转换为Ansi,调用*A函数,该函数会将其内部转换为宽字符并返回,然后程序会将结果转换回宽字符。您认为采用这种方法有意义吗? - mghie

0

使用Delphi 2009,Char是一个Unicode字符。该函数可能需要一个AnsiChar数组。

刚刚检查了一下。GetTempFilename和GetTempPath都需要一个PWideString。

错误信息是什么?


你确定你不是想说 PWideChar 吗?PWideString 是完全不同的东西。 - Rob Kennedy

0

我建议总是阅读关于每个API函数的dwSize参数和/或返回值的准确期望的文档,而且我真的是一直这样说的。

不幸的是,有很多不同的情况,所以仅仅说“所有字符串函数都需要/返回字符数”并不完全正确,可能会让读者经历零星的无效指针AV错误的噩梦。

  • 大多数API函数确实期望/返回字符数,但也有一些例外。
  • 有些计算终止空字符,有些则不计算。
  • 有些函数在传递nil指针和非nil指针时的行为是不同的。
  • 还有一些函数根本没有提供任何方式来指示所需的缓冲区大小。

所有这些信息都可以轻松地在文档中找到,但应该认真阅读。真的,它可以节省您数小时的时间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接