How does Visual Basic store Strings in memory? - vb.net

I ran into a situation where I needed to find the memory address of a String that I use in a piece of VB Code. I tried using various debuggers and also using the simple procexplorer to display a list of printable strings in memory. But it does not show anything which puzzles me. Does VB (.net framework 4) use some kind of encoding mechanism to store strings so that it does not appear in a printable format ?
Here's the variable am trying to locate in memory: "spoilt"
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
MessageBox.Show("Button1")
Dim spoilt As String
spoilt = TextBox1.Text
Label1.Text = spoilt
End Sub

These are managed strings. I'm not sure if the tools you are using know how to read managed strings out of memory.
.NET string literals are stored in the metadata section of the portable executable. Unless the tool understand how to read it out of .NET's metadata section, it won't find it. You can see the strings with a tool like ildasm. Under "View", and "MetaInfo", click "Show!". Somewhere in the new window will be a "User Strings" section. Here's mine for a sample application.
User Strings
-------------------------------------------------------
70000001 : (35) L"Property can only be set to Nothing"
70000049 : (28) L"WinForms_RecursiveFormCreate"
70000083 : (26) L"WinForms_SeeInnerException"
700000b9 : ( 7) L"Button1"
700000c9 : ( 6) L"Label1"
700000d7 : ( 8) L"TextBox1"
700000e9 : ( 5) L"Form1"
700000f5 : (29) L"WindowsApplication1.Resources"
Here we can see that the MetaData Token of the string (in my case) is 700000b9.
Now if you wanted to find the address of the string at runtime...
The tool of choice I would use to do this is WinDbg with the SOS extensions. Here's how to find that string in memory.
This is all for the x86 .NET Framework 4.
Start by opening WinDbg and selecting Open Process and open your EXE. It will immediately break to give you the opportunity to set things up, like breakpoints.
Set a stop on exception when the CLR JIT is loaded. This will be the right time to load SOS debugger extension.
sxe ld:clrjit
Then go ahead and continue execution with g.
At this point we should have hit a breakpoint on a modload for clrjit.
ModLoad: 57910000 57970000 C:\Windows\Microsoft.NET\Framework\v4.0.30319\clrjit.dll
From there we can load the SOS debugging extensions with .loadby sos clr.
We can test that SOS loaded correctly by running !eeversion to get the Execution Enging version. For me this gives "4.0.30319.269 retail".
Now to find that string. Let's start by breaking when the System.Windows.Forms.dll module is loaded.
sxe ld:System.Windows.Forms
And use g to continue the execution. We should break when the module is loaded. Go ahead and step over it so the module is actually loaded with p.
Now we can put a breaking on MessageBox.Show like so:
!bpmd System.Windows.Forms.dll System.Windows.Forms.MessageBox.Show
Now go ahead and go with g. Your application should be running now. Go ahead and click the button, and our breakpoint should be hit.
Then we can step into Show with t.
From there we can use !clrstack -p to show the stack trace with parameters. At the top of the stack will be the call to MessageBox.Show.
004ee820 5c22839c System.Windows.Forms.MessageBox.Show(System.String)
PARAMETERS:
text (<CLR reg>) = 0x022a1058
So now we know that the string's address is 0x022a1058. This of course will be different for you. If we do a !do 0x022a1058 it gives us the string:
Name: System.String
MethodTable: 638afb08
EEClass: 635e8bb0
Size: 28(0x1c) bytes
File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String: Button1

To answer my own question regarding encoding: Yes, Unicode encoding is used to store all strings. The above answer by #vcsjones provides a detailed explanation of how to find the memory location of these strings.
For the benefit of people who are looking for answers on how to find this address, there is a simpler way. mona.py (http://redmine.corelan.be/projects/mona) is a plugin for immunity debugger to do this job. I used it to find the memory location. The only reason I wasn't able to find what I was looking for is because of the unicode format. But, with mona.py there is an option to search for unicode strings as well.

Related

Preventing VB.NET's multiline/verbatim string from destroying your entire code file?

Let's say you have a module that's several hundreds of lines long. At the very top of your code file, you go to start up a string, so you type a quote. Total wreckage ensues as the string remains unterminated for a time, causing everything within your entire code file to be subject to erratic encapsulation by your string (see image for actual example of all the errors generated). No big deal, right? You just finish your string and all the errors will go away. While true, you may find the IDE has had its way with other strings in your document. For example, these lines...
oLog.writeLogFile("Starting System Update and Version Update ")
oLog.writeLogFile("Starting Script for Fetching Data from Source to Dest")
...get changed to this:
oLog.writeLogFile("Starting System Update And Version Update ")
oLog.writeLogFile("Starting Script For Fetching Data from Source To Dest")
Notice how and changes to And, for to For, and to to To. What's happening here is that, as other strings in the document become... eh... "destrung"... so some of the words that were once part of a string are now interpreted as keywords by the IDE. Because it's VB, it modifies capitalization automatically. When you finally terminate your string, all the other strings further down in the document become properly terminated as well, but the jarring effects still remain.
Is there a way to prevent this from occurring?
Why not first type a double ", then return in between them and start typing your string? I do it all the time to prevent this. I find that the short delay in between typing your first " and the moment the IDE starts capitalizing keywords is long enough for me to (remember to) type the second ".

VB.Net: "Nice", maintainable way of matching integer error code to error strings for UI

I'm building a user interface (HMI, human-machine interface) for a machine that does various motion-controlled tasks. The motion controller is a bit on the primitive side in terms of programming, and so I have it sending me error and status codes in the form of integers.
As an example: I have a box that indicates what stage the machine is at during its autocycle. The machine sends a '1', and I want the box to say 'Waiting to start autocycle.' Here are a few more:
1 - Waiting to start autocycle.
2 - Returning to home.
3 - Waiting at home.
4 - Tracking encoder A.
5 - Tracking encoder B.
And so on. Is there a clean way to maintain these messages in VB.net using, say, resources, that I don't know about, or should I just make an XML file that just contains something like
<statusmessage code="1" message="Waiting to start autocycle.">
and read that in when the program starts?
My current method is a hard-coded select statement with the strings in the actual VB source so you have to recompile the program if you want to change a message (gross).
If it's relevant, this program is never going to be multi-language.
Thanks for any advice!
It's easy to do this with an .xml file. That or some similar file format would be my preference. Some people would prefer using app.config or file format. When I evaluate something like this, simplicity of maintenance is probably the highest priority, and there are several methods that would work equally well in this regard. A database table could be used, but it seems like an overcomplication.
If you don't need to worry about multiple languages, it is possible to do this...
Public Enum Foo
<Description("Waiting to start autocycle")> WaitingToStartAutoCycle = 1
<Description("Returning to home")> ReturningToHome = 2
' [ etc...]
End Enum
You can then use reflection to get the description. This is ripped out of a larger piece of code, so forgive me if I miss part of it..
Public Function GetEnumDescription(ByVal value As Object) As String
Dim type As Type = value.GetType()
' Excersize for the reader, validate that type is actually an Enum
Dim f As FieldInfo = type.GetField(value.ToString)
If f IsNot Nothing Then
Dim ca() As Object = f.GetCustomAttributes(GetType(DescriptionAttribute), False)
If ca IsNot Nothing AndAlso ca.Length > 0 Then
Return CType(ca(0), DescriptionAttribute).Description
End If
End If
Return value.ToString ' Last resort if no Description attribute
End Function

Ascw returns "Cannot convert to 'Integer'." in Watch or Immediate

I have following sample code
Dim q As Char = "a"
Dim res As String = CStr(AscW(q))
res contains correctly "97" but when I use AscW(q) in watch or immediate it returns message: Cannot convert to 'Integer'.
Is it a bug or Visual Studio or am I doing something not correctly?
How can I see a character code in Immediate.
Note
The code presented is just an example. I found the problem when trying to see Character code in the Watch Window.
For a workaround, how about the command
? System.Text.Encoding.Unicode.GetBytes(q)
I personally believe that any acceptable VB.Net code should be acceptable in the Immediate window and really don't understand why AscW is causing errors when VB.Net offers no equivalent (e.g. in C#, but not VB.Net, you can cast a Char variable to an Integer to get the character code).
You are doing everything right (and the outputs will be OK in any case), although you are using old VB code. If you need functionalities like AscW (, Asc, ChrW, etc.), you would have to rely on this "old code" to get what you want (directly or via Microsoft.VisualBasic.Strings which, btw, does not show a different behaviour). But, in any other case, you should avoid the utilisation of this old code.
Test these two lines in the Immediate Window:
Dim res As String = CStr(5)
res = 5.ToString()
As you can see, you get an "error" (VS 2010, right-click on the line and select "QuickWatch") in the first line (old version), but not in the second one (.NET version).
Thus, the behaviour you observed can be considered as an inoffensive bug (no real effects in the execution) more or less understandable if you analyse the situation (you are asking a certain language (VB.NET) to support all its own features and the ones from an old language (VB); with the old one, some secondary functionalities might not be perfect).

Equivalent of "Dim As String * 1" VB6 to VB.NET

I have some VB6 code that needs to be migrated to VB.NET, and I wanted to inquire about this line of code, and see if there is a way to implement it in .NET
Dim strChar1 As String * 1
Intellisense keeps telling me that an end of statement is expected.
That's known as a "fixed-length" string. There isn't an exact equivalent in VB.NET.
Edit: Well, OK, there's VBFixedStringAttribute, but I'm pretty sure that exists solely so that automated migration tools can more easily convert VB6 code to VB.NET for you, and it's not really the ".NET way" to do things. Also see the warnings in the article for details on why this still isn't exactly the same thing as a fixed-length string in VB6.
Generally, fixed-length strings are only used in VB6 if you are reading fixed-size records from a file or over the network (i.e. parsing headers in a protocol frame).
For example, you might have a file that contains a set of fixed-length records that all have the format (integer, 1-character-string, double), which you could represent in VB6 as a user-defined type:
Public Type Record
anInteger As Integer
aSingleCharacter As String * 1
aDouble As Double
End Type
This way, VB6 code that reads from the file containing records in this format can read each fixed-sized record stored in the file, and in particular, it will only read 1 byte for aSingleCharacter. Without the * 1, VB6 would have no idea how many characters to read from the file, since a String can normally have any number of characters.
In VB.NET, you can do one of the following, depending on your needs:
If the length matters (i.e. you need to read exactly one byte from some data source, for example) consider using an array instead, such as
Dim aSingleByteArray(1) As Byte
Alternatively, you could use one of the Stream classes. In particular, if you are reading data from a network socket or a file, consider using NetworkStream or FileStream, respectively. A Stream is meant for byte-for-byte access (i.e. raw binary access). StreamReader is a related class that simplifies reading data when it is text-based, so that might be good if you are reading a text file, for example. Otherwise (if dealing with binary data), stick with one of the Stream classes.
If the length doesn't matter, you could just use a "normal" String. That is to say:
Dim aNormalString As String
Which answer is "correct" really depends on why it was declared that way in the original VB6 code.
The fixed length strings has been deprecated in VB.NET because there are several better options.
Since your fixed length string is just one character long, you can use the Char type in this case, as Mark suggested.
Dim strChar1 As Char
Seeing as you're doing a VB6 migration, I'd definitely consider VBFixedStringAttribute as well as the other options listed by Mike Spross, but, in this case, because it is a single character, a Char may be an option in this case too.
As mentioned elsewhere VBFixedString is only acknowledged by the Get and Put VB I/O API. So the best solution (other than rewriting your code that references the "fixed length string") is to write your own equivalent of Microsoft.VisualBasic.Compatibility.VB6.FixedLengthString. See this answer for more details.
VBFixedStringAttribute Class
<VBFixedString(1)> Dim strChar1 As String
ALthough this question was asked ages ago, VB.NET actually has a native fixed-length string -- <VbFixedArray(9)> Public fxdString As Char() 'declare 10-char fixed array. Doing this with scalars actually creates VB6-style Static Arrays.

vb.net character set

According to MSDN vb.net uses this extended character set. In my experience it actually uses this:
What am I missing? Why does it say it uses the one and uses the other?
Am I doing something wrong?
Is there some sort of conversion tool to the original character set?
This behaviour is defined in the documentation of the Chr command:
The returned value depends on the code page for the current thread, which is contained in the ANSICodePage property of the TextInfo class in the System.Globalization namespace. You can obtain ANSICodePage by specifying System.Globalization.CultureInfo.CurrentCulture.TextInfo.ANSICodePage.
So, the output of Chr for values greater than 127 is system-dependent. If you want reproducible results, create the desired instance of Encoding by calling Encoding.GetEncoding(String), then use Encoding.GetChars(Byte()) to convert your numeric values into characters.
If you go up one level in the chart linked in your question, you will see that they do not claim that this chart is always the output of the Chr command:
The characters that appear in Windows above 127 depend on the selected typeface.
The charts in this section show the default character set for a console application.
Your application is a WinForm application, not a console application. Even in the console, the character set used can be changed (for example, by using the chcp command), hence the word "default".
For detailed information about the encodings used in .net, I recommend the following MSDN article: Character Encoding in the .NET Framework.
The first character set is Code Page 437 (CP437), the second looks like Code Page 1252 (CP1252) also known as Windows Latin-1.
I'd guess VB.Net is simply picking up the default encoding for the PC.
How did you write all this? Because usually, when you use a output stream function, you can specify the encoding going with it.
Edit: I know this is not C#, but you can see the idea...
You'd have to set the encoding of your filestream, by doing something like this:
Setting the encoding when creating the filestream