Using CopyMemory in .NET






4.76/5 (17 votes)
Mar 9, 2003
4 min read

153151

1756
Demonstrates the use of CopyMemory in .NET to initialize classes
Introduction
This tutorial will show you how to use the WIndows API function CopyMemory
to populate Class data using a string as input.
In my line of work, I have to deal with several file formats that contain fixed length records. This is probably the fastest, most straight-forward way to read those files because the data is a 'known size'. CopyMemory
is ideal for this because parsing data is virtually eliminated.
I'm going to walk you through the entire process and explain as I go, starting with a new ConsoleApplication
. You should follow the article using .NET on your own machine. I try to avoid straying from the topic at hand, so when you get to the bolded sections of code, just copy and paste them into your program.
Background
CopyMemory
is a function that will copy the contents of one block of memory to a different block of memory without regard to the data type that is stored there. This results in an ultra fast copy of data, especially for objects like Structures, Classes and Strings.
Initializing Classes using CopyMemory
Create the Project
- Open up Visual Studio.NET and click the 'New Project' button.
- Click Visual Basic Projects, from the Project Types list.
- Click Console Application, from the Templates list.
- Type
CopyMemorySample
in the Name box. - Click the OK button.
- The project template will open and you will see the module 'Module1' in the Code Viewer. It is normally good to give descriptive names to all code components, but for this tutorial we will leave Module1 as is.
Module Module1
Sub Main()
End Sub
End Module
Add Windows API declarations
NOTE: We put the declarations into a class because it makes our code much easier to understand. It is not necessarily required, but you will see that it works nicely.
- In the Solution Explorer, right click on the CopyMemorySample Project. (Just under the Solution in bold.)
- Select the 'Add' menu.
- Select 'Add Class' from the Add menu.
- Type
WINAPI
in the Name box. - Click the Open button.
- The class
WINAPI
will be added and you will see the class in the Code Viewer as follows:
Public Class WINAPI
End Class
- Copy or type in the following code so your WINAPI Class looks like this:
Public Class WINAPI
' Declare the Windows API function CopyMemory
' Note that all variables are ByVal. pDst is passed ByVal because we want
' CopyMemory to go to that location and modify the data that is pointed to
' by the IntPtr, and not the pointer itself.
Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal pDst As IntPtr, _
ByVal pSrc As String, _
ByVal ByteLen As Long)
End Class
Add the CClassTest Class
- In the Solution Explorer, right click on the
CopyMemorySample
Project. (Just under the Solution in bold.) - Select the 'Add' menu.
- Select 'Add Class' from the Add menu.
- Type
CClassTest
in the Name box. - Click the Open button.
- The class
CClassTest
will be added and you will see the class in the Code Viewer as follows:
Public Class CClassTest
End Class
- Now, type or copy in the following Class declaration:
Imports System.Runtime.InteropServices
<StructLayout(LayoutKind.Sequential)> _
Public Class CClassTest
' StructLayout(LayoutKind.Sequential) dictates that the below values will be
' stored sequentially in memory, in other words; ID first, Name second and
' Address third in one sequential block of memory.
' MarshalAs tells the Marshaler what the intended type (and size in this case)
' of data we are trying to work with.
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=10)> Public ID As Char()
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=20)> Public Name As Char()
' Private storage for property values, still populated.
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=30)> Private m_chAddress As Char()
' no problem with standard properties either because the private members get
' the data.
Public Property Address() As Char()
Get
Return m_chAddress
End Get
Set(ByVal Value As Char())
m_chAddress = Value
End Set
End Property
End Class 'CClassTest
- Switch the Code View back to Module1 by clicking the Module1 tab at the top of the Code View window.
- Add the function definition below to Module1:
' copies a string into a class's properties using CopyMemory
Private Function CopyStringToClass(ByVal Source As String, ByVal Target As Object) As Object
' If the Target is nothing, immediate return of nothing.
If Target Is Nothing Then Return Nothing
' get an instance of the WINAPI class that
' holds the API functions.
Dim API As WINAPI = New WINAPI()
' Declare an IntPtr which will hold a memory address, don't
' panic here! Just keep going!
Dim p_objTarget As IntPtr
Try
' Call AllocHGlobal to allocate enough memory on the heap
' for a 'Target' object. AllocHGlobal returns a pointer
' to this memory, which is needed for the next call.
p_objTarget = Marshal.AllocHGlobal(Marshal.SizeOf(Target))
' To copy Target to the heap, use StructureToPtr
' as in the following line of code. This is useful
' for API calls that require prefilled structures.
'Marshal.StructureToPtr(Target, p_objTarget, True)
' Use CopyMemory to take the data from the source string
' (Source) and copy it to the block of memory on the
' heap (which, coincidentally is the same size as the
' source string, we defined sizes in our class definition).
API.CopyMemory(p_objTarget, Source, Marshal.SizeOf(Target))
' Now, tell the Marshaler to copy the data on the heap
' (the results of CopyMemory) into our instance of the
' Target object (clsTest).
Marshal.PtrToStructure(p_objTarget, Target)
' Free the memory that was allocated on the heap, otherwise
' you will create a memory leak.
Marshal.FreeHGlobal(p_objTarget)
Catch ex As System.OutOfMemoryException
' An exception could occur if the system is out of
' memory and the block of heap memory could not be
' set aside for you.
CoughUpCookies(ex)
Catch e As Exception
' General exception caught, show the message and move on...
CoughUpCookies(e)
End Try
' Free resources assigned to our instance of the WINAPI class
API = Nothing
' send the results back for printing
Return Target
End Function 'CopyStringToClass
What the Code Does
- The Function
CopyStringToClass
takes a String and an Object instance as parameters. - Declare a variable of type '
WINAPI
' and initialize it using the New keyword. - Declare a variable of type '
IntPtr
' to hold a memory address. - Using a
Try..Catch
block, trap any exceptions (specificallyOutOfMemory
exceptions). - Using the
System.Runtime.InteropServices.Marshal
class, allocate a 'Target' sized block of memory on the Global Heap and assign the returned pointer to that memory to p_objTarget (which is an IntPtr object). - Use the member function
CopyMemory
of theWINAPI
object to copy the string into the memory that we set aside forclsTest
(the 'Target' sized block of heap memory). - Use the
System.Runtime.InteropServices.Marshal.PtrToStructure
method to copy the data from the heap memory that is pointed to byp_objTarget
into the Target object. - Call the
System.Runtime.InteropServices.Marshal.FreeHGlobal
method to free the block of memory that we allocated on the heap for the copy. If this is not done, it will result in a memory leak. - Set the API object (instance of
WINAPI
) to Nothing (null). - Return the populated class to the caller.
Finishing Up:
Now add the following code listed between the 'ADD THIS CODE' comments to Module1. Module1 is complete (for now):
Imports System.Runtime.InteropServices
Module Module1
Sub Main()
'----------- ADD THIS CODE -----------
' 60 char string for sample data
Dim strSource As String = "10785236ABJohn F. Kennedy Jr. 1234 Pennsylvania Blvd. "
' parameter for function, also receives return value
Dim clsTemp As CClassTest = New CClassTest()
' String to Class Copy
clsTemp = CopyStringToClass(strSource, clsTemp)
PrintResultsOfCopy(strSource, clsTemp)
'----------- END OF CODE -----------
End Sub 'Main
' copies a string into a class's properties using CopyMemory
Private Function CopyStringToClass(ByVal Source As String, ByVal Target As Object) As Object
' If the Target is nothing, immediate return of nothing.
If Target Is Nothing Then Return Nothing
' get an instance of the WINAPI class that
' holds the API functions.
Dim API As WINAPI = New WINAPI()
' Declare an IntPtr which will hold a memory address, don't
' panic here! Just keep going!
Dim p_objTarget As IntPtr
Try
' Call AllocHGlobal to allocate enough memory on the heap
' for a 'Target' object. AllocHGlobal returns a pointer
' to this memory, which is needed for the next call.
p_objTarget = Marshal.AllocHGlobal(Marshal.SizeOf(Target))
' To copy Target to the heap, use StructureToPtr
' as in the following line of code. This is useful
' for API calls that require prefilled structures.
'Marshal.StructureToPtr(Target, p_objTarget, True)
' Use CopyMemory to take the data from the source string
' (Source) and copy it to the block of memory on the
' heap (which, coincidentally is the same size as the
' source string, we defined sizes in our class definition).
API.CopyMemory(p_objTarget, Source, Marshal.SizeOf(Target))
' Now, tell the Marshaler to copy the data on the heap
' (the results of CopyMemory) into our instance of the
' Target object (clsTest).
Marshal.PtrToStructure(p_objTarget, Target)
' Free the memory that was allocated on the heap, otherwise
' you will create a memory leak.
Marshal.FreeHGlobal(p_objTarget)
Catch ex As System.OutOfMemoryException
' An exception could occur if the system is out of
' memory and the block of heap memory could not be
' set aside for you.
CoughUpCookies(ex)
Catch e As Exception
' General exception caught, show the message and move on...
CoughUpCookies(e)
End Try
' Free resources assigned to our instance of the WINAPI class
API = Nothing
' send the results back for printing
Return Target
End Function 'CopyStringToClass
'------------ ADD THIS CODE ---------------
' ask the user to press <Enter> and return
Private Sub PromptForEnter()
Console.WriteLine()
Console.Write("Press <Enter> to continue...")
Console.ReadLine()
End Sub 'PromptForEnter
Private Sub PrintResultsOfCopy(ByVal Source As String, ByVal Value As CClassTest)
' let's see what we got from our Source
Console.WriteLine(ControlChars.CrLf & "Results of CopyMemory for CCLassTest:")
Console.WriteLine("Source String = " & Source)
Console.WriteLine("CClassTest.ID = " & CType(Value.ID, String))
Console.WriteLine("CClassTest.Name = " & CType(Value.Name, String))
Console.WriteLine("CClassTest.Address = " & CType(Value.Address, String))
' Prompt for user input
Call PromptForEnter()
End Sub 'PrintResultsOfCopy
'exception display, prompt and return
Private Sub CoughUpCookies(ByVal e As System.Exception)
Console.WriteLine()
Console.WriteLine("Exception Caught: " & e.Message)
Console.WriteLine()
' Prompt for user input
Call PromptForEnter()
End Sub 'CoughUpCookies
' exception display, prompt and return
Private Sub CoughUpCookies(ByVal e As System.OutOfMemoryException)
Console.WriteLine()
Console.WriteLine("Exception Caught: " & e.Message)
Console.WriteLine()
' Prompt for user input
Call PromptForEnter()
End Sub 'CoughUpCookies
'--------------- END OF CODE ---------------
End Module
Running the Sample
- In the Visual Studio.NET IDE, press the <F5> key.
- A Console window will appear and show you that our code works if you entered it exactly as shown above.
- When you are done, press <Enter> to close the program.
Points of Interest
ISSUE: You should note that, although the MSDN documentation for MarshalAs(UnManagedType.ByValTStr)
shows that this can be used to marshal strings for Classes and Structures, it does not work for Classes and Structures. One character is lost for every string in your class or structure (Standard Strings work fine). This is why I use Char Arrays in my Class and Structure examples
History
- Initial Article Date: 02/26/2003