从.NET 8开始,.NET环境变量API的一个显著限制是不能定义环境变量without a value,换句话说,不能定义empty string的值.
然而,Windows和类似Unix的平台都通过系统调用在本地支持这一点,这就是为什么直接调用后者是有意义的.
- 在future 将这种能力带到.NET本身是GitHub issue #50554的主题.
虽然使用P/Invoke声明来访问相关系统调用在Windows上按预期工作,但在类Unix平台上不能,从.NET 8开始:
- NET不对P/Invoke中介的环境修改进行see,并继续通过
Environment
和启动子进程时报告old环境.
这是一个错误,还是下面的代码遗漏了什么?
示例C#控制台应用程序代码,使用8.0.101版的.NET SDK进行测试:
-
在类Unix平台(Linux或MacOS)上运行,作为使用
dotnet new console
创建的项目中Program.cs
文件的内容 -
在
*.csproj
文件的<PropertyGroup>
元素中添加<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
,以便编译项目.
using System.Diagnostics;
using System.Runtime.InteropServices;
partial class Program
{
[LibraryImport("libc", StringMarshalling = StringMarshalling.Utf8)]
private static partial int setenv(string name, string value, int overwrite);
[LibraryImport("libc", StringMarshalling = StringMarshalling.Utf8)]
private static partial IntPtr getenv(string name); // Convert to string with Marshal.PtrToStringUTF8()
[LibraryImport("libc", StringMarshalling = StringMarshalling.Utf8)]
private static partial int system(string command);
static void ThrowSysCallError() => throw new System.ComponentModel.Win32Exception(Marshal.GetLastSystemError());
static void Main(string[] args)
{
// Define / set env. var. 'FOO' with / to value 'new'
if (-1 == setenv("FOO", "new", 1)) ThrowSysCallError();
Console.WriteLine(
$"In-process value of FOO after setting it to 'new', via syscall: [{Marshal.PtrToStringUTF8(getenv("FOO"))}]"
);
Console.Write(
"Value of FOO in a syscall-launched child process: "
);
system("echo [$FOO]");
Console.WriteLine(
$"In-process value of FOO per Environment.GetEnvironmentVariable(\"FOO\"): [{Environment.GetEnvironmentVariable("FOO")}]"
);
Console.Write(
"Value of FOO in a .NET-launched child process: "
);
Process.Start(
new ProcessStartInfo()
{
FileName = "sh",
ArgumentList = { "-c", @"echo [$FOO]" },
}
)?.WaitForExit();
}
}
expected的输出将是:
In-process value of FOO after setting it to 'new', via syscall: [new]
Value of FOO in a syscall-launched child process: [new]
In-process value of FOO per Environment.GetEnvironmentVariable("FOO"): [new]
Value of FOO in a .NET-launched child process: [new]
actual的输出是-请注意,.NET显然没有看到setenv()
系统调用设置的FOO
变量:
In-process value of FOO after setting it to 'new', via syscall: [new]
Value of FOO in a syscall-launched child process: [new]
In-process value of FOO per Environment.GetEnvironmentVariable("FOO"): []
Value of FOO in a .NET-launched child process: []