【问题标题】:System.AccessViolationException in C# interface to Swi-prologSwi-prolog 的 C# 接口中的 System.AccessViolationException
【发布时间】:2014-08-20 04:02:26
【问题描述】:

我是新来的,希望能找到解决问题的方法。问题背景如下:

  • 我正在尝试构建一个专家系统,该系统构成一个与 Swi-prolog 交互的 C# 前端。
  • 我已经下载了SwiPlCs.dll(一个将 .NET 语言与 Swi-Prolog 连接起来的 CSharp 类库)
  • 并在我创建的 Visual Studio 项目(Win 表单应用程序)中添加了对它的引用,以测试我是否可以从 c# 查询 prolog(我按照文档中使用的示例找到了 here)。
  • 效果很好。
  • 然后,在一个更复杂的场景中,我构建了一个 WCF 服务,它将充当 Swi-Prolog 和 C# 客户端应用程序(它使用该服务)之间的中间层。
  • 服务托管在 IIS 7.0 中。
  • 为了简单起见,假设我的服务包含三种方法。
    • 第一种方法初始化prolog引擎,查询prolog源文件,然后查询文件。
    • 第二种方法执行另一个查询。
    • 第三个方法调用 PlCleanup()。

方法#1:

    public void LaunchAssessment()
    {
        Dictionary<string, string> questions = new Dictionary<string, string>();
        #region : Querying prolog using SwiPlCs
        try
        {
            if (!PlEngine.IsInitialized)
            {

                String[] param = { "-q" };
                PlEngine.Initialize(param);
                PlQuery.PlCall("consult('D:/My FYP Work/initialAssessment')");

                using (var q = new PlQuery("go(X, Y)"))
                {
                    foreach (PlQueryVariables v in q.SolutionVariables)
                    {
                        questions.Add("name", v["X"].ToString());
                        questions.Add("age", v["Y"].ToString());
                    }
                }
            }
        }
        catch (SbsSW.SwiPlCs.Exceptions.PlException exp)
        {
            throw new FaultException<PrologFault>(new PrologFault(exp.Source), exp.MessagePl);
        }

        #endregion

        Callback.PoseQuestion(questions, ResponseType.None);
    }    

方法#2:

public void DetermineAgeGroup(int age)
    {            
        //Determine age group
        string age_group = string.Empty;
        try
        {
            using (var query = new PlQuery("age_group(" + age + ", G)"))  
                {
                    foreach (PlQueryVariables v in query.SolutionVariables)
                        age_group += v["G"].ToString();
                }                
        }
        catch (SbsSW.SwiPlCs.Exceptions.PlException exp)
        {
            throw new FaultException<PrologFault>(new PrologFault(exp.Source), exp.MessagePl);
        }

        //Check whether age_group is found or not
        if (string.IsNullOrEmpty(age_group))
        {
            throw new FaultException<NoSolutionFoundFault>(new NoSolutionFoundFault("No solution found"), "Age specified exceeds the diagnosis range!");
        }
        else
        {
            Callback.RespondToUser(age_group, ResponseType.Age);
        }

    }

方法#3:

    public void QuitProlog()
    {
        if (PlEngine.IsInitialized)
        {
            PlEngine.PlCleanup();
        }
    }

客户端调用第一个方法就好了,第一个查询的结果成功返回。当客户端尝试调用第二种方法时,会引发异常并带有消息(尝试读取或写入受保护的内存),这会导致应用程序冻结。我检查了事件查看器,这就是我得到的:

    Application: w3wp.exe
    Framework Version: v4.0.30319
    Description: The process was terminated due to an unhandled exception.
    Exception Info: System.AccessViolationException

堆栈:

    at SbsSW.SwiPlCs.SafeNativeMethods.PL_new_term_ref()
    at SbsSW.SwiPlCs.PlQuery..ctor(System.String, System.String)
    at SbsSW.SwiPlCs.PlQuery..ctor(System.String)
    at PrologQueryService.PrologQueryService.DetermineAgeGroup(Int32)

【问题讨论】:

  • 在哪一行抛出异常?
  • 在调用DetermineAgeGroup(int age) 方法时。在这一行中:使用 (var query = new PlQuery("age_group(" + age + ", G)"))
  • 您的 WCF 服务在哪种实例化模式下工作(PerCall、Single、Per-Session)?
  • 设置为 Per-Session 且 ConcurrencyMode 为单一
  • 嗯。 PerSession 和 Single 并发模式是正确的。尝试编译所有针对 x86 平台的程序集。有时,此类互操作性问题可能是由针对 x64 平台的编译引起的。

标签: c# .net wcf swi-prolog


【解决方案1】:

我还尝试将接口用于 .NET 项目。

查看 SWI-Prolog 的 CSharp 接口的official repository,我注意到该项目非常老旧,并且官方网站下载页面中提供的二进制文件中似乎没有包含最新更新。

然后我做了以下步骤:

  • .NET 专用的 contrib 存储库表明兼容的 SWI-Prolog 版本(在撰写本文时)是“8.0.3-1”(查看README file)。 -> 然后我从我的电脑上卸载了最新的稳定版并安装了指定的。我是从 link 的旧版本的完整下载列表中获得的。

  • 我克隆了 SWI-Prolog/contrib-swiplcs 存储库,从解决方案中卸载了不兼容的项目,就我而言,因为我不使用 Visual Studio。 -> 我将目标框架设置为 Net Framework 4.8 并重新编译它(您也可以使用标准 NET 执行此操作)。注意旧项目文件中定义的一些杂注指令(例如,我通过代码重新定义了 _PL_X64 变量。

  • 我将主要的单元测试方法引入了一个带有 xUnit 的新项目,并进行了适当的更改。

  • 我将目标设置为 x64,重新编译并重建了测试和“hello world”示例。

成功了! 我能够在 Net 4.8 和其他 Net Core 应用程序中使用 SWI-Prolog(如果您为了针对 Net Standard 进行了必要的更改)。在这两种情况下你都不应该有任何问题)。

这是我的fork 作为初步示例。

最后,我可以在我的 C# 应用程序中加载带有程序的 *.pl Prolog 文件,并使用它来评估一些业务逻辑规则(例如布尔答案 [Permitted/Not-Permitted]):

[Fact]
public void ShouldLoadAProgramAndUseIt()
{
    var pathValues = Environment.GetEnvironmentVariable("PATH");
    pathValues += @";C:\Program Files\swipl\bin";
    Environment.SetEnvironmentVariable("PATH", pathValues);

    // Positioning to project folder
    var currentDirectory = Directory.GetCurrentDirectory().Split('\\').ToList();
    currentDirectory.RemoveAll(r => currentDirectory.ToArray().Reverse().Take(3).Contains(r));
    var basePath = currentDirectory.Aggregate((c1, c2) => $"{c1}\\{c2}");
    
    var filePath = $"{basePath}\\prolog_examples\\exec_checker.pl";
    
    String[] param = { "-q", "-f", filePath };
    PlEngine.Initialize(param);
    try
    {
        var query = "exutable('2020-08-15',[('monthly', ['2019-12-30', '2020-03-10'])])";
        _testOutputHelper.WriteLine($"Query: {query}");
        
        using (var q = new PlQuery(query))
        {
            var booleanAnswer = q.NextSolution();
            _testOutputHelper.WriteLine($"Answer: {booleanAnswer}");
            Assert.True(booleanAnswer);
        }

        query = "exutable('2020-08-15',[('daily', ['2019-12-30', '2020-08-15'])])";
        _testOutputHelper.WriteLine($"Query: {query}");
        
        using (var q = new PlQuery(query))
        {
            var booleanAnswer = q.NextSolution();
            _testOutputHelper.WriteLine($"Answer: {booleanAnswer}");
            Assert.False(booleanAnswer);
        }
    }
    finally
    {
        PlEngine.PlCleanup();
    }
}

【讨论】:

    【解决方案2】:

    尝试在第一种方法结束时关闭引擎,并在第二种方法中再次初始化。

    除非您反对,否则您可以将此作为问题的答案。

    【讨论】:

    • “尝试”是否被认为是一个答案?你自己试过吗?虽然关闭和重新打开是一种似乎可行的解决方法(参见上面的 cmets),但是否还有一个可能不涉及关闭和重新打开的真正解决方案?是否可以在某处报告错误?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多