目前,我正在开发一个 ASP.NET 应用程序,由于一些遗留问题,该应用程序需要执行一些 Perl 脚本。为此,我编写了一个使用嵌入式 Perl API 的小型 C++ 库。该库只有一个(C)入口点,允许 C# 代码在传递命令行参数和环境变量的同时执行脚本。此环境变量允许 C# 代码模拟对 Perl 脚本的 CGI 调用。
现在我发现了一些奇怪的事情。C# 应用程序允许(最大数量的)并发 Perl 脚本执行。每次调用都会创建一个不同的环境,并将其传递给我的 C++ 库,然后 C++ 库会将此环境传递给perl_parse
函数。然后我注意到,Perl 脚本看到的实际环境与传递的实际环境不匹配,而似乎是旧副本。所以我的问题是,我是不是忽略了什么?有没有一种特殊的方法可以并发运行多个嵌入式 Perl 解释器?即使我将并发线程数限制为 1,问题仍然存在。
C++库的相关部分如下:
extern "C" __declspec(dllexport) BOOL ExecutePerlScript(PCSTR* environmentVariables,
PCSTR path)
{
BOOL result(FALSE);
// Create the Perl interpreter
PerlInterpreter* my_perl(perl_alloc());
if (NULL != my_perl)
{
PERL_SET_CONTEXT(my_perl);
PL_perl_destruct_level = 1;
perl_construct(my_perl);
PL_origalen = 1;
PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
// Initialize the Perl interpreter
result = (perl_parse(my_perl,
XsInit,
NR_DEFAULT_ARGUMENTS,
DEFAULT_ARGUMENTS,
const_cast<char**>(environmentVariables)) == 0) ? TRUE : FALSE;
// Run the interpreter
if (result)
{
result = (perl_run(my_perl) == 0) ? TRUE : FALSE;
}
if (result)
{
result = LoadFile(path,
my_perl);
}
if (result)
{
// Execute the Perl script
eval_pv("eval \"$" SCRIPT_TO_EVALUATE_VARIABLE_NAME "; 1\" or do { $" SCRIPT_EXECUTION_ERROR_VARIABLE_NAME " = $@; }",
TRUE);
}
// Destruct the interpreter
PL_perl_destruct_level = 1;
perl_destruct(my_perl);
perl_free(my_perl);
}
return result;
}
extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpvReserved)
{
BOOL result(FALSE);
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
if (0 == g_initCount)
{
PERL_SYS_INIT3(0,
NULL,
NULL);
}
g_initCount++;
result = TRUE;
break;
case DLL_PROCESS_DETACH:
if (g_initCount > 0)
{
g_initCount--;
if (0 == g_initCount)
{
PERL_SYS_TERM();
}
}
result = TRUE;
break;
}
return result;
}
environmentVariables
上述代码片段中的格式是一个数组,char*
其中每个元素都在表单中,<variable name>=<variable value>
并且数组的最后一个元素是NULL
。
我运行的Perl脚本如下:
use strict;
use CGI qw/:standard/;
print "---- ENVIRONMENT ----\n";
for my $env (sort keys %ENV)
{
print "$env = $ENV{$env}\n";
}
print "\n";
例如,其中一次执行(在循环中)将以下环境传递给 C++ 函数:
- AUTH_TYPE =
- CONTENT_LENGTH = 47
- CONTENT_TYPE = application/x-www-form-urlencoded
- GATEWAY_INTERFACE = CGI/1.1
- PATH_INFO = /test.pl
- PATH_TRANSLATED = E:\Perl\PerlTestApplication\test.pl
- QUERY_STRING = lang=nl
- REMOTE_ADDR = 1.2.3.4
- REMOTE_HOST = remote.host
- REMOTE_USER =
- REQUEST_METHOD = POST
- SCRIPT_NAME = /test.pl
- SERVER_NAME = example.domain
- SERVER_PORT = 443
- SERVER_PROTOCOL = HTTP/1.1
- SERVER_SOFTWARE = Microsoft-IIS/10.0
然后脚本打印以下环境:
---- ENVIRONMENT ----
AUTH_TYPE =
CONTENT_LENGTH = 45
CONTENT_TYPE = application/x-www-form-urlencoded
GATEWAY_INTERFACE = CGI/1.1
PATH_INFO = /test.pl
PATH_TRANSLATED = E:\Perl\PerlTestApplication\test.pl
QUERY_STRING = lang=nl
REMOTE_ADDR = 1.2.3.4
REMOTE_HOST = remote.host
REMOTE_USER =
REQUEST_METHOD = POST
SCRIPT_NAME = /test.pl
SERVER_NAME = example.domain
SERVER_PORT = 443
SERVER_PROTOCOL = HTTP/1.1
SERVER_SOFTWARE = Microsoft-IIS/10.0
可以看出,CONTENT_LENGTH
变量的值不同,并且在 Perl 环境中的值与之前传递给脚本的环境相同。因此,不知何故,我传递给新的 Perl 解释器实例的环境没有被清理,仍然使用了另一个环境。我已经PERL_SET_CONTEXT
在构造后立即使用 来设置当前线程中的上下文,但这似乎还不够。
我已在 Active Perl 安装的 Perl 5.24 和 Strawberry Perl 安装的 Perl 5.30 上尝试过此操作,但都给出了相同的错误结果。
我做错了什么?