I write down a class to record results to xml files.When this multi-threads debug,it's get "attempt to read or write protected memory..." error.Why does the error display?
static private readonly object recordLock = new object();
private readonly object createLock = new object();
public static XMLHandle GetXMLHandle()
{
if (xmlHandle == null)
{
lock (lockHelper)
{
if (xmlHandle == null)
xmlHandle = new XMLHandle();
}
}
return xmlHandle;
}
public void RecordXML(string filePath, string fileName, string content, bool HaveLink)
{
System.Threading.Monitor.Enter(recordLock);<<======here
//...
System.Threading.Monitor.Exit(recordLock);
}
May be an invalid code generated by xml serializer?
Also VS debugger sometimes crashes on multithread debugging and you can do only few things to workaround it.
Ensure that you are not debugging an optimized binary (in Project Properties - Build - Optimize code).
Try switching between 32-64 bit.
Try toggling "Enable native code debugging".
Try toggling "Enable VS hosting process". I encountered a debugger problem myself and this fixed it.
Try switching between .NET frameworks (especially 3.5 and 4.0+).
Update your Visual Studio.
3 and 4 are located in the Debug tab of Project Properties.
Related
I'm debugging an IPC logic where one .NET 6 processes is launching another, and I can't get to debug both within the same Visual Studio 2022 instance.
When the child process hits DebugBreak(true) as below:
[Conditional("ATTACH_DEBUGGER")]
public static void DebugBreak(bool condition)
{
if (condition)
{
if (!System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Launch();
System.Diagnostics.Debugger.Break();
}
}
}
I get prompted to launch a new instance of VS2022 to debug it:
However, I want to debug it within the existing VS instance, were I'm already debugging the parent process.
Is that possible at all? Is there a VS config setting I might be missing?
Current workaround: attaching manually, while putting the child process in the waiting state using WaitForDebugger() below:
[Conditional("ATTACH_DEBUGGER")]
public static void WaitForDebugger()
{
using var process = Process.GetCurrentProcess();
Console.WriteLine($"Waiting for debugger, process: {process.ProcessName}, id: {process.Id}, Assembly: {Assembly.GetExecutingAssembly().Location}");
while (!System.Diagnostics.Debugger.IsAttached)
{
// hit Esc to continue without debugger
if (Console.KeyAvailable && Console.ReadKey(intercept: true).KeyChar == 27)
{
break;
}
Thread.Sleep(TimeSpan.FromSeconds(1));
Console.Beep(frequency: 1000, duration: 50);
}
}
I have a very similar setup where the following steps achieve this. However, this is with .NET Framework so I cannot guarantee it will work with .NET Core. (UPDATE: see below for .NET Core approach; see end for link to complete code samples on GitHub.)
First, identify a point in your first process (say, FirstProcess) where you want to attach the debugger to the second process (SecondProcess). Obviously SecondProcess will need to be running by this point (as SecondProcess.exe, say).
Open Solution Explorer and navigate to References for the relevant project in FirstProcess. Right-click, search for "env" and add two references, EnvDTE (v.8.0.0.0) and EnvDTE80 (v.8.0.0.0).
Add the following methods to the class in FirstProcess from where you want to attach:
private static void Attach(DTE2 dte)
{
var processName = "SecondProcess.exe";
var processes = dte.Debugger.LocalProcesses;
// Note: Depending on your setup, consider whether an exact match is required instead of using .IndexOf()
foreach (var proc in processes.Cast<EnvDTE.Process>().Where(proc => proc.Name.IndexOf(processName) != -1))
{
proc.Attach();
}
}
private static DTE2 GetCurrent()
{
// Note: "16.0" is for Visual Studio 2019; you might need to tweak this for VS2022.
var dte2 = (DTE2)Marshal.GetActiveObject("VisualStudio.DTE.16.0");
return dte2;
}
Visual Studio should now prompt you to add the following references to your class:
using System.Runtime.InteropServices;
using EnvDTE80;
Finally, insert the following line at the point in FirstProcess where you wish to attach the debugger to SecondProcess:
Attach(GetCurrent());
If you manage to get this working, please feel free to edit this answer with any changes needed for the .NET Core environment.
UPDATE - for .NET Core:
For .NET Core there are two problems to overcome:
The DTE references are not available in .NET Core. However, they can
be added as NuGet packages. You will need to add two NuGet
packages, envdte and envdte80 (both by Microsoft with 2M+
downloads), to FirstProcess.
The method Marshal.GetActiveObject() is not available in .NET
Core. To resolve this you can get the source code from Microsoft
(here) and add it manually; this has already been done in
the code sample below.
What follows is a complete working .NET Core code sample for FirstProcess. This correctly attaches programmatically to SecondProcess after startup.
namespace FirstProcess
{
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Threading;
using EnvDTE80;
class Program
{
private const string FileName = "C:\\Users\\YourUserName\\source\\repos\\TwoProcessesSolution\\SecondProcess\\bin\\Debug\\net5.0\\SecondProcess.exe";
private const string ProcessName = "SecondProcess";
static void Main(string[] args)
{
var childProcess = Process.Start(new ProcessStartInfo(FileName));
Attach(GetCurrent());
while (true)
{
// Your code here.
Thread.Sleep(1000);
}
childProcess.Kill();
}
private static void Attach(DTE2 dte)
{
var processes = dte.Debugger.LocalProcesses;
// Note: Depending on your setup, consider whether an exact match is required instead of using .IndexOf()
foreach (var proc in processes.Cast<EnvDTE.Process>().Where(proc => proc.Name.IndexOf(ProcessName) != -1))
{
proc.Attach();
}
}
private static DTE2 GetCurrent()
{
// Note: "16.0" is for Visual Studio 2019; you might need to tweak this for VS2022.
var dte2 = (DTE2)Marshal2.GetActiveObject("VisualStudio.DTE.16.0");
return dte2;
}
public static class Marshal2
{
internal const String OLEAUT32 = "oleaut32.dll";
internal const String OLE32 = "ole32.dll";
[System.Security.SecurityCritical] // auto-generated_required
public static Object GetActiveObject(String progID)
{
Object obj = null;
Guid clsid;
// Call CLSIDFromProgIDEx first then fall back on CLSIDFromProgID if
// CLSIDFromProgIDEx doesn't exist.
try
{
CLSIDFromProgIDEx(progID, out clsid);
}
// catch
catch (Exception)
{
CLSIDFromProgID(progID, out clsid);
}
GetActiveObject(ref clsid, IntPtr.Zero, out obj);
return obj;
}
//[DllImport(Microsoft.Win32.Win32Native.OLE32, PreserveSig = false)]
[DllImport(OLE32, PreserveSig = false)]
[ResourceExposure(ResourceScope.None)]
[SuppressUnmanagedCodeSecurity]
[System.Security.SecurityCritical] // auto-generated
private static extern void CLSIDFromProgIDEx([MarshalAs(UnmanagedType.LPWStr)] String progId, out Guid clsid);
//[DllImport(Microsoft.Win32.Win32Native.OLE32, PreserveSig = false)]
[DllImport(OLE32, PreserveSig = false)]
[ResourceExposure(ResourceScope.None)]
[SuppressUnmanagedCodeSecurity]
[System.Security.SecurityCritical] // auto-generated
private static extern void CLSIDFromProgID([MarshalAs(UnmanagedType.LPWStr)] String progId, out Guid clsid);
//[DllImport(Microsoft.Win32.Win32Native.OLEAUT32, PreserveSig = false)]
[DllImport(OLEAUT32, PreserveSig = false)]
[ResourceExposure(ResourceScope.None)]
[SuppressUnmanagedCodeSecurity]
[System.Security.SecurityCritical] // auto-generated
private static extern void GetActiveObject(ref Guid rclsid, IntPtr reserved, [MarshalAs(UnmanagedType.Interface)] out Object ppunk);
}
}
}
Note 1: This is for Visual Studio 2019 and .NET Core 5. I would expect this to work for VS2022 and .NET Core 6 with a single change to the Visual Studio version as annotated above.
Note 2: You will need to have only one instance of Visual Studio open (though if this isn't possible, the code is probably fixable quite easily).
Downloadable demo
Complete working examples for both .NET Framework (v4.7.2) and .NET Core (v5.0) are available on GitHub here: https://github.com/NeilTalbott/VisualStudioAutoAttacher
Getting a list of open windows in .Net Framework on Windows was relatively easy. How can I do the same in .Net Core/.Net 5 or later on macOS?
To clarify, I'm looking for a way to retrieve a list of all open windows owned by any running application/process. I don't have much experience of macOS development - I'm a Windows developer - but I've tried to use the NSApplication as suggested by this answer.
I created a .Net 6.0 Console application in VS2022 on macOS Monterey (12.2), added a reference to Xamarin.Mac and libxammac.dylib as described here - which describes doing this in Xamarin rather than .Net, but I don't see any other option to create a Console application. With the simple code:
static void Main(string[] args)
{
NSApplication.Init();
}
I get the output
Xamarin.Mac: dlopen error: dlsym(RTLD_DEFAULT, mono_get_runtime_build_info): symbol not found
I've no idea what this means. I'm not even sure this approach has any merit.
Does anyone know if it's possible to use NSApplication from a .Net Core/6.0 application, and if so whether NSApplication will give me the ability to read a system-wide list of open windows? If not, is there another way to accomplish this?
This is only for my own internal use, it doesn't need to be in any way portable or stable outside of my own environment.
In the link you refer to, there is an important note:
... as Xamarin.Mac.dll does not run under the .NET Core runtime, it only runs with the Mono runtime.
Because you try to run Xamarin.Mac.dll under .net-core, you get this dlopen error.
No System-wide List via NSApplication
The linked answer with NSApplication.shared.windows is incorrect if you want to read a system-wide list of open windows. It can only be used to determine all currently existing windows for the application from which the call is made, see Apple's documentation.
Alternative solution
Nevertheless, there are several ways to access the Window information in macOS. One of them could be a small unmanaged C-lib that gets the necessary information via CoreFoundation and CoreGraphics and returns it to C# via Platform Invoke (P/Invoke).
Native Code
Here is example code for a C-Lib that determines and returns the names of the window owners.
WindowsListLib.h
extern char const **windowList(void);
extern void freeWindowList(char const **list);
The interface of the library consists of only two functions. The first function called windowList returns a list with the names of the window owners. The last element of the list must be NULL so that you can detect where the list ends on the managed C# side. Since the memory for the string list is allocated dynamically, you must use the freeWindowList function to free the associated memory after processing.
WindowsListLib.c
#include "WindowListLib.h"
#include <CoreFoundation/CoreFoundation.h>
#include <CoreGraphics/CoreGraphics.h>
static void errorExit(char *msg) {
fprintf(stderr, "%s\n", msg);
exit(1);
}
static char *copyUTF8String(CFStringRef string) {
CFIndex length = CFStringGetLength(string);
CFIndex size = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
char *buf = malloc(size);
if(!buf) {
errorExit("malloc failed");
}
if(!CFStringGetCString(string, buf, size, kCFStringEncodingUTF8)) {
errorExit("copyUTF8String with utf8 encoding failed");
}
return buf;
}
char const **windowList(void) {
CFArrayRef cfWindowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
CFIndex count = CFArrayGetCount(cfWindowList);
char const **list = malloc(sizeof(char *) * (count + 1));
if(!list) {
errorExit("malloc failed");
}
list[count] = NULL;
for(CFIndex i = 0; i < count; i++) {
CFDictionaryRef windowInfo = CFArrayGetValueAtIndex(cfWindowList, i);
CFStringRef name = CFDictionaryGetValue(windowInfo, kCGWindowOwnerName);
if(name) {
list[i] = copyUTF8String(name);
} else {
list[i] = strdup("unknown");
}
}
CFRelease(cfWindowList);
return list;
}
void freeWindowList(char const **list) {
const char **ptr = list;
while(*ptr++) {
free((void *)*ptr);
}
free(list);
}
CGWindowListCopyWindowInfo is the actual function that gets the window information. It returns a list of dictionaries containing the details. From this we extract kCGWindowOwnerName. This CFStringRef is converted to a dynamically allocated UTF-8 string by the function copyUTF8String.
By convention, calls like CGWindowListCopyWindowInfo that contain the word copy (or create) must be released after use with CFRelease to avoid creating memory leaks.
C# Code
The whole thing can then be called on the C# side something like this:
using System.Runtime.InteropServices;
namespace WindowList
{
public static class Program
{
[DllImport("WindowListLib", EntryPoint = "windowList")]
private static extern IntPtr WindowList();
[DllImport("WindowListLib", EntryPoint = "freeWindowList")]
private static extern void FreeWindowList(IntPtr list);
private static List<string> GetWindows()
{
var nativeWindowList = WindowList();
var windows = new List<string>();
var nativeWindowPtr = nativeWindowList;
string? windowName;
do
{
var strPtr = Marshal.ReadIntPtr(nativeWindowPtr);
windowName = Marshal.PtrToStringUTF8(strPtr);
if (windowName == null) continue;
windows.Add(windowName);
nativeWindowPtr += Marshal.SizeOf(typeof(IntPtr));
} while (windowName != null);
FreeWindowList(nativeWindowList);
return windows;
}
static void Main()
{
foreach (var winName in GetWindows())
{
Console.WriteLine(winName);
}
}
}
}
The GetWindows method fetches the data via a native call to WindowList and converts the C strings to managed strings, then releases the native resources via a call to FreeWindowList.
This function returns only the owner names, such as Finder, Xcode, Safari, etc. If there are multiple windows, the owners will also be returned multiple times, etc. The exact logic of what should be determined will probably have to be changed according to your requirements. However, the code above should at least show a possible approach to how this can be done.
Screenshot
I am creating a project automation tool in Visual Studio 2013 where I have my own project template and I am trying to add it to an existing solution programatically.I am using the following code in a console application.
EnvDTE.DTE dte = (EnvDTE.DTE)Marshal.GetActiveObject("VisualStudio.DTE.12.0");
string solDir = dte.Solution.FullName;
solDir=solDir.Substring(0, solDir.LastIndexOf("\\"));
dte.Solution.AddFromTemplate(path, solDir+"\\TestProj", "TestProj", false);
It is working when I run the application from Visual Studio IDE. But when I try to run the exe from command prompt, I get the following exception.
Unhandled Exception: System.Runtime.InteropServices.COMException: Operation unav
ailable (Exception from HRESULT: 0x800401E3 (MK_E_UNAVAILABLE))
at System.Runtime.InteropServices.Marshal.GetActiveObject(Guid& rclsid, IntPtr reserved, Object& ppunk)
at System.Runtime.InteropServices.Marshal.GetActiveObject(String progID)
at ProjectAutomation.Console.Program.Main(String[] args)
I want to know whether there is any way to get the active EnvDTE.DTE instance outside Visual Studio IDE .?
Automating an existing Visual Studio instance from an external tool to modify a loaded solution is a bad idea. If you use GetActiveObject(...) and there are two Visual Studio instances launched, how do you know that the correct instance is returned? And what if the user or Visual Studio is doing something with the solution when the user launches the external tool? There are two better approaches:
1) Use an external tool to automate a new Visual Studio instance, load the desired solution and modify it. This can be done even with the VS instance not visible. To create a new instance the proper code is:
System.Type type = Type.GetTypeFromProgID("VisualStudio.DTE.12.0");
EnvDTE.DTE dte = (EnvDTE.DTE) System.Activator.CreateInstance(type);
dte.MainWindow.Visible = true;
...
2) Use a Visual Studio extension, such as a macro (VS 2010 or lower), add-in (VS 2013 or lower) or package (any VS version) to provide a menu item or button toolbar that, when clicked, modifies the currently loaded solution. This prevent the "busy" scenario because if VS is busy the menu item or toolbar button can't be clicked (unless the "busy" operation is asynchronous).
I found an alternative to GetActiveObject, here, where Kiril explains how to enumerate the ROT. There are other examples on MSDN.
Since some SO users don't like links here are the details:
Enumerate all of the processes, named devenv.exe.
Show a list of main window titles. (I strip "Microsoft Visual Studio" off the end)
Ask the user which one they want to use.
Use the process.Id to find an object in the ROT, which I believe is the OP's question. This is done by enumerating the ROT using IEnumMoniker.Next(), which returns monikers and process id's (in the case of VS).
Having found the moniker. Cast the running object to a DTE and off you go.
COM, ROT and Moniker sounded too complex for me, so I was happy to see that the heavy lifting had already been done at the link above.
I had the example working in a couple of minutes. It worked the first time I stepped through with the debugger. But at full speed, I needed to add some sleeps or retries, because it is easy to get an exception from HRESULT: 0x8001010A (RPC_E_SERVERCALL_RETRYLATER))
Also, I replaced the exact match with a regex that tolerates other versions of VS:
Regex monikerRegex = new Regex(#"!VisualStudio.DTE\.\d+\.\d+\:" + processId, RegexOptions.IgnoreCase);
The issue where VS may be busy, with an open dialog, or compiling many projects is common to many applications you might try to force feed key strokes or COM requests. If you get an error retry for a few seconds. Finally, pop up a message box if needed.
Some SO users don't like links because links get broken ;)
Took me over an hour to write my version so might as well post it here. Requires references to envdte and envte80 (from add references / assemblies / extensions). Provides static method for opening a C# file in Visual Studio or Notepad++ as a backup and optionally navigate to a specific line.
using System;
using System.Diagnostics;
using EnvDTE80;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.InteropServices;
namespace whatever
{
public static class CsFile
{
public static void Open(string fileName, int? lineNr = null)
{
try
{
OpenFileInVisualStudio(fileName, lineNr);
}
catch
{
try
{
OpenFileInNotePadPlusPlus(fileName, lineNr);
}
catch
{
// Woe is me for all has failed. Somehow show an error.
}
}
}
public static void OpenFileInVisualStudio(string fileName, int? lineNr = null)
{
DTE2 dte = null;
TryFor(1000, () => dte = GetDteByName("VisualStudio.DTE"));
if (dte == null) throw new Exception("Visual Studio not running?");
dte.MainWindow.Activate();
TryFor(1000, () => dte.ItemOperations.OpenFile(fileName));
if (lineNr.HasValue) TryFor(1000, () => ((EnvDTE.TextSelection)dte.ActiveDocument.Selection).GotoLine(lineNr.Value, true));
}
public static void OpenFileInNotePadPlusPlus(string fileName, int? lineNr = null)
{
if (lineNr.HasValue) fileName += " -n" + lineNr.Value.ToString();
Process.Start(#"C:\Program Files (x86)\Notepad++\notepad++.exe", fileName);
}
private static void TryFor(int ms, Action action)
{
DateTime timeout = DateTime.Now.AddMilliseconds(ms);
bool success = false;
do
{
try
{
action();
success = true;
}
catch (Exception ex)
{
if (DateTime.Now > timeout) throw ex;
}
} while (!success);
}
static DTE2 GetDteByName(string name)
{
IntPtr numFetched = Marshal.AllocHGlobal(sizeof(int));
IRunningObjectTable runningObjectTable;
IEnumMoniker monikerEnumerator;
IMoniker[] monikers = new IMoniker[1];
IBindCtx bindCtx;
Marshal.ThrowExceptionForHR(CreateBindCtx(reserved: 0, ppbc: out bindCtx));
bindCtx.GetRunningObjectTable(out runningObjectTable);
runningObjectTable.EnumRunning(out monikerEnumerator);
monikerEnumerator.Reset();
while (monikerEnumerator.Next(1, monikers, numFetched) == 0)
{
IBindCtx ctx;
CreateBindCtx(0, out ctx);
string runningObjectName;
monikers[0].GetDisplayName(ctx, null, out runningObjectName);
if (runningObjectName.Contains(name))
{
object runningObjectVal;
runningObjectTable.GetObject(monikers[0], out runningObjectVal);
DTE2 dte = (DTE2)runningObjectVal;
return (dte);
}
}
return null;
}
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
}
}
I am new here and I hope that i will find a solution for my problem. The background of the problem is as follows:
I am trying to build an expert system that constitute a C# front-end which is interacting with Swi-prolog.
I have downloaded SwiPlCs.dll (A CSharp class library to connect .NET languages with Swi-Prolog)
And added a reference to it in a Visual Studio project(Win form app) that I have created to test if I can query prolog from c# (I followed the example used in the documentation found here).
It worked fine.
Then, in a more complicated scenario, I have built a WCF service that will act as an intermediary layer between Swi-Prolog and C# client application (it consumes the service).
The service is hosted in IIS 7.0.
For the sake of simplicity, lets say my service contains three methods.
The first method initializes the prolog engine, consults prolog source file then queries the file.
The second method performs another query.
The third method calls PlCleanup().
Method#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);
}
Method#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);
}
}
Method#3:
public void QuitProlog()
{
if (PlEngine.IsInitialized)
{
PlEngine.PlCleanup();
}
}
The client invokes the first method just fine and a result of the first query is successfully returned. When client tries to call the second method an exception is thrown with message (attempted to read or write protected memory) which causes the application to freeze. I checked the event viewer and this is what I get:
Application: w3wp.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.AccessViolationException
Stack:
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)
I also tried to use the interface for a .NET project.
Looking in the official repository of the CSharp interface to SWI-Prolog I noticed that the project is very old and the latest updates do not seem included in the binaries available in the download page of the official website.
Then I did the following steps:
The contrib repository dedicated to .NET indicates that the compatible SWI-Prolog version (at the time of writing) is "8.0.3-1" (look in the README file).
-> Then I uninstalled from my computer the latest stable and installed the indicated one. I got it from the full list of downloads of the old versions at this link.
I cloned the SWI-Prolog/contrib-swiplcs repository, unloaded the incompatible projects from the solution, in my case, since I don't use Visual Studio.
-> I set the target framework to Net Framework 4.8 and recompiled it (you can also do this with standard NET). Beware of some pragma directives defined in the old project file (For example I re-defined _PL_X64 variable via code.
I brought the main unit test methods into a new project with xUnit wiht the appropriate changes.
I set the target to x64, recompiled and rebuilt the tests and the "hello world" example.
It worked!
I was able to use SWI-Prolog both for Net 4.8 and in other Net Core applications (if you make the needed changes in order to target the Net Standard). You should not have any problem in both cases).
This is my fork as a preliminary example.
Finally, I can load a *.pl Prolog file with a program in my C# application and use it to evaluate some business logic rules (example with boolean answer [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();
}
}
Try to close engine in the end of the first method and initialize it in the second again.
You can check this as the answer to the question unless you object.
I'm working in VS2012 with update 1 on a win2k8 r2 64 bit.
Within a simple class library application i do Add > New Item> ADO.NET Entity Data Model
I select a SQL Server on the network and select the database and add a single table. The table gets added, and I can access it as a class name in my code.
The issue: When I do anything with backend DB, the app using my library crashes with stackoverflow error (no exception). For instance this will crash: var logs =_db_context.LOGs.ToList();
Any ideas?
EDIT: The same projects were working in VS2010 on the same machine. This only started happening when I upgraded to VS2012 which upgraded entity framework as well. Also worth mentioning that if I remove the code the access the database, the app runs just fine.
Also, removing and re-adding .edmx does not help, neither does clean/re-build or restart VS.
EDIT2: After debugging I've noticed when the line LogServerEntities context = new LogServerEntities() is reached, and I try to expand the context variable from "Locals" VS ends debugging saying Managed (v4.0.30319)' has exited with code -2146233082 (0x80131506).
The class library was actually a custom trace listener and looked like following. When I commented the FirstChanceHandler in the constructor, the exception actually made its way to the console output: an assembly reference (System.Management.Automation) was failing to load. I did not really need that assembly and simply removed it, and the stackoverflow error (which I'm guessing is a bug) went away.
public Listener()
{
AppDomain.CurrentDomain.FirstChanceException += FirstChanceHandler;
}
public void FirstChanceHandler(object source, FirstChanceExceptionEventArgs e)
{
WriteException(e.Exception);
}
public void WriteException(Exception e)
{
string app_identity = System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name;
string server_name = System.Environment.MachineName;
using (LogServerEntities context = new LogServerEntities())
{
LOG log = new LOG();
log.DATE = DateTime.Now;
log.THREAD = Thread.CurrentThread.Name;
log.MESSAGE = e.Message;
log.LOGGER = string.Format("{0} {1}", app_identity, server_name);
log.LEVEL = Level.Exception.ToString();
log.EXCEPTION = e.GetType().FullName;
var web_exception = e as WebException;
if (web_exception != null)
{
if (web_exception.Status == WebExceptionStatus.ProtocolError)
{
var response = web_exception.Response as HttpWebResponse;
if (response != null)
log.HTTP_RESPONSE_CODE = ((int)response.StatusCode).ToString();
else
log.HTTP_STATUS = web_exception.Status.ToString();
}
else
{
log.HTTP_STATUS = web_exception.Status.ToString();
}
}
context.LOGs.Add(log);
context.SaveChanges();
}
}