Click here to Skip to main content
15,881,625 members
Articles / Programming Languages / Visual Basic
Article

Using CopyMemory in .NET

Rate me:
Please Sign up or sign in to vote.
4.76/5 (19 votes)
8 Mar 20034 min read 152.1K   1.8K   40   5
Demonstrates the use of CopyMemory in .NET to initialize classes

Sample Image - NETCopyMemorySample.gif

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.
Here is what your module should have in it right now:
VB
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:
VB
Public Class WINAPI

End Class
  • Copy or type in the following code so your WINAPI Class looks like this:
VB
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:
VB
Public Class CClassTest

End Class
  • Now, type or copy in the following Class declaration:
VB
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:
VB
' 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 (specifically OutOfMemory 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 the WINAPI object to copy the string into the memory that we set aside for clsTest (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 by p_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):

VB
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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralAttempted to read or write protected memory. This is often an indication that other memory is corrupt. Pin
dheeraj.mittal29-Oct-09 1:36
dheeraj.mittal29-Oct-09 1:36 
GeneralDoesn't work in VS 2005 Pin
avirami21-Dec-05 18:36
avirami21-Dec-05 18:36 
GeneralNo need to use P/Invoke Pin
Richard Deeming9-Mar-03 23:20
mveRichard Deeming9-Mar-03 23:20 
GeneralRe: No need to use P/Invoke Pin
Anonymous8-Nov-03 4:07
Anonymous8-Nov-03 4:07 
GeneralcOPY Pin
D_Ana26-Apr-10 16:54
D_Ana26-Apr-10 16:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.