3

I am doing work for a client who has lost the source code for one of their VB.Net WinForms applications. The assembly they have is not obfuscated at all. I am trying to recover as much of the source as I can as C# source and have tried several tools for decompiling assemblies, including Reflector, ILSpy and JustDecompile (all the latest versions), but they all produce code with a huge number of errors in them. Because of the large number of errors in the generated code, I am going to ask about the specific errors (in different questions), hopefully to get more directed answers and in this way try shed some light on why all the tools are having difficulty decompiling this assembly.

This question pertains to the fact that the code generated by all these tools always have a large number of invalid member variables (fields) such as the following:

private short $STATIC$Report_Print$20211C1280B1$nHeight;
private ArrayList $STATIC$Report_Print$20211C1280B1$oColumnLefts;
private StaticLocalInitFlag $STATIC$Report_Print$20211C1280B1$oColumnLefts$Init;

Can someone explain why the generated code has these invalid member variables and how I can resolve these?

BruceHill
  • 6,954
  • 8
  • 62
  • 114
  • 1
    I have no idea, why you get these names, but you could try one more decompiler: http://www.jetbrains.com/decompiler/ – Daniel Hilgarth Sep 05 '11 at 16:03
  • 1
    Yeah, this'll happen in numerous circumstances (such as disassembling of some generics code and so on), you might want to see [this answer](http://stackoverflow.com/questions/3159026/strange-decompiled-code-from-event-subscribe-code-in-a-generic-class/3159053#3159053) for reference. – Grant Thomas Sep 05 '11 at 16:05
  • @Mr. Disappointment: That is a great reference. I do see code similar to the generated code shown in this question and will refer to that question. Thanks. – BruceHill Sep 05 '11 at 16:16

3 Answers3

10

Those are identifiers generated by the VB.NET compiler to implement the Static keyword. For example:

Class Example
    Public Sub test()
        Static lookhere As Integer = 42
    End Sub
End Class

generates this IL:

.field private specialname int32 $STATIC$test$2001$lookhere
.field private specialname class [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.StaticLocalInitFlag $STATIC$test$2001$lookhere$Init

By using reserved letters in the field name, the compiler can be sure there will never be an accidental collision with another field. There's no direct equivalent to Static in the C# language. You can leave them as private fields in the class but you have to watch out for initialization. The purpose of the $Init flag and rather a lot of IL that ensures the variable is correctly initialized. You'll need to rename them by hand.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Thanks, Hans. This explanation makes sense and explains why there are so many of these $STATIC member variables in the decompiled code, as the assembly was originally written in VB.Net. I am not very familiar with VB as I am actually a C# developer, but I will read up on _static_ keyword in VB. I notice also on the class StaticLocalInitFlag Visual Studio 2010 gives a message that it is used internally by the Visual Basic compiler and should not be directly called from code; so must I simply delete all these _Init_ member variables? – BruceHill Sep 05 '11 at 19:19
  • 3
    No, you should rename them. As I said, you have to worry about initialization of those variables. You should also have a raft of code in the method that uses them with try/catch/finally. It is likely that you can move initialization to the constructor but you'll have to understand the code before you do this. Side-effects can cause bugs. Decompiling to VB.NET will of course be a lot less painful. – Hans Passant Sep 05 '11 at 19:26
  • 1
    +1 Not least for "decompiling to VB.Net will of course be a lot less painful" – MarkJ Sep 06 '11 at 08:05
9

In short, what's valid in IL isn't necessarily the same as what's valid in the source language. It's fairly common to give compiler-generated (aka synthetic in some circles) members name which are invalid in the language, as it avoids any possible clashes. These are sometimes called unspeakable names as they can't be "spoken" in the source language. For example, the C# compiler usually includes <> in such names.

As for resolving the issue - some decompilers will work out where such names have come from automatically, but you can usually simply change the name everywhere. You won't end up with the original source code, but if you look at what you do end up with, you may be able to then work out more easily what the original source did look like.

Note that the compiler may generate more than just invalid names: in C#, for example, iterators blocks generate IL which in some cases can't be expressed directly in "normal" C# itself. This may not be a problem for you, but it's worth being aware of.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
2

Those aren't variables, they are fields (they have access modifiers).

They will be compiler generated fields which will be generated in a number of different circumstances. The names are purposely invalid to avoid conflicts with "normal" fields.

If you can provide a little more context someone clever can probably figure out what the source originally looked like for the compiler to emit those fields.

Justin
  • 84,773
  • 49
  • 224
  • 367
  • I disagree with your first sentence - I usually understand the term "variable" to include instance variables, static variables and local variables. – Jon Skeet Sep 05 '11 at 16:11
  • @Jon Fair enough, but I normally avoid referring to those as "variables" to avoid confusion with local variables declared in methods. – Justin Sep 05 '11 at 16:14
  • I usually find that referring to "local variables" when I mean local variables is good enough. From the C# spec section 1.3: "There are several kinds of variables in C#, including fields, array elements, local variables, and parameters." – Jon Skeet Sep 05 '11 at 16:18
  • I have changed my wording to say "member variables", the term that I normally use for "fields". But, I am aware that they are not local variables. – BruceHill Sep 05 '11 at 16:20
  • @BruceHill Sorry I didn't meant to nitpick, I was just checking. The distinction is that if they were locals the decompiler might have added them for readability wheras the decompiler won't add fields. If the declaration hadn't included an access modifier it wouldn't have been able to tell which you were talking about before your latest edit. – Justin Sep 05 '11 at 16:23
  • @Justin Not a problem, I did not take the comment negatively, and I appreciate your contribution to the question. – BruceHill Sep 05 '11 at 18:18