Within the code I'm responsible for, I have a few 'throw new Exception()' methods flying around.
The issue is, the catch is not always in the best place or even in the same class. This means when I catch an error, I don't know where the throw originated or even if I do, there could be hundreds/thousands of lines of code in each class.
So, within the throw message, for debugging, it may be useful to state the Class Name, Project Name, Method Name and the line number for easier navigation. The line number is tricky. I can't hard code it because as soon as I amend the code it's unlikely to remain on the same line.
So, my 2 questions are
1) Do we like this idea or think no, there are better approaches!
2) Any ideas how to get the line number?
Have you considered looking at the StackTrace information found under System.Diagnostics? An example can be found at:
http://msdn.microsoft.com/en-us/library/system.diagnostics.stacktrace.aspx
If you include the debugging symbols (.pdb) files, in the same directory as the .dll or .exe, it should load them automatically and provide the line number in the exception.StackTrace.
To build the symbols, in your release build settings, under Advanced Build Settings, turn Debug Info to Full
Look at the System.Diagnostics.StackFrame class for grabbing line numbers. I believe the method GetFileLineNumber may help you out
http://msdn.microsoft.com/en-us/library/system.diagnostics.stackframe
This info is usually available in the stack trace - By Jeremy (see my comments).
Sorry, not sure how to mark a comment as the right answer!?
Line numbers do not show up in code compiled in Release mode. If this is an in-house application you and you really want the line numbers you could always deploy the code compiled in Debug mode and then deploy the PDB's with the assemblies. But there is a performance cost involved in this as well so this is not always the best approach. I am not sure of any better approach at this point though.
If I remember correctly, the Roslyn project gives us a better way to get line numbers but not familiar enough with it to give more details.
You can capture the line number of a caller using C# 5.0 feature combined with default parameters. So instead of constructing and throwing the exception directly, make a method that constructs your exception.
Exception CreateMyException(
[CallerFilePath] string filePath = "",
[CallerMemberName] string memberName = "",
[CallerLineNumber] int lineNumber = 0)
{
return new Exception(string.Format("Exception thrown from line {0} in member {1} in file {2}",
lineNumber, memberName, filePath));
}
...elsewhere in your code...
throw CreateMyException(); // compiler injects current values for defaulted arguments.
Related
I am trying to figure out if there is a more efficient way than what I'm doing now to build up a message coming in on a serial port and validate it is the right message before I parse it. A complete message starts with a $ and ends with a CR/LF. I use an event handler to get the characters as they show up at the serial port so the message will not necessarily come in as one complete block. Just to confuse things, there are a bunch of other messages that come in on the serial port that don't necessarily start with a $ or end with a CR/LF. I want to see those but not parse them. I understand that concatenating strings is probably not a good idea so I use a StringBuilder to build the message then I use a couple of .ToString() calls to make sure I've got the right message to parse. Do the .ToString calls generate much garbage? Is there a better way?
I'm not a particularly experienced programmer so thanks for the help.
private void SetText(string text)
{
//This is the original approach
//this.rtbIncoming.Text += text;
//First post the raw data to the console rtb
rtbIncoming.AppendText(text);
//Now clean up the text and only post messages to the CPFMessages rtb that start with a $ and end with a LF
incomingMessage.Append(text);
//Make sure the message starts with a $
int stxIndex = incomingMessage.ToString().IndexOf('$');
if (stxIndex == 0)
{ }
else
{
if (stxIndex > 0)
incomingMessage.Remove(0, stxIndex);
}
//If the message is terminated with a LF: 1) post it to the CPFMessage textbox,
// 2) remove it from incomingMessage,
// 3) parse and display fields
int etxIndex = incomingMessage.ToString().IndexOf('\n');
if (etxIndex >= 0)
{
rtbCPFMessages.AppendText(incomingMessage.ToString(0, etxIndex));
incomingMessage.Remove(0, etxIndex);
parseCPFMessage();
}
}
Do the .ToString calls generate much garbage?
Every time you call ToString(), you get a new String object instance. Whether that's "much garbage" depends on your definition of "much garbage" and what you do with those instances.
Is there a better way?
You can inspect the contents of StringBuilder directly, but you'll have to write your own methods to do that. You could use state-machine-based techniques to monitor the stream of data.
Whether any of that would be "better" than your current implementation depends on a number of factors, including but not limited to:
Are you seeing a specific performance issue now?
If so, what specific performance goal are you trying to achieve?
What other overhead exists in your code?
The first question above is very important. Your first priority should be code that works. If your code is working now, and does not have a specific performance issue that you know you need to solve, then you can safely ignore the GC issues for now. .NET's GC system is designed to perform well in scenarios just like this one, and usually will. Only in unusual situations would you need to do extra work to solve a performance problem here.
Without a good, minimal, complete code example that clearly illustrates the above and any other relevant issues, it would not be possible to say with any specificity whether there is in fact "a better way". If the above answers don't provide the information you're looking for, consider improving your question so that it is not so broad.
This application tests hardware and prints the results of the test to the console. Multiple devices can be under test at once so I have multiple threads and locking around access to the console, both input and output. So I originally had this in my code right before passing the result to the function that prints:
string message = String.Format("The DUT is: {0}. The total test " +
"was a : {2}.", MAC, testResultString);
The 2 caused the application to stop executing that function. It switched control back to the other threads but never complained about an error etc so the problem took quite a while to track down. What are good strategies/best practices for dealing with String.Format since it is apparently pretty quiet when there is a problem. Or alternatives to string format that have similar flexibility.
Edit: yes the bug was tracked down and the code changed to:
string message = String.Format("The DUT is: {0}. The total test " +
"was a : {1}.", MAC, testResultString);
The point of the question is moreso how to deal with String.Format silently failing. As correctly pointed out by #alexd, this is not a problem specific to String.Format. Any function in a separate thread that throws an exception will have the same issue.
Thanks for the pointers on Re-sharper and the edits #VirtualBlackFox.
As Daniel James Bryars already said, meet ReSharper:
2 Warnings on this line as the second parameter is never used in the format string (And one error due to missing ;).
You can even with an attribute mark your own code or external code like NLog with this feature.
Warning are aggregated on the scrollbar as colored lines, available on a separate window and it can be integrated in nearly any automated system (Sonar for example)
The problem is not specific to String.Format. Pretty much any exception, thrown from a background thread, will lead to the same problem.
You may consider AppDomain.UnhandledException to catch and report such exceptions:
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
{
Exception x = (Exception)e.ExceptionObject;
// report error, etc.
};
But there are quite some details to be aware of, see http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception%28v=vs.110%29.aspx.
P.S. This page provides a good overview of possibilities:
WPF global exception handler
This will throw an error because {2} actually refers to the third parameter after the string. Since you only have two, it throws an exception.
string message = String.Format("The DUT is: {0}. The total test " +
"was a : {1}.", MAC, testResultString);
As long as your token references don't exceed your parameter count, you should not have to worry about error handling on a String.Format.
Why donĀ“t you try something like:
string message = String.Format("The DUT is: {0}. The total test was a : {1}.", MAC, testResultString);
I hope this helps! And SLaks says in the comment! Add a try/catch to check the exceptions ;)
If your using VS then you may need to enable those exceptions under the debug menu. String.Format does throw an exception if the number of arguments is less than any index used. Look at the MSDN page.
Update: more specifically, you need to enable exceptions from the CLR (Common Language Runtime).
In C#6 you can now use "String interpolation" (see here and here) and do something this:
string message = $"The DUT is: {MAC}. The total test " +
"was a : {testResultString}.";
and I don't know if this is compile time checked.
I'm writing a C# program using Visual Studio 2010 where I want to write out certain events to a log file and include the line number the code was on when that happened.
I've only found two ways of capturing line numbers - CallerLineNumber, which requires .Net 4.5/C#5 (I'm targeting .Net 4) and StackFrame.GetFileLineNumber, which apparently requires a debug build and pdb file to work properly, and I'm producing a release build and no pdb file.
But here's what I don't get - both of the above are run-time solutions, but line numbers are compile-time entities. Why is a runtime solution necessary?
I could type in the correct line number as a literal constant by just looking at the bottom of the screen where it says something like "ln 175" . . .
LogEvent("It happened at line 175");
but the problem with that is that if I edit any code before line 175 my literal might no longer be correct. But the compiler knows the correct line number and I've used programming languages in the past that could just pop in the correct line number as a compile time constant. (e.g., ANSI C and Microsoft C++ support a predefined macro called
_LINE_) Is there any way to get C# to do that? If not are there any solutions to my problem?
CAVEATS: This is NOT an answer to the OP. I know that. But people looking for something similar may find this page.
This is not about .NET 4.
This is still ultimately a run-time solution.
But VS 2015, C#, .NET Core or .NET 4.5 allow:
using System.Runtime.CompilerServices;
using System.Diagnostics;
public static String CurrentLocation(
[CallerFilePath] string file = null,
[CallerLineNumber] int lineNumber = 0,
[CallerMemberName] string method = null)
{
String location = file;
if (lineNumber != 0)
{
location += "(" + lineNumber.ToString() + ")";
}
if (!String.IsNullOrWhiteSpace(method))
{
location += ": " + method;
}
if (!String.IsNullOrWhiteSpace(location))
{
location += ": ";
}
return location;
}
[UPDATED USAGE]
With usage something like:
Console.Error.WriteLine(CurrentLocation() + "some message");
or
Debug.WriteLine(CurrentLocation() + "some message");
No, C# doesn't have a macro preprocessor or any meta programming features, so there are no "Compile time" solutions in the entire language. But there are 3rd party macro languages out there that you can use, if you have to, but of course it complicates the build process, Visual Studio won't just figure out how to built it by itself.
You can even use the C preprocessor if you want it. (assuming MSVC compiler)
cl.exe /TC /P /C /EP something.cs > something.raw.cs
cl.exe is the C compiler
/TC tells the C compiler to treat all files as C sources despite their extensions
/P tells the C compiler to only preprocess the file do not compile it
/C preserves the comments
/EP prevents the compiler from generating #line directives, that the C# compiler wouldn't understand
This will allow you to use #include, #define and #if as well as __FILE__ and __LINE__ in your C# program, but again you have to set up Visual Studio to do this additional compilation step, or use a different build system.
One option would be to use the StackTrace class, like so
[Conditional("DEBUG")]
public static void DebugPrintTrace()
{
StackTrace st = new StackTrace(true);
StackFrame sf = st.GetFrame(1);
Console.WriteLine("Trace "
+ sf.GetFileName() + " "
+ sf.GetMethod().Name + ":"
+ sf.GetFileLineNumber() + "\n");
}
In my opinion, line numbers are the wrong approach. Let me explain:
How unique is line number 175? Even if you find a solution, the next question is: which file was it? And it will repeat: if you know the file, you'll ask yourself: which version of the file was it? And you'll integrate the revision number of your source control system. Once you did that, the question will come up: who was calling that code.
If you're debugging an exception, use a debugger and break on first chance exceptions.
If you're analyzing a performance issue, line numbers don't matter, anything unique will do. Try a memory profiler.
If your methods are too long to identify the problems, refactor your code and make the methods shorter. With short methods, you can use an AOP framework like PostSharp with its logging aspect to achieve at least the CallerMemberName feature. It's available down to .NET 2.0 and it's very easy to add logging and remove logging, not like deleting single lines in your code.
I have the following log4net statement in a c# application:
log.Info(CultureInfo.InvariantCulture, m => m(notice));
with the string contents of:
notice = "Checking: 645: Bp $B!!:{4V7r;K Bp $B$D$^$M$5$S (B <xxx#xxxxxx. Co. Jp> (B <xxxxx#xxxxxxx.Com>)"
causing this exception:
[Common.Logging.Factory.AbstractLogger+FormatMessageCallbackFormattedMessage]System.FormatException: Index (zero based) must be greater than or equal to zero and less than the size of the argument list.
at System.Text.StringBuilder.AppendFormat(IFormatProvider provider, String format, Object[] args)
at System.String.Format(IFormatProvider provider, String format, Object[] args)
at Common.Logging.Factory.AbstractLogger.FormatMessageCallbackFormattedMessage.FormatMessage(String format, Object[] args)
If you notice in the string (which, in this case, is a totally piece of garbage) there is a single bracket "{". I'm fairly certain that this is causing the exception. What can I do to avoid this? Escape the string somehow?
It's a fairly harmless exception, except that it shows up in the log file and is distracting.
It ends up that the Common.Logging log function uses the string.Format functions regardless of whether they are needed or not. So, as #HansKesting mentioned in comments, escaping any unintended brackets (braces) will be needed. So, when logging data that I suspect my have this problem, I changed the code to:
notice = notice.Replace("{", "{{");
log.Info(CultureInfo.InvariantCulture, m => m(notice));
Hopes this helps others.
You are using a self writen extention which accepts a Action as log argument. Your method m(string) causes an error. You should check the source code of your method m. Log4net it self will never cause any errors because it is designed to fail silent. If the error is caused by log4net you would have find a critical bug.
The reason log4net does not accept a Action<string> as argument is that it can have side affects like this.
I'm messing around with some windows functions using p/invoke. Occasionally, I get an error code that is not ERROR_SUCCESS (such an odd name).
Is there a way to look these up within the program? Forexample, if I get error 1017. Can I tell the user
The system has attempted to load or
restore a file into the registry, but
the specified file is not in a
registry file format.
(ERROR_NOT_REGISTRY_FILE: 0x3F9)
Instead of
Error Code: 1017
I'm not sure if there's a niifty .NET wrapper, but you could call the FormatMessage API using P/Invoke.
See this answer for how it would normally be called from native code. Though the question refers to grabbing error codes from HRESULTs, the answer also applies for retreiving codes from the regular OS error codes coming from GetLastError/GetLastWin32Error).
EDIT: Thanks Malfist for pointing me to pinvoke.net, which includes alternative, managed API:
using System.ComponentModel;
string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
Console.WriteLine(errorMessage);
You could take the defines from winerror.h at Rensselaer Polytechnic Institute, and put them into an Enum:
public enum Win32ErrorCode : long
{
ERROR_SUCCESS = 0L,
NO_ERROR = 0L,
ERROR_INVALID_FUNCTION = 1L,
ERROR_FILE_NOT_FOUND = 2L,
ERROR_PATH_NOT_FOUND = 3L,
ERROR_TOO_MANY_OPEN_FILES = 4L,
ERROR_ACCESS_DENIED = 5L,
etc.
}
Then if your error code is in a variable error_code you would use :
Enum.GetName(typeof(Win32ErrorCode), error_code);
I landed on this page while in search of a managed alternative to calling FormatMessage through P/Invoke.
As others have said, there is no way to get those capitalized, underscored names, short of looking them up in winerror.h, which I have seen reproduced online in various places where I landed in the course of searching for information about resolving specific status codes. A quick Google search, for winerror.h, itself, uncovered a page, at Rensselaer Polytechnic Instutute, where someone has helpfully extracted the #define statements from it.
Looking at it gave me an idea; I think there may be a way to get there, working from the source code of winerror.h, which I have, as part of the Windows Platform SDK that ships with every recent version of Microsoft Visual Studio.
Right now, I am in the middle of sorting out a pressing issue in the .NET assembly that brought me to this page. Then, I'll see what I can cobble together; this kind of challenge is right up my alley, and somebody threw down a gauntlet.
Yes there's a function that does that but I don't remember what it is. In the mean time, you can use the error lookup tool (Tools->Error Lookup) to see what a particular code means from within Visual Studio.