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.
Darren FormosaPosted Oct 16, 2008, 6:28 AM
AlanPosted Oct 15, 2008, 11:04 AM
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.
Darren FormosaPosted Oct 15, 2008, 5:29 AM
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?
AlanPosted Oct 14, 2008, 5:19 PM
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);
Darren FormosaPosted Oct 14, 2008, 8:29 AM
AlanPosted Oct 13, 2008, 2:26 PM
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.
Darren FormosaPosted Oct 13, 2008, 5:15 AM
AlanPosted Oct 11, 2008, 7:50 AM
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.
Darren FormosaPosted Oct 11, 2008, 7:09 AM
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?
AlanPosted Oct 10, 2008, 4:37 PM
'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);
Darren FormosaPosted Oct 10, 2008, 2:33 PM
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#.
AlanPosted Oct 10, 2008, 10:57 AM
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.
Darren FormosaPosted Oct 10, 2008, 8:52 AM
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);
AlanPosted Oct 9, 2008, 2:00 PM
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();
}
}
Kirtan PatelPosted Oct 9, 2008, 10:28 AM
http://www.developerfusion.com/tools/convert/csharp-to-vb/