I have a C# program that has been crashing after a variable amount of time. I've tracked it down to OpenALPR and have now duplicated the problem in a test program.
I basically ask to get the plates from an image in a while loop. It fails after a bunch of iterations. Failed after Iterations : 179, 221, 516, 429, 295, 150
Program output:
...
Iter (219) No Plates Found in image 71197e9d829d4d429e74a71c983380dc_09032015
134103267.jpg
Config file location provided via API
LBP Time: 0.005ms.
Total Time to process image: 1.916ms.
Iter (220) No Plates Found in image 71197e9d829d4d429e74a71c983380dc_09032015
134103267.jpg
Config file location provided via API
LBP Time: 0.003ms.
Total Time to process image: 4.071ms.
Iter (221) No Plates Found in image 71197e9d829d4d429e74a71c983380dc_09032015
134103267.jpg
Config file location provided via API
Failure message:
Unhandled Exception:
Unhandled Exception: System.AccessViolationException: Attempted to read or write
protected memory. This is often an indication that other memory is corrupt.
at openalprnet.AlprNet.Dispose(Boolean )
System.AccessViolationException: Attempted to read or write protected memory. Th
is is often an indication that other memory is corrupt.
at alpr.Alpr.{ctor}(Alpr* , basic_string<char\,std::char_traits<char>\,std::a
llocator<char> >* , basic_string<char\,std::char_traits<char>\,std::allocator<ch
ar> >* , basic_string<char\,std::char_traits<char>\,std::allocator<char> >* )
at openalprnet.AlprNet..ctor(String country, String configFile, String runtim
eDir)
at AlprTest.Program.Main(String[] args) in C:\Users\foo\Desktop\c#LPR\A
lprTest\Program.cs:line 25
One time, I also got part of another error message(not sure if its related or not) : Unable to load regex: ######. Although the error above is pointing to the CTOR, I have, in my normal application, had it fail during the recognize call. I've also seen (not sure how accurate these stack traces are) it in openalprnet.AlprNet.Dispose(Boolean) which was called from alpr.Alpr.{ctor}(...
My test program :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using openalprnet;
namespace AlprTest
{
class Program
{
static void Main(string[] args)
{
string chipPath = "71197e9d829d4d429e74a71c983380dc_09032015134103267.jpg";
string confPath = Path.GetFullPath(".\\openalpr.conf");
string runtimeDirPath = Path.GetFullPath(".\\runtime_data");
int i = 0;
while (true)
{
++i;
try
{
// Look at target velocity and pick a conf file to use.
//
AlprNet alpr = new AlprNet("us", confPath, runtimeDirPath);
if (!alpr.isLoaded())
{
return;
}
// Optionally apply pattern matching for a particular region
alpr.DefaultRegion = "va"; // was md
alpr.DetectRegion = true;
AlprResultsNet results = alpr.recognize(chipPath);
if (results.plates.Count < 1)
{
Console.WriteLine(" Iter ({1}) No Plates Found in image {0}", chipPath, i);
}
else
{
int j = 0;
foreach (var result in results.plates)
{
Console.WriteLine("Plate {0}: {1} result(s)", ++j, result.topNPlates.Count);
Console.WriteLine(" Processing Time: {0} msec(s)", result.processing_time_ms);
foreach (var plate in result.topNPlates)
{
Console.WriteLine(" - {0}\t Confidence: {1}\tMatches Template: {2}", plate.characters,
plate.overall_confidence, plate.matches_template);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception caught in LPR processing. Ex={0}", ex);
return;
}
}
}
}
}
The program depends on the openalpr distribution and corresponding opencv dlls. openalpr-net.dll, liblept170.dll, opencv_core248.dll, opencv_features2d248.dll, opencv_ffmpeg248.dll, opencv_flann248.dll, opencv_highgui248.dll, opencv_imgproc248.dll, opencv_objdetect248.dll, opencv_video248.dll. It also uses a runtime_data directory (which I simply copied from the sample app) which seems to contain training data and such.
So, obviously, I'm using C#. I'm using Visual Studio 2010 and .NET 4.0
I'm assuming that I'm using OpenALPR wrong and that there is, in fact, nothing wrong with it. This would seem to be a pretty basic function. Aside from fixing it... why does this crash my program and how can I catch this and recover? You notice my try-catch totally fails to catch it and it crashes the whole application.
EDIT :
While running the test app, it starts with like 2gig of memory, but it just grows and grows and grows. It crashed with 7.7 gig after 147 loops.
EDIT EDIT :
Added in call to Dispose after each iteration and now the program sits pretty steady at 50-75 megs of memory.
Turns out the problem was the Dispose.
Need to Dispose of the object. Better yet, don't dispose of the object and re-use it. So long as the config doesn't change you can reuse the object. Sadly, the prewarp is in the config so you likely won't be able to reuse the object. Call Dispose before the object leaves scope.
try
{
// Look at target velocity and pick a conf file to use.
//
AlprNet alpr = new AlprNet("us", confPath, runtimeDirPath);
if (!alpr.isLoaded())
{
return;
}
// Optionally apply pattern matching for a particular region
alpr.DefaultRegion = "va"; // was md
alpr.DetectRegion = true;
AlprResultsNet results = alpr.recognize(chipPath);
...
alpr.Dispose(); // Dispose of the object so it can clean up
}
Related
Development Environment: .Net Framework 4.7.2 using VS 2022 on Win 10 Pro x64
Preface: I've reviewed the two similar questions I found at SO; the first deals with permissions and the second with restrictions on using the root directory. Neither contained info that enabled me to resolve my issue.
I'm working on a C# winforms app which uses a SQLite database. I recently discovered "PRAGMA integrity_check" will create an empty DB and return “ok” if the target DB file is missing so I need to ensure the file’s not gone missing before executing the PRAGMA. My simple solution is to wrap integrity_check in an IF (File.Exist) ELSE but the Exist method is returning ”false”.
In MSDN documentation there 7 stated reasons why a false might be returned in addition to the file actually not existing (listed to avoid the need to follow a link):
path is null
path is invalid
path exceeds maximum length (260)
path is a zero-length string
path has invalid characters
storage media is failing/missing
caller has insufficient permissions to read the specified file
My operating assumption is none of those are the root cause since I can read from and write to the DB programmatically in the app.
Code building the path:
namespace BURS_Library
{
public class MISC
{
public const string DBName = "BURS.db";
}
}
using System;
using System.IO;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
namespace BURS_Library
{
public class BURS_Path
{
public static string AppData()
{
string userAppDataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
userAppDataDir = userAppDataDir.Replace("Roaming", "LocalLow");
if ( ! Directory.Exists(Path.Combine(userAppDataDir, "BURS_Data_tst")))
{
// display error MessageBox
Environment.Exit(1);
}
return Path.Combine(userAppDataDir, "BURS_Data_tst");
}
public static string DB()
{
return Path.Combine(AppData(), MISC.DBName);
}
{
}
Resultant path: C:\Users\Art\AppData\LocalLow\BURS_Data_tst\BURS.db
Code with File.Exist
using _Library;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
namespace BURS_UI
{
public static class Program
{
[STAThread]
public static void Main(string[] tsArgs)
{
if (File.Exists(BURS_Path.DB()))
{
// perform db Integrity Check
}
else
{
// display error MessageBox
Environment.Exit(2);
}
BURS_Connections.SetConnection(BURS_Path.DB());
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Discover());
}
}
}
If my operating assumption is valid why is File.Exist returning false?
Thank you for your time & expertise.
Following #BentTranberg's suggestion a test was run using the following code (in case its useful to somebody):
if (Directory.Exists(#"C:\Users"))
Save2Log(#"FOUND: C:\Users", true);
if (Directory.Exists(#"C:\Users\Art"))
Save2Log(#"FOUND: C:\Users\Art", true);
if (Directory.Exists(#"C:\Users\Art\AppData"))
Save2Log(#"FOUND: C:\Users\Art\AppData", true);
if (Directory.Exists(#"C:\Users\Art\AppData\LocalLow"))
Save2Log(#"FOUND: C:\Users\Art\AppData\LocalLow", true);
if (Directory.Exists(#"C:\Users\Art\AppData\LocalLow\BURS_Data_tst"))
Save2Log(#"FOUND: C:\Users\Art\AppData\LocalLow\BURS_Data_tst", true);
if (File.Exists(#"C:\Users\Art\AppData\LocalLow\BURS_Data_tst\BURS.db"))
Save2Log(#"FOUND: C:\Users\Art\AppData\LocalLow\BURS_Data_tst\BURS.db", true);
Save2Log($"METHOD: {BURS_Path.DB()}", true);
Which produced the following result:
FOUND: C:\Users
FOUND: C:\Users\Art
FOUND: C:\Users\Art\AppData
FOUND: C:\Users\Art\AppData\LocalLow
FOUND: C:\Users\Art\AppData\LocalLow\BURS_Data_tst
FOUND: C:\Users\Art\AppData\LocalLow\BURS_Data_tst\BURS.db
METHOD: C:\Users\Art\AppData\LocalLow\BURS_Data_tst\BURS.db
Next I reran my original code which surprisingly now worked as expected. To validate that result I ran more test:
int existFail = 0;
for (int i = 0; i < 10000; i++)
{
if ( ! File.Exists(BURS_Path.DB())) existFail++;
}
Save2Log($"number of exist fail in 10,000 = {existFail}", true);
I did that 5 times and in 50,000 iterations there were zero incorrect returns. At this point the error has not been reproduced.
My computer was shut down over night which may have impacted the findings. I will rerun this each morning for the next 3 days and post the results as an edit.
Should we have something with the External application to properly register the event?
I also tried putting two breakpoints one inside the start module and other inside the Export module.
the first responded and waited for me to continue and the next didn't respond(hope did not run the line)
Also,I had manually tried coping the addin file to the addin location to avoid any post build event error but still doesnt seem to work.
could you tell me what I am I doing wrong here.
Here is the code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.UI.Events;
using Autodesk.Revit.DB.Events;
using System.IO;
namespace UserDataSheet
{
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class UserDataSheetclass : IExternalApplication
{
public Result OnShutdown(UIControlledApplication application)
{
return Result.Succeeded;
}
public Result OnStartup(UIControlledApplication application)
{
try
{
// Register event.
application.ControlledApplication.DocumentOpened += new EventHandler<Autodesk.Revit.DB.Events.DocumentOpenedEventArgs>(ExportLog);
return Result.Succeeded;
}
catch (Exception)
{
return Result.Failed;
}
}
public void ExportLog(object sender, DocumentOpenedEventArgs args)
{
var doc = sender as Document;
var isFamilyDoc = doc.IsFamilyDocument;
// variables to use
string RevitUserName = "";
DateTime OpenTime = new DateTime();
string localUserName = "";
string filename = "";
string filepath = "";
string content = "";
if (isFamilyDoc == false)
{
var IsloggedIn = Autodesk.Revit.ApplicationServices.Application.IsLoggedIn;
if (IsloggedIn == true )//&& doc.IsModelInCloud == true)
{
// use variables
filepath = doc.PathName;
filename = doc.Title;
RevitUserName = doc.Application.Username;
OpenTime = DateTime.Now;
localUserName = System.Environment.MachineName;
}
content = string.Format("Date and time : {0} \n Revit UserName : {1} \n Local PC UserName : {2} \n FileName : {3} \n FilePath : {4} "
, OpenTime.ToString(), RevitUserName, localUserName, filename, filepath);
TaskDialog.Show("Model Open Writer info", "user and file details : \n " + content);
}
var writefilepath = Path.GetTempPath();
var Writefile = writefilepath + "//records.txt";
FileStream fs = new FileStream(Writefile, FileMode.Append);
StreamWriter writer = new StreamWriter(fs);
writer.Write(content);
writer.Close();
File.OpenRead(Writefile);
}
}
}
First of all, you can completely remove the TransactionMode and RegenerationOption. The latter is completely obsolete and does nothing at all anywhere whatsoever. The former is only useful when declaring an external command. It is useless and ignored in the context of an external application.
Secondly, to address your question: you can set a breakpoint in the beginning of OnStartup. If the breakpoint is not hit, your add-in is not being loaded at all. Probably something is wrong with your add-in registration, e.g., in the add-in manifest *.addin file.
Go back to square one, i.e., work through the getting started material and the developer guide instructions on registering and loading a Revit add-in.
If the breakpoint in OnStartup is hit, then your add-in is loading correctly, which means that the add-in manifest *addin file is OK. So, you do not need to worry about that. The VisibilityMode tag is not used for external applications, by the way.
Thanks, Jeremy It worked
Firstly I apologies for adding this as answer( I don't know how to add codes in comment)
It worked when I deleted my Addin file and recreated it may be I had made some mistake in it.
meanwhile I have copied the following code from examples and used it,honestly I did't understand this line of the code.
"public void ExportLog(object sender, DocumentOpenedEventArgs args)"
can you point to a right source that explains this part. I have three questions here :
what object type is sender and args are they of type application?
How do I add a 3rd parameter to this method say I want the user to input a string to name the file the data is copied to.
Can I do this
var newEvent = new EventHandler<Autodesk.Revit.DB.Events.DocumentOpenedEventArgs>(ExportLog);
instead of
application.ControlledApplication.DocumentOpened += new EventHandler<Autodesk.Revit.DB.Events.DocumentOpenedEventArgs>(ExportLog);
why does all example use += is this to register the event every time a new instance of Revit is opened?
Thanks for your help.
You can see the class of sender yourself by setting a breakpoint at the beginning of ExportLog and looking in the debugger.
No, you cannot modify the signature of the event handler. It is predetermined by the Revit API.
Yes.
It sounds to me as if you might save some time and effort for yourself by learning a bit more about the basics of C# and .NET programming in general before continuing to tackle this task.
I am trying to interface C# to R using RDotNet.
The following code is wants R to calculate the sum of two numbers and C# to get the result back and display it in the command window.
using System;
using RDotNet;
namespace rcon
{
class Program
{
static void Main(string[] args)
{
string dllPath = #"C:\Program Files\R\R-3.1.0\bin\i386";
REngine.SetDllDirectory(dllPath);
REngine.CreateInstance("RDotNet");
//REngine engine = REngine.GetInstanceFromID("RDotNet");
using (REngine engine = REngine.GetInstanceFromID("RDotNet"))
{
var x = engine.Evaluate("x <- 1 + 2");
Console.WriteLine(x);
}
}
}
}
but when I try to send the command to R and get back the calue in x I got an error:
"InvalidOperationException was unhandled"
"Operation is not valid due to the current state of the object."
If I explore the object "engine" I see that IsRunning=false.
Can this be the problem? And how can I fix this in order to be able to interface to R?
It looks like you have outdated version of R.NET.
From R.NET project documentation
R.NET 1.5.10 and subsequent versions include significant changes
notably to alleviate two stumbling blocks often dealt with by users:
paths to the R shared library, and preventing multiple engine
initializations.
You can update your R.NET using NuGet manager from Visual Studio. See the same documentation page for detals.
Here is code sample from the same documentatin page - note that initialization of REngine is significantly simpler now (as now Rengine looks at the Registry settings set up by the R installer):
REngine.SetEnvironmentVariables(); // <-- May be omitted; the next line would call it.
REngine engine = REngine.GetInstance();
// A somewhat contrived but customary Hello World:
CharacterVector charVec = engine.CreateCharacterVector(new[] { "Hello, R world!, .NET speaking" });
engine.SetSymbol("greetings", charVec);
engine.Evaluate("str(greetings)"); // print out in the console
string[] a = engine.Evaluate("'Hi there .NET, from the R engine'").AsCharacter().ToArray();
Console.WriteLine("R answered: '{0}'", a[0]);
Console.WriteLine("Press any key to exit the program");
Console.ReadKey();
engine.Dispose();
I'm trying to read the static securities definition file from the CME, located at:
ftp://ftp.cmegroup.com/fix/Production/secdef.dat.gz
Since they seem to be standard fix messages, I thought I could use QuickFix to help me read them into C# rather than parsing the file myself. I created a test app that basically does what I want, but I'm having 2 issues:
1) I'm getting a QuickFix exception "Invalid message: Header fields out of order" when forming the message from the string. If I set the "validate" boolean to false, this message disappears and the constructor succeeds, but may be an indicator for the next issue.
2) Upon calling p.Crack, I'm getting the QuickFix exception "QuickFix.UnsupportedMessageType", but there doesn't seem to be any indication of what the message type is that is supposedly unsupported.
Anyway, maybe QuickFix wasn't intended to be used in this way, but any ideas on how to get this to work?
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using QuickFix;
namespace TestQuickFix
{
class Program : QuickFix.MessageCracker
{
static void Main(string[] args)
{
int count = 0;
string line;
Program p = new Program();
StreamReader file = new StreamReader(#"C:\secdef.dat");
while (((line = file.ReadLine()) != null && count < 10))
{
// ISSUE #1 REQUIRES false 2ND ARG WHEN CREATING THE MESSAGE
Message m = new Message(line, false);
// ISSUE #2 Exception of type 'QuickFix.UnsupportedMessageType' was thrown.
p.Crack(m, new SessionID("beginString", "senderCompID", "targetCompID"));
}
file.Close();
}
public void OnMessage(QuickFix.FIX50.SecurityDefinition secDef, SessionID sessionID)
{
Console.WriteLine(secDef.ToString());
}
}
}
The messages seems to be in FIX50sp2 format, supported by QuickFIX. (Please take a look at the tag 1128=9).
http://www.onixs.biz/fix-dictionary/5.0.SP2/tagNum_1128.html
BUT every single message seems to be not-well formatted. In the header are missed tag 8 (should be the BeginString), and also the tag 56 (TargetCompID), that are mandatory.
Therefore in order to load a single line in a message you must put the "false" parameter to avoid validation.
I suppose the second error is related to the not-well formatted messages.
After emailing the QuickFix listserv with this question, I was able to get enough information to get this to work. Although each line still seems to be malformed for some reason, if I keep validation off, I can get the parser to do exactly what I need it to with the following simplified code:
using System;
using System.IO;
using QuickFix;
using QuickFix.DataDictionary;
namespace TestQuickFix
{
class Program
{
private const int MAX_LINES = 10;
static void Main(string[] args)
{
DataDictionary dd = new QuickFix.DataDictionary.DataDictionary("fix\\FIX50SP2.xml");
StreamReader file = new StreamReader(#"C:\secdef.dat");
int count = 0; string line;
while (((line = file.ReadLine()) != null && count++ < MAX_LINES))
{
QuickFix.FIX50.SecurityDefinition secDef = new QuickFix.FIX50.SecurityDefinition();
secDef.FromString(line, false, dd, dd);
Console.WriteLine(secDef.SecurityDesc);
}
file.Close();
}
}
}
In the following test program, the error output is correctly written to Error.txt.
using System;
using System.IO;
public class Test{
public static void Main(string[] args){
Console.SetOut(new StreamWriter("Output.txt", true));
Console.SetError(new StreamWriter("Error.txt", true));
int[] test = new int[1];
Console.Error.WriteLine(test[0]);
}
}
However, if we change the line
Console.Error.WriteLine(test[0]);
to
Console.Error.WriteLine(test[7]);
which will cause an exception, the error message for this exception gets printed to the console instead of to the file. How can I programmatically set it up so that the error message for system-thrown exceptions is also redirected to a file?
Simple console redirection (2> or >) is not an option because of the context in which this program is run.
I don't think the Console.SetError method actually changes the "DOS" error output of the application, just where Console.Error outputs. You should try PInvoking SetStdHandle. For example of this, see here: Redirect stdout+stderr on a C# Windows service
I believe, error output doesn't work the way you try using it. It is just a way to output information about some errors. Look at this http://msdn.microsoft.com/en-us/library/system.console.seterror.aspx
If you want to write something about an error, you need to do the next:
Console.Error.WriteLine("Error Log for Application {0}", appName);
so if you want to write info about exceptions to stderr, you need to catch your exceptions and direct its messages to stderr.
...
catch(Exception e) { Console.Error.WriteLine(e.Message); }
Are you looking for something like this?
FileStream errorFileStream = new FileStream("Error.txt", FileMode.OpenOrCreate);
StreamWriter errorStreamWriter = new StreamWriter(errorFileStream);
Console.SetError(errorStreamWriter);
string[] test = new string[2];
test[0] = "Hello";
test[1] = "World";
try
{
Console.WriteLine(test[2]);
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message);
}
Console.Error.Close();