How to get the exact location of an exception in C#, as can be done in VB.net?

MontanaMan

Member
Joined
Feb 4, 2022
Messages
8
Programming Experience
5-10
The test system:
-Windows 10
-VS2022
-C#
-.Net Framework 4.8
-WinForms

What I'm Trying to Accomplish:
We are trying to build an error reporting system in C# that will give the exact line number that throws an exception. It needs to work with the compiled release .exe file. It is easy to do this in VB.net, but I can't find a way to accomplish this in C#.

Here is how to get an exact location in VB.net:

Visual Basic:
10:     On Error Resume Next
20:     Err.Raise(60000)
' Returns 20.
30:     MsgBox(Erl())

The above code is from the Microsoft documentation here, showing usage of the ErrObject.Erl property, which was carried over from VB6. I used that system to build an extensive error reporting capability back when I was a VB6 programmer. It was an extremely useful tool that allowed any end-user in the world to report detailed error information back to the Mother Ship (the developers). This allowed us to rapidly home in on the problem and do the necessary re-factoring.

Of course it is better to eliminate errors to begin with, but when shareware is being downloaded by a million users all over the world, on many different versions of Windows, with varying locality settings, there are going to be errors that don't pop up in beta testing.

Unfortunately, I'm not able to find any way in C# to add number tags at the beginning of code lines, like the 10:, 20:, 30: above. Those are not Visual Studio line numbers ... they are typed in by the programmer to label each line in the code as described in the Microsoft documentation here. I've also not found any way to get the Microsoft ErrObject working in C#, like it does in VB.net.

Here is what I've tried in C#:

Here is my test code:

C#:
   private void button2_Click(object sender, EventArgs e)
    {
        try
        {
            int x = 10;      // This is the line reported by ex.ToString()
            int y = 0;
            int z = 0;
            z = x / y;       // This is the line throwing the exception (divide by zero)
        }
        catch (System.Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }

The Problem:
The ex.ToString() always returns the first line of the try block (in this example, line 5), instead of returning the actual line that triggered the error (line 8). This is ALSO TRUE OF the other solutions, involving StackTrace, etc.

My Question:
Is there a way in C# to get the exact code line that throws the exception, like we can do in VB.net? If so, how?

Thanks!!
Any help you could provide would be sincerely appreciated.
 
Last edited:

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,251
Location
Chesapeake, VA
Programming Experience
10+
I'll have to try with newer versions, but in the past, the StackTrace information was always accurate down to the line number when I had an exception as long as I was building in debug mode because optimizations are turned off. When building in release mode, some line numbers would look inaccurate because the compiler applied optimizations and may have compacted some code. Even though the numbers would have been off by a few lines, it was still always good enough to be able to see what the problem was.

Anyway, being off by a couple of lines should never be a hindrance if you follow the single responsibility principle. Often by following this, you will end up with methods that are never more than 20-25 lines. Also, the SRP and short code makes it quite easy to identify the cause of a problem.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,251
Location
Chesapeake, VA
Programming Experience
10+
Yup, it's simply the optimizer kicking in.

Here's a debug build that shows the correct line number:
Screenshot_1.png


And then the same code in a release build with the "wrong" line number:
Screenshot_2.png


But then if you put an extra line of code which forces some code to run before where the exception would be, the correct line is shown again even in a release build:
Screenshot_3.png
 

MontanaMan

Member
Joined
Feb 4, 2022
Messages
8
Programming Experience
5-10
Ok ... we have the solution. You can run the compiler optimized, then run the Dotfuscator, and still get the exact error line number reported by the System.Exception Class. And ... there are no PDB files to help hackers crack your software.

Just set Debugging information to "Embedded."

Instructions for .Net Framework 4.8 instructions (Current as of VS2022):
(Step-by-Step to help new people)
  • Right-Click on your project and select "Properties"
  • In the left panel of the Properties window, select "Build"
  • Make sure that "Optimize Code" is selected (or not ... it's your choice)
  • Click the "Advanced" button, at the bottom-right of the screen
  • In the Advanced window, about 2/3 down, you'll see "Debugging information:" with a drop-down box.
  • Select "Embedded" and click "OK". The Advanced window disappears.
Instructions for .Net 6.0 (Current as of VS2022):
  • Right-Click on your project and select "Properties"
  • In the left panel of the Properties window, select "Build > General"
  • Under "Optimize Code", select "Release" (or not ... it's your choice)
  • Directly under "Optimize Code, you'll see "Debug Symbols" with a drop-down box.
  • Select "Embedded in DLL/EXE".
  • Close the Project Properties window.
Now ... rebuild (REbuild!) the release version with compiler optimized and debug set to embedded. Then run the dotfuscator, and your optimized, dotfuscated, Release .exe will still get the accurate line number from the following code:
C#:
   catch (System.Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }

Obviously, this example is bare-bones, but you can build a robust error-reporting system that includes this and other features built into the System.Exception Class (Microsoft documentation here). We intend to build a website that will receive the information from the desktop app, and store it into a DB as part of a basic service ticket system.

Performance Tests:

I set up a small app that runs a loop of simple math operations 200,000,000 repetitions, timing the results (average of 3 runs). Here are the results:

Compiler Optimized - Debugging information set to "None" - Dotfuscated:
Time: 4116.3 ms (no line number reported)

Compiler Optimized - Debugging information set to "Embedded" - Dotfuscated:
Time: 4115.9 ms (exact line number reported!)

Hope this helps anybody who is interested in creating an error-reporting system for their software.

Best of luck to all in their coding
 
Top Bottom