Tips, Tricks and Code Snippets
Below are a few tips and tricks that I’ve either created or come across that I’ve used or have just found interesting enough to store someplace for possible use later. It’s formatted in a FAQ format.
How do I determine the executing assemblies path?
If your wanting to do this with a Windows Forms application, just use:
1
Dim path As String = System.IO.Path.GetDirectoryName(Application.ExecutablePath)
Otherwise, you can use:
1
Dim path As String = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly.Location)
How do I get a list of installed printers?
Use the System.Drawing.Printing.PrinterSettings.InstalledPrinters
collection.
How do I translate a Win32 error number into a human readable text message?
You have two ways (using VB.NET) to get a numeric Win32 error message. One is to use the System.Runtime.InteropServices.Marshal.GetLastWin32Error
method, while the other is to use the VB.NET specific Err.LastDllError
method. Now that you have the numeric representation of the Win32 error, we want to turn it into a text representation, you know, the error message in English.
There’s couple of ways to accomplish this. First, we’ll explore the Win32 P/Invoke way first. Using this method is the way that the SDK documentation is pointing out. What I’ve done is wrapped the FormatMessage
API call with a VB.NET friendly wrapped version.
1
2
3
4
5
6
7
8
9
10
Private Declare Function FormatMessageA Lib "kernel32" (ByVal flags As Integer, ByRef source As Object, ByVal messageID As Integer, ByVal languageID As Integer, ByVal buffer As String, ByVal size As Integer, ByRef arguments As Integer) As Integer
Public Shared Function FormatMessage(ByVal [error] As Integer) As String
Const FORMAT_MESSAGE_FROM_SYSTEM As Short = &H1000
Const LANG_NEUTRAL As Short = &H0
Dim buffer As String = Space(999)
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, [error], LANG_NEUTRAL, buffer, 999, 0)
buffer = Replace(Replace(buffer, Chr(13), ""), Chr(10), "")
Return buffer.Substring(0, buffer.IndexOf(Chr(0)))
End Function
You can then use this method as follows:
1
Throw New Exception(Marshal.GetLastWin32Error)
A more friendly .NET Framework method is to use the Win32Exception
class. Normally when you want to know what the human readable version of the numeric value is, you will probably throw this so the user of your application can see the error. At the very least, so you can easily figure out what is the problem.
1
2
3
4
Imports System.ComponentModel
Imports System.Runtime.InteropServices
Throw New Win32Exception(Marshal.GetLastWin32Error)
How do I change the color of the text on a tab of the TabControl?
You’ll have to set the TabControls DrawMode
property to OwnerDrawFixed
and draw the text in the TabControls DrawItem
procedure.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Private Sub TabControl1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) _
Handles TabControl1.DrawItem
Dim r As RectangleF = RectangleF.op_Implicit(e.Bounds)
Dim ItemBrush As New SolidBrush(TabControl1.BackColor)
Dim sf As New StringFormat
sf.Alignment = StringAlignment.Center
sf.LineAlignment = StringAlignment.Center
If CBool(e.State And DrawItemState.Selected) Then
e.Graphics.FillRectangle(ItemBrush, e.Bounds)
e.Graphics.DrawString(TabControl1.TabPages(e.Index).Text, e.Font, Brushes.Red, r, sf)
Else
e.Graphics.DrawString(TabControl1.TabPages(e.Index).Text, e.Font, Brushes.Blue, r, sf)
End If
End Sub
How can I determine the date for next Friday?
1
2
3
4
5
6
7
8
9
10
Public Function GetNextFriday(ByVal startDate As Date) As Date
If startDate.DayOfWeek < DayOfWeek.Friday Then
' If todays day of week is less than Friday.
' Add the number of days till next Friday.
Return startDate.AddDays(DayOfWeek.Friday - startDate.DayOfWeek)
Else
' Otherwise, subtract the difference from 7.
Return startDate.AddDays(7 - (startDate.DayOfWeek - DayOfWeek.Friday))
End If
End Function
How can I ‘export’ functions from a .NET DLL to non-.NET applications?
Well, the official answer is to use COM in order to export your classes to .NET applications. But what do you do when these other applications are unable to use COM? Well, you can then possibly use a wrapper DLL written in Managed C++ to accomplish the task. But wait, there appears to be yet one more way… hack the resulting IL code of your DLL to export directly as a native Win32 type ‘export’. Export Managed Code as Unmanaged article discusses all the gory details.
How do I catch errors in ‘release’ builds, but not while debugging?
Here’s the scenario. You want to setup a global error handler (which you should) for your application. This is a good thing. However, it causes a pain when trying to develop within the debugger since now every time an error occurs, your global error handler will capture the error. Here’s a solution:
Often times, applications will have a global exception handler in the Main method that catches any exceptions that weren’t handled elsewhere. The compiler itself uses a strategy similar to this for out of memory situations – once we’ve run out of memory, the compiler’s toast, so we throw an exception indicating “out of memory” and then catch it at the top level, giving a nice “out of memory, please restart the compiler” error. (Hopefully none of you have ever seen it.) Anyway, when you’re debugging the application it can be convenient to not catch exceptions at the top level so that you immediately break into the debugger when you get a global exception. A simple way to do this is to use When to not catch the exception when a debugger is present:
1
2
3
4
5
6
7
Sub Main()
Try
RunApplication()
Catch ex As Exception When Not System.Diagnostics.Debugger.IsAttached
HandleGlobalException(ex)
End Try
End Sub
Source: Paul Vick
How do I determine if an assembly was built for debug or release?
If you want to determine if your assembly was built using debug or release, you would think you could use the FileVersionInfo.IsDebug
or IsRelease
; however, the compiler doesn’t apparently set the proper bit as you would expect. Since that doesn’t work, you could use the DebuggableAttribute
on the assembly by using the following code.
1
2
3
Dim assm As Reflection.Assembly = Reflection.Assembly.LoadFrom(Application.ExecutablePath)
Dim found As Boolean = assm.GetCustomAttributes(GetType(DebuggableAttribute), False).Length > 0
Me.Text = "Assembly is " & IIf(found, "debug", "release")
Source: Thanks to Jeff Key for posting this tip to his blog (you can view his post for the C# code).
How do I build a sockets (network) terminal client?
Haven’t had a chance to go through this video walk through, but I’m sure it’s excellent since it’s done by Carl Franklin. As soon as I get a chance to check it out, I’ll update this information. You can check it out here.
Holy crap!!! My application is taking up too much memory and it’s just a default form? What is going on here?
Well, this is actually normal with an application utilizing the .NET Framework. Many things are happening when you launch your application such as code access security, JIT compilation, memory management and the referencing of the actual .NET Framework library(ies). However, if you are really concerned with the amount of memory your application is reporting you can try the following to give your application and Windows a kick in the rear to more accurately report the current memory usage of your application. This tip is purely for cosmetic purposes, to help calm those irritating phone calls asking why your application uses more memory than Microsoft Word does… and it’s just a Hello World application. There is no performance gain of any kind from using this tip. See warnings below for additional information.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Public Class MemoryManagement
Private Declare Function SetProcessWorkingSetSize Lib "kernel32.dll" ( _
ByVal process As IntPtr, _
ByVal minimumWorkingSetSize As Integer, _
ByVal maximumWorkingSetSize As Integer) As Integer
Public Shared Sub FlushMemory()
GC.Collect()
GC.WaitForPendingFinalizers()
If (Environment.OSVersion.Platform = PlatformID.Win32NT) Then
SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1)
End If
End Sub
End Class
This will force a garbage collection to occur and if running on Windows NT, 2000, XP, or 2003, will (according to the SDK documentation):
The working set of a process is the set of memory pages currently visible to the process in physical RAM memory. These pages are resident and available for an application to use without triggering a page fault. The minimum and maximum working set sizes affect the virtual memory paging behavior of a process.
The working set of the specified process can be emptied by specifying the value -1 for both the minimum and maximum working set sizes.
Using a simple windows application, adding a command button, within the click event call upon the FlushMemory
method, you will see the application go from using nearly 15meg of memory usage and a VM size of over 7meg to around 1meg (or less) of memory while the VM size stays pretty constant.
Warning: This nice little method doesn’t come without a cost. Don’t call upon this method during critical sections of your code. Forcing a garbage collection can be very time consuming. Also, don’t try to set the minimum and maximum sizes; let .NET’s memory management do it’s job.
A couple of more references (or more accurately oppinions) for you to read if you intend on using GC.Collect: Chris Brumme - ReleaseComObject and Rico Mariani - Two things to avoid for better memory usage.
How do I load an image from a file using a file stream?
1
2
3
4
5
6
7
8
Protected Shared Function GetImageFromFile(ByVal FileName As String) As Byte()
Dim myFile As String = FileName
Dim fs As FileStream = New FileStream(myFile, FileMode.Open, FileAccess.Read)
Dim br As BinaryReader = New BinaryReader(fs)
Dim bytesize As Long = fs.Length
ReDim GetImageFromFile(bytesize)
GetImageFromFile = br.ReadBytes(bytesize)
End Function
To use the above function:
1
2
Dim img As New Bitmap(New IO.MemoryStream(GetImageFromURL("c:\file.jpg")))
Me.BackgroundImage = img
Source: Duncan MacKenzie
How do I load an image from a URI address?
1
2
3
4
5
6
7
8
9
10
Function GetImageFromURL(ByVal url As String) As Byte()
Dim wr As HttpWebRequest = _
DirectCast(WebRequest.Create(url), HttpWebRequest)
Dim wresponse As HttpWebResponse = _
DirectCast(wr.GetResponse, HttpWebResponse)
Dim responseStream As Stream = wresponse.GetResponseStream
Dim br As BinaryReader = New BinaryReader(responseStream)
Dim bytesize As Long = wresponse.ContentLength
Return br.ReadBytes(bytesize)
End Function
To use the above function:
1
2
Dim img As New Bitmap(New IO.MemoryStream(GetImageFromURL("http://msdn.microsoft.com/longhorn/art/codenameLonghorn.JPG")))
Me.BackgroundImage = img
Source: Duncan MacKenzie
How do I use animated cursors in .NET?
Check out the Using Colored and Animated Cursors article by Dr. GUI.
How do I create a Windows Service?
MSDN has an excellent article (Walkthrough: Creating a Windows Application in the Component Designer) that shows how to create a Windows Service from start to finish. I would also recommend reading the Install a Windows Service the way you want to article available on CodeProject.com that shows how to do a few of the features that seem to have been left out; such as adding a service description and modifying the All service to interact with the desktop option.
How do I generate a GUID (Globally Unique IDentifier)?
You can use the System.Guid
class.
1
Dim guid As String = System.Guid.NewGuid.ToString
You can also control how the GUID is formatted. For example, if you use ToString("N")
, the dashes (“-“) will be removed.
Have any other ideas? Leave a comment.