簡體   English   中英

Swi-prolog 的 C# 接口中的 System.AccessViolationException

[英]System.AccessViolationException in C# interface to Swi-prolog

我是新來的,我希望我能找到解決我問題的方法。 問題的背景如下:

  • 我正在嘗試構建一個專家系統,該系統構成與 Swi-prolog 交互的 C# 前端。
  • 我已經下載了SwiPlCs.dll (一個 CSharp 類庫,用於將 .NET 語言與 Swi-Prolog 連接起來)
  • 並在我創建的 Visual Studio 項目(Win 表單應用程序)中添加了對它的引用,以測試我是否可以從 c# 查詢 prolog(我遵循了此處找到的文檔中使用的示例)。
  • 它工作得很好。
  • 然后,在更復雜的場景中,我構建了一個 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)

我還嘗試將接口用於 .NET 項目。

在查看 SWI-Prolog 的 CSharp 接口的官方存儲庫中,我注意到該項目很舊,並且官方網站的下載頁面中提供的二進制文件中似乎沒有包含最新的更新。

然后我做了以下步驟:

  • 專用於 .NET 的 contrib 存儲庫表明兼容的 SWI-Prolog 版本(在撰寫本文時)為“8.0.3-1”(查看自述文件)。 -> 然后我從我的計算機上卸載了最新的穩定版並安裝了指定的穩定版。 我是從這個鏈接上舊版本的完整下載列表中得到的。

  • 我克隆了SWI-Prolog/contrib-swiplcs存儲庫,從解決方案中卸載了不兼容的項目,就我而言,因為我不使用 Visual Studio。 -> 我將目標框架設置為 Net Framework 4.8 並重新編譯它(您也可以使用標准 NET 執行此操作)。 注意舊項目文件中定義的一些 pragma 指令(例如,我通過代碼重新定義了_PL_X64變量。

  • 我將主要的單元測試方法帶入了一個帶有 xUnit 的新項目,並進行了適當的更改。

  • 我將目標設置為 x64,重新編譯並重新構建測試和“hello world”示例。

有效! 我能夠將 SWI-Prolog 用於 Net 4.8 和其他 Net Core 應用程序(如果您進行了必要的更改以針對 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();
    }
}

嘗試在第一種方法結束時關閉引擎並在第二種方法中再次初始化它。

除非您反對,否則您可以將此作為問題的答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM