如何在Delphi中获取当前用户的名称?

4

您好,我正在使用Delphi FM2和XE3在Windows 8上进行开发。

我的问题是,我希望用户按下按钮后能够导航到位于AppData中的子文件夹,例如C:\ Users \ Kobus \ AppData \ Roaming.minecraft。

由于每个用户的用户名都不同,因此这种方法行不通。

所以我使用以下代码来获取用户名:

function GetCurrentUserName : string;
const
  cnMaxUserNameLen = 254;
var
  sUserName     : string;
  dwUserNameLen : DWord;
begin
  dwUserNameLen := cnMaxUserNameLen-1;
  SetLength( sUserName, cnMaxUserNameLen );
  GetUserName(PChar( sUserName ),dwUserNameLen );
  SetLength( sUserName, dwUserNameLen );
  Result := sUserName;
end;

username := GetCurrentUserName;

然后我运行ShowMessage('C:\Users\'+username+'\AppData\Roaming\.minecraft\saves\');查看输出。

但是我得到的输出是:'C:\Users\Kobus',因为某种原因其余路径名称丢失了。

我需要显示的是:'C:\Users\'Kobus'\AppData\Roaming.minecraft\saves\'。

谢谢。


4
在Windows XP系统中,你指的是 C:\Documents and Settings\<username>等路径。但即使你知道用户名,也不一定知道该路径是什么。可以使用 SHGetSpecialFolderPath 函数来获取该路径。 - Andreas Rejbrand
4个回答

12
问题在于 dwUserNameLen 包含了字符串的长度,包括结尾的零终止符。因此,当你执行以下操作时:
SetLength(sUserName, dwUserNameLen);

这导致sUserName被设置为'Kobus#0'。随后,您将其传递给Windows API对话框函数,该函数将字符串视为以 null 结尾的字符串,并在无关的 null 终止符处截断字符串。

因此,您可以这样修复它:

SetLength(sUserName, dwUserNameLen-1);

请注意,如果调用 GetUserName 失败,您还应该检查其返回值:

if not GetUserName(PChar(sUserName), dwUserNameLen) then
  RaiseLastOSError;

或者是一个更加清晰的变体:

Win32Check(GetUserName(PChar(sUserName), dwUserNameLen));

最后一个要点是:这种方法并不正确地获取漫游应用程序数据文件夹。首先,您假设了各种实现细节。您的方法将在使用不同命名模式的较旧版本的Windows上失败。或者在某些未来版本的Windows上失败。或者在以不同方式配置的当前版本上失败。

正确的方法是询问系统漫游应用程序数据文件夹的位置。使用 CSIDL_APPDATA(用于较旧的Windows版本)或 FOLDERID_RoamingAppData(适用于现代Windows版本)进行操作。


3
+1 对于答案来说,“最后一点”部分是最重要的。 - Andreas Rejbrand
谢谢你的帮助,我会尝试按照你在最后一点所说的去做。 - Kobus Vdwalt
在这里,我个人会使用 SetLength( sUserName, StrLen(sUserName) );。或者更懒的方式是 sUserName := PChar(sUserName);。因为这个 Win32 API 调用不会返回由多个零分隔的多个字符串,所以可以忽略 dwUserNameLen - Arioch 'The

1
我觉得你的问题属于XY问题之一。
你实际想要读取%AppData%\.minecraft\saves\的完整路径。
你在考虑如何读取当前的Username
请查看CSIDLSHGetFolderPath
function GetShellFolder(CSIDLFolder : integer) : string;
begin
  SetLength(Result, MAX_PATH);
  SHGetSpecialFolderPath(0, PChar(Result), CSIDLFolder, false);
  SetLength(Result, StrLen(PChar(Result)));
  if (Result <> '') then
    Result  := IncludeTrailingBackslash(Result);
end;

....

//Usage
ShowMessage(GetShellFolder(CSIDL_APPDATA)+'.minecraft\saves');

更新

替代方法

检查System.IOUtils单元中的GetHomePath函数。

将会为多平台实现您所需的相同结果。

uses System.IOUtils;

procedure TForm17.btn1Click(Sender: TObject);
begin
    ShowMessage(TPath.GetHomePath() + TPath.DirectorySeparatorChar + '.minecraft\saves');
end;

1

我不需要花太长时间就能找到一小段代码 :). 所以在总结提示后,我在自己的应用程序中使用的是:

//=================================================================
procedure TMainF1.UserTestClick(Sender: TObject);
const
  cnMaxUserNameLen = 254;
var
  sUserName     : string;
  dwUserNameLen : DWord;
begin
  dwUserNameLen := cnMaxUserNameLen-1;
  SetLength( sUserName, cnMaxUserNameLen );
  Win32Check(GetUserName( PChar(sUserName), dwUserNameLen ));
  sUserName := PChar( sUserName );
  label_user.Caption := UpperCase(sUserName);
end;

//== works well with D7

-2
username := GetEnvironmentVariable('username');

username设置为当前用户的名称,避免了复杂性。


一个只包含代码的答案通常是一个较差的答案。你应该尝试解释代码试图做什么,以及它如何解决问题,并回答所提出的问题。 - David Heffernan
在我看来,这个答案最大的问题是它未能解决原帖中方法的主要问题。 - Andreas Rejbrand
好的,如果你愿意的话。我建议这是一个相当明显的想法。回答了提出的问题,“如何在Delphi中获取当前用户的名称。” 在我看来,这是最简单的方法。 - Magoo
问题很简单,就是如何获取数据 - 你会期望任何寻找答案的人都使用那种方法来实现操作吗?问题不是“我该如何找到当前用户的minecraft.exe” - Magoo
6
请注意,环境中可能没有该变量,但API调用总是会给出正确的答案。 - David Heffernan

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