Problem passing data to an activeX
-
I'm using in a project an activeX component with these 3 methods:
public void PutData(ref int pData, int nBytes); public int GetDataSize(); public void GetData(ref int pData);
The same functions, in a c++ project are:void PutData(long* pData, long nBytes); long GetDataSize(); void GetData(long* pData);
With the first function I pass a buffer to the component and the component copies it in its own buffer. With the third function I pass a buffer to the component and the component copies in my buffer the data that it previously stored in its own buffer. I can't get the component work properly, because it seems to copy only the first 4 bytes (= the first integer). I tried the component in a c++ project and it works properly, so I think that the problem is in the way I'm passing the data to the activex. I use the functions in this way:int[] buffer = new int[10]; int[] buffer_check = new int[10]; for (int i=0; i<10; i++) buffer[i] = i; ocx.PutData(ref buffer[0], 10*sizeof(int)); //That's works OK System.Diagnostics.Debug.Assert(ocx.GetDataSize() == 10*sizeof(int)); ocx.GetData(ref buffer_check[0], 10*sizeof(int)); //This fails for any i != 0 for (i=0; i<10; i++) System.Diagnostics.Debug.Assert(buffer_check[i] == buffer[i]);
Someone has any idea about what I'm doing wrong? Thanks! Paolo -
I'm using in a project an activeX component with these 3 methods:
public void PutData(ref int pData, int nBytes); public int GetDataSize(); public void GetData(ref int pData);
The same functions, in a c++ project are:void PutData(long* pData, long nBytes); long GetDataSize(); void GetData(long* pData);
With the first function I pass a buffer to the component and the component copies it in its own buffer. With the third function I pass a buffer to the component and the component copies in my buffer the data that it previously stored in its own buffer. I can't get the component work properly, because it seems to copy only the first 4 bytes (= the first integer). I tried the component in a c++ project and it works properly, so I think that the problem is in the way I'm passing the data to the activex. I use the functions in this way:int[] buffer = new int[10]; int[] buffer_check = new int[10]; for (int i=0; i<10; i++) buffer[i] = i; ocx.PutData(ref buffer[0], 10*sizeof(int)); //That's works OK System.Diagnostics.Debug.Assert(ocx.GetDataSize() == 10*sizeof(int)); ocx.GetData(ref buffer_check[0], 10*sizeof(int)); //This fails for any i != 0 for (i=0; i<10; i++) System.Diagnostics.Debug.Assert(buffer_check[i] == buffer[i]);
Someone has any idea about what I'm doing wrong? Thanks! PaoloHi Paolo, The ActiveX is expecting a pointer to, presumably, an array of long values. But from C# you are only passing a single Int32. You need to use an IntPtr to pass the data back and forth, like so:
// Create buffer
Int32[] buffer = new Int32[10];
buffer[5] = 1;// Allocate and copy to unmanaged memory
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Int32)) * 10);
Marshal.Copy(buffer, 0, ptr, 10);// Change buffer, so we can see that the ptr has restores the
// old value
buffer[5] = 2;// Copy from unmanged memory back to array
Marshal.Copy(ptr, buffer, 0, 10);// Free memory, or else we will leak it
Marshal.FreeHGlobal(ptr);Note: The Marshal class is in the System.Runtime.InteropServices namespace.
Take care, Tom ----------------------------------------------- Check out my blog at http://tjoe.wordpress.com
-
Hi Paolo, The ActiveX is expecting a pointer to, presumably, an array of long values. But from C# you are only passing a single Int32. You need to use an IntPtr to pass the data back and forth, like so:
// Create buffer
Int32[] buffer = new Int32[10];
buffer[5] = 1;// Allocate and copy to unmanaged memory
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Int32)) * 10);
Marshal.Copy(buffer, 0, ptr, 10);// Change buffer, so we can see that the ptr has restores the
// old value
buffer[5] = 2;// Copy from unmanged memory back to array
Marshal.Copy(ptr, buffer, 0, 10);// Free memory, or else we will leak it
Marshal.FreeHGlobal(ptr);Note: The Marshal class is in the System.Runtime.InteropServices namespace.
Take care, Tom ----------------------------------------------- Check out my blog at http://tjoe.wordpress.com
It doesn't work :( Your code works, but it stops if I use different unmanaged memory areas when storing and when retrieving or if I change the data in the unmanaged memory. For example, this code doesn't work:
// Create buffer Int32[] buffer = new Int32[10]; buffer[5] = 1; // Allocate and copy to unmanaged memory IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Int32)) * 10); Marshal.Copy(buffer, 0, ptr, 10); // **** Need to add this, otherwise c# complains about not being able to convert IntPtr to int int bptr = (int)ptr; ocx.PutData(ref bptr, Marshal.SizeOf(typeof(Int32)) * 10); // Change buffer, so we can see that the ptr has restored the // old value buffer[5] = 2; // **** Allocate another are to retrieve data IntPtr ptr2 = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Int32)) * 10); int bptr2 = (int)ptr2; ocx.GetData(ref bptr2); // **** Here there is the problem: when GetData() returns, // the bptr2 variable has been changed and its value is = bptr = (int ptr) // Copy from unmanged memory back to array Marshal.Copy(ptr2, buffer, 0, 10); // Free memory, or else we will leak it Marshal.FreeHGlobal(ptr); Marshal.FreeHGlobal(ptr2);
-
It doesn't work :( Your code works, but it stops if I use different unmanaged memory areas when storing and when retrieving or if I change the data in the unmanaged memory. For example, this code doesn't work:
// Create buffer Int32[] buffer = new Int32[10]; buffer[5] = 1; // Allocate and copy to unmanaged memory IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Int32)) * 10); Marshal.Copy(buffer, 0, ptr, 10); // **** Need to add this, otherwise c# complains about not being able to convert IntPtr to int int bptr = (int)ptr; ocx.PutData(ref bptr, Marshal.SizeOf(typeof(Int32)) * 10); // Change buffer, so we can see that the ptr has restored the // old value buffer[5] = 2; // **** Allocate another are to retrieve data IntPtr ptr2 = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Int32)) * 10); int bptr2 = (int)ptr2; ocx.GetData(ref bptr2); // **** Here there is the problem: when GetData() returns, // the bptr2 variable has been changed and its value is = bptr = (int ptr) // Copy from unmanged memory back to array Marshal.Copy(ptr2, buffer, 0, 10); // Free memory, or else we will leak it Marshal.FreeHGlobal(ptr); Marshal.FreeHGlobal(ptr2);
Do you have access to the ActiveX source code? Or is it publicly available? If you have access, could you post examples of PutData and GetData. If it's publicly available, could you please provide a link. If GetData is updating bptr2 so that it has the same value as bptr, then I would assume that PutData simply stored the long* that was passed in (instead of copying it). Then GetData is simply passing out the same long*. If this is the case then you do not need to allocate ptr2, because GetData does not required you to pass in a pre-allocated buffer. This really depends on how the OCX methods are written and you would need a full understanding of how they work to properly use them. For example, if the PutData does in fact save the long* that was passed in, then the code below would cause access violations:
// Create buffer
Int32[] buffer = new Int32[10];
buffer[5] = 1;// Allocate and copy to unmanaged memory
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Int32)) * 10);
Marshal.Copy(buffer, 0, ptr, 10);// **** Need to add this, otherwise c# complains about not being able to convert IntPtr to int
int bptr = (int)ptr;
ocx.PutData(ref bptr, Marshal.SizeOf(typeof(Int32)) * 10);// Change buffer, so we can see that the ptr has restored the
// old value
buffer[5] = 2;// Free memory, or else we will leak it
Marshal.FreeHGlobal(ptr);// ... Somewhere else in code
// **** Allocate another are to retrieve data
IntPtr ptr2 = IntPtr.Zero;
int bptr2 = (int)ptr2;ocx.GetData(ref bptr2);
// Copy from unmanged memory back to array
Marshal.Copy(ptr2, buffer, 0, 10);In the code above, the long* that was cached is freed and then is accessed later. If you are only using the OCX in a serial fashion like your example, then it would be fine. Let me know if this makes sense.
Take care, Tom ----------------------------------------------- Check out my blog at http://tjoe.wordpress.com
-
Do you have access to the ActiveX source code? Or is it publicly available? If you have access, could you post examples of PutData and GetData. If it's publicly available, could you please provide a link. If GetData is updating bptr2 so that it has the same value as bptr, then I would assume that PutData simply stored the long* that was passed in (instead of copying it). Then GetData is simply passing out the same long*. If this is the case then you do not need to allocate ptr2, because GetData does not required you to pass in a pre-allocated buffer. This really depends on how the OCX methods are written and you would need a full understanding of how they work to properly use them. For example, if the PutData does in fact save the long* that was passed in, then the code below would cause access violations:
// Create buffer
Int32[] buffer = new Int32[10];
buffer[5] = 1;// Allocate and copy to unmanaged memory
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Int32)) * 10);
Marshal.Copy(buffer, 0, ptr, 10);// **** Need to add this, otherwise c# complains about not being able to convert IntPtr to int
int bptr = (int)ptr;
ocx.PutData(ref bptr, Marshal.SizeOf(typeof(Int32)) * 10);// Change buffer, so we can see that the ptr has restored the
// old value
buffer[5] = 2;// Free memory, or else we will leak it
Marshal.FreeHGlobal(ptr);// ... Somewhere else in code
// **** Allocate another are to retrieve data
IntPtr ptr2 = IntPtr.Zero;
int bptr2 = (int)ptr2;ocx.GetData(ref bptr2);
// Copy from unmanged memory back to array
Marshal.Copy(ptr2, buffer, 0, 10);In the code above, the long* that was cached is freed and then is accessed later. If you are only using the OCX in a serial fashion like your example, then it would be fine. Let me know if this makes sense.
Take care, Tom ----------------------------------------------- Check out my blog at http://tjoe.wordpress.com
No, I haven't access to the activeX source code, but the activeX it is freely available at http://www.kolbasoft.com/ I put the test projects (c# and c++), the link to the activeX and a a few notes in www.vernazza.org/Vecad.zip I agree with you, it seems that GetData is passing out the long* that I passed it before instead of the data pointed, but I can't understand why it works while I call that function from c++ , and it doesn't when I call it from c#!
-
No, I haven't access to the activeX source code, but the activeX it is freely available at http://www.kolbasoft.com/ I put the test projects (c# and c++), the link to the activeX and a a few notes in www.vernazza.org/Vecad.zip I agree with you, it seems that GetData is passing out the long* that I passed it before instead of the data pointed, but I can't understand why it works while I call that function from c++ , and it doesn't when I call it from c#!
You may want to use AllocCoTaskMem[^] instead (and it's associated free method), but I don't think that is your problem. Looking at the put code:
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Int32)) * 10);
Marshal.Copy(buffer, 0, ptr, 10);
int bptr = (int)ptr;
axVecad1.PutExData(ref bptr, 10 * 4);After the first line, ptr points to the allocated block of memory. The array is then copied to the allocated block. Next ptr is cast to an int, so bptr now holds the correct address to the buffer. Now, when passing in the address to the PutExData, the address of the bptr is passed in (ref bptr). What you really want to pass in is the value of bptr. So really you are passing in a pointer to bptr which has a pointer to the actual data (so a pointer to a pointer). Basically, the signature for the PutExData should have the first parameter as an "IntPtr", not as a "ref Int32". I don't think there is a way to properly pass in the IntPtr address when the signature has the ref value there. Because you would need to create an Int32 value at the same location as the allocated buffer. I realize that VS generated the signature of the method for you (more specifically, aximp.exe generates it). The best work-around (short of hand-writing all the code you need) I found was here[^]. Be sure to read the whole thread, he talks about activeX controls later in the thread. Hopefully this helps. UPDATE: Actually, it looks like aximp.exe can generate C# source code now, so that would probably be your best option.
Take care, Tom ----------------------------------------------- Check out my blog at http://tjoe.wordpress.com