Forum guidelines
AuthorQuestion
CopyMemory in C#
Posted on: 09 Oct 2008

Hello, I am trying to convert an existing VB program to C#.net and am having problems with CopyMemory.  The program I am writing is a DLL which plugs in to 3rd party program (which is working), however, I need access to an array of numbers which is passed to the routine but it is passed as a double so I belive it is the memory location of the required array.  This has been done in VB by calling CopyMemory (Options(0), narray, 40) where:- Options(0 to 4) as Double and narray is the double passed from the program to the routine.

Does anyone know how CopyMemory works or how I can convert a double to IntPtr so I can use CopyMemory in C#?

Any suggestion would be grateful.


AuthorReply
Re: CopyMemory in C#
Posted on: 09 Oct 2008  
Try This Online Code converter ( vb <==> C# Converter)

http://www.developerfusion.com/tools/convert/csharp-to-vb/

MY BLOG ( www.codestructs.com ) | MY SITE

Kirtan
 Patel   BCA   MCA (SEMESTER-IV)  MINDCRACKER MVP WEB HACKER



Alan
  • 0
  • 0
Re: CopyMemory in C#
Posted on: 09 Oct 2008  

Although it's more usual to use the managed method Marshal.Copy, you can in fact use CopyMemory, which is an alias for the API function RtlMoveMemory, from C#.

You can even use it to copy one managed array to another as the following simple example shows:

using System;
using System.Runtime.InteropServices;

class Test
{

    [DllImport("kernel32.dll", EntryPoint="RtlMoveMemory")]
    static extern void CopyMemory(double[] Destination, double[] Source, uint Length);

    static void Main()
    {
       double[] Source = new double[5]{1.0, 2.0, 3.0, 4.0, 5.0};
       double[] Destination = new double[5];  // all 0.0 initially   
       CopyMemory(Destination, Source, 40);
       foreach(double d in Destination)
       {
           Console.WriteLine(d);
       }
       Console.ReadLine();
    }
}

 

 


Re: CopyMemory in C#
Posted on: 10 Oct 2008  

I am currently using the CopyMemory as shown above, however, the variable which is passed to the function I have now deciphered is to the first variable in the array from the 3rd party program. 

In VB, by using CopyMemory (Options(0), narray, 40) it takes 40 bytes from where [narray] starts in memory and copies that 40 bytes to the beginning of the [Options] array, populating the array with the values from the 3rd party program.

I have attempted this in C# and only the first entry of the [Options] array gets populated.  I am passing the [narray] variable as "ref" but when I get the IntPtr of the variable it seems to be to the variable in the function not from the 3rd party program.  It seems to be  passing as "ref" as I can set the value and the 3rd party program knows that it has been changed.

I am using:

public static int UserFunction (ref double narray)

IntPtr myarrpointer;

IntPtr arrpasspointer;

double[] myarray = new double[5]

GCHandle myGC  = GCHandle.Alloc(myarray, GCHandleType.Pinned);

GCHandle inGC = GCHandle.Alloc(narray, GCHandleType.Pinned);

myarrpointer = myGC.AddrOfPinnedObject();

arrpasspointer = inGC.AddrOfPinnedObject();

CopyMemory (myarrpointer, arrpasspointer, 40);


Alan
  • 0
  • 0
Re: CopyMemory in C#
Posted on: 10 Oct 2008  

Can you just explain how you're getting 'narray' in the first place?

It sounds to me as though it should be an IntPtr to an unmanaged double array of five elements but  you seem to have it as a managed scalar double.

If it's a managed scalar double passed by reference, then CopyMemory will copy only one element to the array, namely the double contained in narray itself.


Re: CopyMemory in C#
Posted on: 10 Oct 2008  

Thanks for the post Alan,

narray is passed to the routine from a 3rd party program.  I had the same thought as you that it would be the InPtr to the variable but narray does actually contain the 1st value of the array which is passed, and when the value is changed in C# it changes in the 3rd party program.

In VB it worked by this value being passed by reference and then CopyMemory just taking the 40 bytes from the memory location and passing it into the array. 

It must still work in the same way as the 3rd party program which calls the function is the same - does the "ref" command in C# work the same as "ByRef" in VB or is there another one? In VB, I could pass the variables by name to CopyMemory is there a way of doing that in C#.


Alan
  • 0
  • 0
Re: CopyMemory in C#
Posted on: 10 Oct 2008  

'ref' in C# does work the same way as 'ByRef' in VB but the difference is that you're going through the p/invoke layer. So the double value that comes back is copied to managed memory and  is isolated from the rest of the array which is still on the unmanaged heap.

If you change the signature of the 3rd party function from 'ref double narray' to 'IntPtr narray' then it should still work as we're dealing with four byte pointers in both cases.

All you need to do then is to use the IntPtr to copy the unmanaged array to the managed array. Rather than CopyMemory, I'd use this overload of Marshal.Copy:

http://msdn.microsoft.com/en-us/library/ms146633.aspx

So the code would simply be:

double[] myarray = new double[5];
Marshal.Copy(narray, myarray, 0, 5);


Re: CopyMemory in C#
Posted on: 11 Oct 2008  

Thanks Alan, that worked great - you are most knowledgable.

Could you possibly offer me your wisdom on the problem below?

I need to pass a string from 3rd party program and maniuplate it and send it back.  It is defined as UserInstruction(string myString); which C# can use but any changes are not echoed in the 3rd party program.  I have used the "ref" command but this causes a crash.  Any ideas?


Alan
  • 0
  • 0
Re: CopyMemory in C#
Posted on: 11 Oct 2008  

The problem there, Darren, is that .NET strings are immutable.

However, luckily, we do have the StringBuilder type which is mutable and can be used to return strings when calling unmanaged code. As it's already a reference type it should just be passed by value:

UserInstruction(StringBuilder myStringBuilder);

If you know what the size of the string is going to be then it's a good idea to initialize the StringBuilder with the appropriate capacity. For example if the string is 100 characters long:

StringBuilder sb = new StringBuilder(100);

and then pass 'sb' to the unmanaged function.

You can, of course, get the string by simply appying the ToString() method to the StringBuilder object.


Re: CopyMemory in C#
Posted on: 13 Oct 2008  
Alan, thanks for the reply.  I have tried the method you have described but to no avail.  I have set the function as UserInstruction (StringBuilder myString) and have setup a new field StringBuilder mynewString = new StringBuilder(100); I have set this value to "THIS IS MY NEW STRING" and then set myString = mynewString to pass it back to the 3rd party program but it did not see the change.  Am I missing something? 
Alan
  • 0
  • 0
Re: CopyMemory in C#
Posted on: 13 Oct 2008  

Setting 'myString' to the changed string won't actually send it back to the 3rd party program because myString is pointing to a location in managed memory rather than unmanaged memory.

You'll need to call the function again to do this or, if this isn't feasible (because of what the function does), then you'll need to pass an IntPtr rather than a string to get the unmanaged memory address which you can then manipulate using Marshal class methods.

In fact, if you pass your own string to start with, you'll need to allocate some memory on the unmanaged heap and then pass an IntPtr to that.

If you can't call the function again, can you let me know what type of string (ansi, unicode or BSTR) the 3rd party program is expecting? If it's designed to be callable from VB, then it's probably the third of these but we'll need to know as the Marshal class has different methods for each one.


Re: CopyMemory in C#
Posted on: 14 Oct 2008  
Alan, not sure what you mean by this.  I understand that the string is not actually passed by reference, however, it does have to be a string which is passed to the routine. I cannot send the IntPtr to the variable as the 3rd party program specifies a string.  I am in agreement with you that it is a BSTR that is passed into the routine. The string that is passed in is just a value that can ferry information between 3rd party and C#.
Alan
  • 0
  • 0
Re: CopyMemory in C#
Posted on: 14 Oct 2008  

As far as the unmanaged function is concerned, the string parameter is just a pointer to where the actual string is stored so I don't think it will be a problem if you declare the function to receive an IntPtr rather than a string.

What I then had in mind was something like this (off top of my head):

 // fill this out to maximum number of characters needed
string s = "THIS IS MY NEW STRING";
// convert managed string to unmanaged BSTR
IntPtr ip = Marshal.StringToBSTR(s);
// pass pointer to BSTR to unmanaged function
UserInstruction(ip);
// presumably function now manipulates this string
// recover as managed string
s = Marshal.PtrToStringBSTR(ip);
// manipulate again, switch to lower case let's say
s = s.ToLower();
// copy the characters of s back to unmanaged memory
Marshal.Copy(s.ToCharArray(), 0, ip, s.Length);
// give unmanaged function time to read manipulated string
// and then release pointer
Marshal.FreeBSTR(ip);


Re: CopyMemory in C#
Posted on: 15 Oct 2008  

Alan, I have tried the method and after tweaking it seems to work in some capacity.  I passed the IntPtr into the function and then attempted to use Marshal.PtrToStringBST(mynewptr) but this crashed the program so I used Marshal.PtrToStringUni (mynewptr) and this gave me access to the string.  I then changed it's value and passed it back to the program using Marshal.Copy (myString.ToCharArray(), 0, mynewptr, myString.length) which has worked, the 3rd party program can see that it has changed. However, for some reason there seems to be a null character after each letter i.e I have changed = Hex (49 00 20 00 68 00...etc) - do you know why this is?


Alan
  • 0
  • 0
Re: CopyMemory in C#
Posted on: 15 Oct 2008  

Unicode characters require two bytes per character.

As the  ASCII characters (codes 32 - 127) only require one byte this means that the other byte will be a null ('\0').

If the 3rd party dll is using unicode characters, this shouldn't be a problem. However, if it's using ASCII (i.e. single byte characters), we could strip out the nulls (by converting the managed characters to bytes) before sending the string back.


Re: CopyMemory in C#
Posted on: 16 Oct 2008  
Alan, cheers for all your help on this subject - I have converted the string to bytes using System.Text.ASCIIEncoding and this seems to have worked great.

SPONSORED BY

Custom Software Development
MCN is your source for developing solutions involving websites, mobile apps, cloud-computing, databases, BI, back-end services and processes and client-server applications.