Windows FreePascal:如何在非拉丁窗口中使用GetEnvironmentVariable?

Windows FreePascal:如何在非拉丁窗口中使用GetEnvironmentVariable?,windows,encoding,lazarus,freepascal,Windows,Encoding,Lazarus,Freepascal,我一直在使用Lazarus 1.6.4和FPC 3.0.2编写一些代码,以获取Windows中的用户名和Linux中的用户。为了实现这一点,我使用了SysUtils.GetEnvironmentVariable函数。在Linux中,它工作得很完美,但在Windows中,它返回一些损坏的内容,看起来像是以错误编码打开的UTF8字符串。Windows中的“我的用户名”有西里尔字母符号,因此GetEnvironmentVariable返回的不是实际名称,而是???????,但它应该是Пзззззз。

我一直在使用Lazarus 1.6.4和FPC 3.0.2编写一些代码,以获取Windows中的用户名和Linux中的用户。为了实现这一点,我使用了SysUtils.GetEnvironmentVariable函数。在Linux中,它工作得很完美,但在Windows中,它返回一些损坏的内容,看起来像是以错误编码打开的UTF8字符串。Windows中的“我的用户名”有西里尔字母符号,因此GetEnvironmentVariable返回的不是实际名称,而是
???????
,但它应该是
Пзззззз
。 这是我的代码:

function GetUserName: string;
{$IFDEF MSWINDOWS}
const
  envVar = 'USERNAME';
{$ENDIF}
{$IFDEF UNIX}
  envVar = 'USER';
{$ENDIF}
begin
  Result := SysUtils.GetEnvironmentVariable(envVar);
{$IFDEF MSWINDOWS}
  { TODO : BUG: Does not work correct for non-latin strings }
  Result := LazUTF8.UTF8ToWinCP(Result)
{$ENDIF}
end;
当其中包含非拉丁符号时,它会返回损坏的字符串


如何在Windows操作系统中以正确的编码获取EnvironmentVariable?

经过一天的运行,我找到了适合我的解决方案。您应该使用SysUtils.GetEnvironmentVariable的Unicode版本,并将结果转换为Windows当前代码页。下面是代码:

uses
  {$IFDEF MSWINDOWS}
  LazUTF8
  {$ENDIF}
  ;

function GetUserName: string;
const
  envVar: UnicodeString =
{$IFDEF MSWINDOWS}
  'USERNAME'
{$ENDIF}
{$IFDEF UNIX}
  'USER'
{$ENDIF};
begin
  // USE Unicode String Version only!
  Result := SysUtils.GetEnvironmentVariable(envVar);
{$IFDEF MSWINDOWS}
  Result := LazUTF8.UTF8ToWinCP(Result)
{$ENDIF}
end;    
2017年12月1日更新 正如David在评论中提到的,我的解决方案取决于ENV。在讨论中,我找到了更可靠的解决方案。代码如下:

function GetCurrentUserName: String;

{$IFDEF WINDOWS}
const
  MaxLen = 256;
var
  Len: DWORD;
  WS: WideString;
  Res: windows.BOOL;
{$ENDIF}
begin
  Result := '';
  {$IFDEF UNIX}
  {$IF (DEFINED(LINUX)) OR (DEFINED(FREEBSD))}
  Result := SysToUtf8(GetUserName(fpgetuid));   //GetUsername in unit Users, fpgetuid in unit BaseUnix
  {$ELSE Linux/BSD}
  Result := GetEnvironmentVariableUtf8('USER');
  {$ENDIF UNIX}
  {$ELSE}
  {$IFDEF WINDOWS}
  Len := MaxLen;
  {$IFnDEF WINCE}
  if Win32MajorVersion <= 4 then
  begin
    SetLength(Result,MaxLen);
    Res := Windows.GetuserName(@Result[1], Len);
    //writeln('GetUserNameA = ',Res);
    if Res then
    begin
      SetLength(Result,Len-1);
      Result := SysToUtf8(Result);
    end
    else SetLength(Result,0);
  end
  else
  {$ENDIF NOT WINCE}
  begin
    SetLength(WS, MaxLen-1);
    Res := Windows.GetUserNameW(@WS[1], Len);
    //writeln('GetUserNameW = ',Res);
    if Res then
    begin
      SetLength(WS, Len - 1);
      Result := Utf16ToUtf8(WS);
    end
    else SetLength(Result,0);
  end;
  {$ENDIF WINDOWS}
  {$ENDIF UNIX}
end;
函数GetCurrentUserName:String; {$IFDEF WINDOWS} 常数 MaxLen=256; 变量 伦:德沃德; WS:WideString; Res:windows.BOOL; {$ENDIF} 开始 结果:=''; {$IFDEF UNIX} {$IF(已定义(LINUX))或(已定义(FREEBSD))} 结果:=SYSTOUF8(GetUserName(fpgetuid))//unit Users中的GetUsername,unit BaseUnix中的fpgetuid {$ELSE Linux/BSD} 结果:=GetEnvironmentVariableUtf8('USER'); {$ENDIF UNIX} {$ELSE} {$IFDEF WINDOWS} Len:=MaxLen; {$IFnDEF WINCE}
如果3.0+getenvironmentvariable中的Win32MajorVersionAfaik在windows上被重载以进行unicodestring,则使用-W变量

var res,tag : unicodestring;

begin
  tag:='HOME';
  res:=getenvironmentvariable(tag);
end;
只是


也可能有效,但请确保将utf8设置为默认编码,或将结果分配给Unicode解压。

这不是在Windows上获取用户名的方法,而且您不希望在Windows上使用ANSI编码。@DavidFeffernan请详细解释。你有什么建议吗?你永远都不想使用ANSI编码,因为它只能对有限的字符集进行编码。要获取当前用户名,请使用提供该服务的Win32 API函数。例如
GetUserName
。也许Lazarus或FPC RTL已经包装了它,我不知道。@DavidHeffernan Lazarus没有。
GetUserName
函数仅在UNIX相关上下文中列出。我不明白,当我的解决方案完成工作并产生正确的输出时,为什么我要使用更复杂的解决方案?显然,你可以随心所欲。但是它不能处理语言环境以外的字符,而且也不能保证环境变量会存在。和
GetUserName
将在windows标题翻译中提供。
  getenvironmentstring (unicodestring('whatever'));