I'm writing a C# application, and I need part of it to be in C++ for speed efficiency.
The function in C++ is exported as follow:
extern "C" __declspec(dllexport) int fastSegment(char*, int, int);
I import this function in C# as follow:
[DllImport(_dllname, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static private extern bool fastSegment(byte[] img, int width, int height);
The image I want to process is called as follow:
fastSegment(image, 640, 480);
image is of the right size. Since I don't want to waste memory and allocate a new array, I modified the array directly in the C++ function.
What happens? Blue screen of death. I never saw it in Windows 7 before.
My function only wrote in image[0], image[1] and image[2] for testing purposes, and when I remove this everything is fine.
My guess is that the virtual machine protected the memory, but I find it strange I just can't write in it, or that the virtual machine didn't simply throw an exception. Is there a way to unprotect that buffer, or do I have to allocate a new one?
Edit:
It appiers the program runs now when I write in data. What could have been the cause of this sudden crash?
The garbage collector is allowed to move managed data. The native code is not able to detect this, so it is possible that it writes to the wrong memory address. But you can tell the .NET runtime not to move your array. E.g. with the GCHandle class:
GCHandle handle = GCHandle.Alloc(image, GCHandleType.Pinned);
try
{
fastSegment(handle.AddrOfPinnedObject(), 640, 480);
}
finally
{
// make sure to free the handle to avoid leaks!
handle.Free();
}
Edit: This is only one way, but I think it illustrates the problem. Please read about Marshaling between Managed and Unmanaged Code
You can't do that. Your array will be readonly in this case.
If you want to change your C# array in native you need to marshal it as a pointer.
Have a look at the example from Microsoft.
There is also an explanation why this is needed.
It is possible that your data is getting moved around by the GC - but if you get the crash every time you run your application it's highly unlikely thats the case. Try one of the following:
[DllImport(_dllname, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern bool fastSegment(IntPtr img, int width, int height);
[ReliabilityContract(Consistency.MayCorruptProcess, Cer.None)]
static void FastSegment(byte[] data, int width, int height)
{
var length = width * height;
var ptr = Marshal.AllocHGlobal(width * height);
try
{
Marshal.Copy(data, 0, ptr, length);
fastSegment(ptr, width, height);
Marshal.Copy(ptr, data, 0, length);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
// ---- OR ----
[DllImport(_dllname, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern bool fastSegment(IntPtr img, int width, int height);
[ReliabilityContract(Consistency.MayCorruptProcess, Cer.None)]
static void FastSegment(byte[] data, int width, int height)
{
var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
try
{
fastSegment(handle.AddrOfPinnedObject(), width, height);
}
finally
{
handle.Free();
}
}
// ---- OR ----
[DllImport(_dllname, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static unsafe extern bool fastSegment(byte* img, int width, int height);
[ReliabilityContract(Consistency.MayCorruptProcess, Cer.None)]
static void FastSegment(byte[] data, int width, int height)
{
unsafe
{
fixed (byte* dataPinned = data)
{
fastSegment(dataPinned, width, height);
}
}
}
Related
I have a Delphi function in a dll written like this:
function LensFlare(Bitmap: TBitmap; X, Y: Int32; Brightness: Real): TBitmap; StdCall;
Begin
// ...
Result := Bitmap;
End;
I want to use it in C#,
I have tried this but I was not successful:
[DllImport("ImageProcessor")]
static extern Bitmap LensFlare(Bitmap bitmap, int x, int y, double Brightness);
private void button1_Click(object sender, EventArgs e)
{
Bitmap b = new Bitmap(#"d:\a.bmp");
pictureBox1.Image = LensFlare(b, 100, 100, 50); // Error!
}
Error: "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
How can I do this?
You can't use this function. It's not even safe to use between two Delphi modules unless you use packages. You cannot pass native Delphi classes across a module boundary like that.
You'll need to switch to an interop friendly type. The obvious option is to use an HBITMAP. You will need to modify the Delphi library. If you don't have the source you will need to contact the original developer.
Delphi's TBitmap class is very different than .NET's Bitmap class. They are not compatible with each other, and neither is safe for interoperability purposes.
You will have to use a raw Win32 HBITMAP handle instead.
function LensFlare(Bitmap: HBITMAP; X, Y: Int32; Brightness: Real): HBITMAP; StdCall;
Begin
// ...
Result := Bitmap;
End;
[DllImport("ImageProcessor")]
static extern IntPtr LensFlare(PtrInt bitmap, int x, int y, double Brightness);
[DllImport("gdi32.dll")]
static extern bool DeleteObject(IntPtr hObject);
private void button1_Click(object sender, EventArgs e)
{
Bitmap b = new Bitmap(#"d:\a.bmp");
IntPtr hbmp = LensFlare(b.GetHbitmap(), 100, 100, 50);
try {
pictureBox1.Image = Image.FromHbitmap(hbmp);
}
finally {
DeleteObject(hbmp);
}
}
I have a C# DLL, whose code derives from a base class, which is written in managed C++. (I don't have any control over the base class code)
This base class (which is in managed C++) has a member
int *buffer
is expected to be memset (filled with Zeros) by the derived class (which is in C#). The derived class knows the size of the buffer.
I am using unsafe context, to access the member "int *buffer" of the base class, in the derived class. Please let me know is there any way special way to memset the buffer in "unsafe" context in c#.
I already looked into this What is the equivalent of memset in C#? for details, but I would like to know is there something specifically for "unsafe" context.
Background : This is a conversion project, where the derived class itself was in managed c++ before. Now I am converting the derived class DLL alone to C#. Also I have no control over the base class code! The current code flow is as follows: Only the derived class knows the size of the buffer. The base class creates a memory for that particular size, by getting the size of the buffer from derived, but it doesn't zero fill. The derived class Zero fills it first and then need to appropriately fill the buffer with its contents. Though strange, that is how it is.
Thanks!
Well, there is... memset. Why settle for a replacement when you can p/invoke the real thing?
[DllImport("msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static extern IntPtr MemSet(IntPtr dest, int c, IntPtr count);
Taken from pinvoke.net
edit
As #Hans rightfully mentions in the OP comments, this is useless if you don't already know the size of *buffer.
You can code it on your own:
void memset( byte* buffer, int value, int size )
{
for( int i = 0; i < count; i++)
{
*( buffer + i ) = value;
}
}
Or you can use an API for this. Actually RtlZeroMemory sets values to zero. It's not actually memset.
[DllImport("kernel32.dll")]
static extern void RtlZeroMemory(IntPtr dst, int length);
RtlZeroMemory(buffer, bufferLength);
RtlZeroMemory is not actually an entry point in kernel32. If yo want something like that, use this in C#
public static unsafe void ZeroMemory(IntPtr Safebuffer, int count)
{
if (count == 0) return;
byte* buffer = (byte*)Safebuffer.ToPointer();
memset(buffer, count);
}
public static unsafe void ZeroMemory(byte* buffer, int count)
{
if (count == 0) return;
while (count-- > 0)
{
buffer[count] = 0;
}
}
Asked a few questions about a project I was working on, got some good feedback and made some progress. The idea is to create an application that generates images of fractals, accelerated by CUDA. I am creating the ui in C# and having a DLL do the heavy lifting.
Basically, I am allocating a byte array in C#, passing that to the dll to fill with pixel data, and then using that to create a Bitmap and display that with a Windows Forms PictureBox in the ui. Previous questions have helped - was using dll to allocate memory before, now using consistent calling convention between dll and c#, but the code still gives an System.ArgumentException at "img = new Bitmap(...)
Relevant Code:
C++
extern "C" __declspec(dllexport) void __cdecl generateBitmap(void *bitmap)
{
int width = 1920;
int height = 1080;
int *dev_bmp;
gpuErrchk(cudaMalloc((void**)&dev_bmp, (3*width*height*sizeof(int))));
kernel<<<BLOCKS_PER_GRID, THREADS_PER_BLOCK>>>(dev_bmp, width, height);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
gpuErrchk(cudaMemcpy(bitmap, dev_bmp, (width*height*3), cudaMemcpyDeviceToHost));
cudaFree(dev_bmp);
}
c#
public unsafe class NativeMethods
{
[DllImport(#"C:\Users\Bill\Documents\Visual Studio 2012\Projects\FractalMaxUnmanaged\Debug\FractalMaxUnmanaged.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern void generateBitmap(void *bitmap);
public static Bitmap create()
{
byte[] buf = new byte[1920 * 1080 * 3];
fixed (void* pBuffer = buf)
{
generateBitmap(pBuffer);
}
IntPtr unmanagedPtr = Marshal.AllocHGlobal(buf.Length);
Marshal.Copy(buf, 0, unmanagedPtr, buf.Length);
Bitmap img = new Bitmap(1920, 1080, 3, PixelFormat.Format24bppRgb, unmanagedPtr);
Marshal.FreeHGlobal(unmanagedPtr);
return img;
}
}
//...
private unsafe void mandlebrotButton_Click(object sender, EventArgs e)
{
FractalBox1.Image = (Image)NativeMethods.create();
}
What am I still doing wrong? As far as I can tell, all the parameters are invalid, but I get an invalid parameter exception in System.Drawing when I try to create the bitmap.
I am not sure what happens exactly in your case cause you didn't specify which parameter is invalid in the exception. I see that your stride must not be correct.
stride Type: System.Int32
Integer that specifies the byte offset between the beginning of one
scan line and the next. This is usually (but not necessarily) the
number of bytes in the pixel format (for example, 2 for 16 bits per
pixel) multiplied by the width of the bitmap. The value passed to this
parameter must be a multiple of four..
So your constructor should be like this:
Bitmap img = new Bitmap(1920, 1080, 1920 * 3, PixelFormat.Format24bppRgb, unmanagedPtr);
I have a buffer (uint8[] of BGR pixel data) in C holding a video frame. A pointer to this buffer is passed back by the C code to C# code as an IntPtr. I require to add a text overlay to the each frame and then pass on a pointer to the frame for further processing. I believe what I need to do (in C#) is to copy each frame to a bitmap object, get the device context of the bitmap and use then use TextOut (etc) to write text to the bitmap. I would then copy the modified bitmap frame data back to my original array.
My question is twofold:
Is this the best approach?
What is the best (fastest) way to copy the data from my IntPtr to a bitmap object.
Thanks.
The fastest way is by not copying the data. That requires that your data is in a supported pixel format, BGR sounds a bit scary but odds are high it is actually PixelFormat.Format24bppRgb.
Which then allows you to use the Bitmap(int, int, int, PixelFormat, IntPtr constructor).
I can't comment on your approach, but the fastest way to copy data using two pointers would be to do a Platform Invoke call to the memcpy function in msvcrt.dll
Code example below, taken from the WriteableBitmapEx source
internal static class NativeMethods
{
internal static unsafe void CopyUnmanagedMemory(byte* srcPtr, int srcOffset,
byte* dstPtr, int dstOffset, int count)
{
srcPtr += srcOffset;
dstPtr += dstOffset;
memcpy(dstPtr, srcPtr, count);
}
// Win32 memory copy function
[DllImport("msvcrt.dll", EntryPoint = "memcpy",
CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
private static extern unsafe byte* memcpy(byte* dst, byte* src, int count);
}
To convert an IntPtr to byte* simply use
unsafe
{
IntPtr myPtr;
byte* bytePtr = (byte*)myPtr.ToPointer();
}
As I understand it, marking an method as unsafe will disable some of the CLR checks on that code, but does this have any effect on the rest of the system which is safe, other than the fact that the DLL/EXE can not run in a untrusted environment.
In particular,
Are they are any safety checks that will not work on the complete dll because it is marked as unsafe?
If a DLL is marked as unsafe, but the methods marked as unsafe are
not actually called, is this the same as if the DLL is marked as
safe?
Are they any run-time benefits on keeping the unsafe code in a
separate DLL?
I have the problem with redrawing nested controls on 64-bit windows as detailed here and the one the solutions (the one that appears to work) involves unsafe code and I would like to understand the effect that adding this code has to my project.
An unsafe code is capable of corrupting the managed heap. As such, anything that runs in the same process can be affected.
This includes all other libraries and potentially all other AppDomains in the same process.
UPDATE
Here is an example:
http://blogs.msdn.com/b/tess/archive/2006/02/09/net-crash-managed-heap-corruption-calling-unmanaged-code.aspx
UPDATE 2
Is unsafe code that is written
diligently bad?
No. There are tons of unsafe code in the .NET framework itself. Examples many, but here is one in the System.String:
public static unsafe string Copy(string str)
{
if (str == null)
{
throw new ArgumentNullException("str");
}
int length = str.Length;
string str2 = FastAllocateString(length);
fixed (char* chRef = &str2.m_firstChar)
{
fixed (char* chRef2 = &str.m_firstChar)
{
wstrcpyPtrAligned(chRef, chRef2, length);
}
}
return str2;
}
The answer to your question is: The unsafe keyword does not mean "unsafe", it means "potentially unsafe". The compiler and framework cannot work to make certain that it's safe. It is up to you to make certain that the code cannot perform unsafe reads or writes to memory.
I would strongly encourage you to follow this advice given in the article you linked:
1) Redesign the application to have less containers and reduce the number of nesting levels.
If you're using containers for the sole purpose of control arrangement, write your own container that can do all the arrangement with one level.
Updated
You can modify the code in that article so that it doesn't use pointers (i.e. doesn't require the unsafe keyword). Keep in mind that this will now require marshalling which means extra copying. This is probably a good thing because the original code is passing a WINDOWPOS pointer from the OS to BeginInvoke which does not execute during the same dispatch event that the OS generated the pointer in. In other words, that code was smelly already.
internal class MyTabPage : TabPage
{
private const int WM_WINDOWPOSCHANGING = 70;
private const int WM_SETREDRAW = 0xB;
private const int SWP_NOACTIVATE = 0x0010;
private const int SWP_NOZORDER = 0x0004;
private const int SWP_NOSIZE = 0x0001;
private const int SWP_NOMOVE = 0x0002;
[DllImport("User32.dll", CharSet = CharSet.Auto)]
extern static int SendMessage(HandleRef hWnd, int msg, int wParam, int lParam);
[DllImport("User32.dll", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
extern static bool SetWindowPos(HandleRef hWnd, HandleRef hWndInsertAfter,
int x, int y, int cx, int cy, int flags);
[StructLayout(LayoutKind.Sequential)]
private class WINDOWPOS
{
public IntPtr hwnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public int flags;
};
private delegate void ResizeChildDelegate(WINDOWPOS wpos);
private void ResizeChild(WINDOWPOS wpos)
{
// verify if it's the right instance of MyPanel if needed
if ((this.Controls.Count == 1) && (this.Controls[0] is Panel))
{
Panel child = this.Controls[0] as Panel;
// stop window redraw to avoid flicker
SendMessage(new HandleRef(child, child.Handle), WM_SETREDRAW, 0, 0);
// start a new stack of SetWindowPos calls
SetWindowPos(new HandleRef(child, child.Handle), new HandleRef(null, IntPtr.Zero),
0, 0, wpos.cx, wpos.cy, SWP_NOACTIVATE | SWP_NOZORDER);
// turn window repainting back on
SendMessage(new HandleRef(child, child.Handle), WM_SETREDRAW, 1, 0);
// send repaint message to this control and its children
this.Invalidate(true);
}
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_WINDOWPOSCHANGING)
{
WINDOWPOS wpos = new WINDOWPOS();
Marshal.PtrToStructure(m.LParam, wpos);
Debug.WriteLine("WM_WINDOWPOSCHANGING received by " + this.Name + " flags " + wpos.flags);
if (((wpos.flags & (SWP_NOZORDER | SWP_NOACTIVATE)) == (SWP_NOZORDER | SWP_NOACTIVATE)) &&
((wpos.flags & ~(SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE)) == 0))
{
if ((wpos.cx != this.Width) || (wpos.cy != this.Height))
{
BeginInvoke(new ResizeChildDelegate(ResizeChild), wpos);
return;
}
}
}
base.WndProc(ref m);
}
}
Note: The change in WINDOWPOS from value type to reference type is intentional. Using a reference type reduces the number of copies to just one (the initial marshal)(**).
Updated Again
I just noticed that the code originally made the p/invoke declarations public. Never, ever expose p/invoke outside of a class(*). Write managed methods that invoke private p/invoke declarations if your intent is to expose the capabilities provided; which in this case is not true, the p/invoke is strictly internal.
(*) Ok, one exception. You're creating a NativeMethods, UnsafeNativeMethods, etc. Which is the recommended way to do p/invoke by FxCop.
Updated
(**) I was asked (elsewhere) to describe precicely why using a reference type here is better, so I've added that info here. The question I was asked was, "Doesn't this add memory pressure?"
If WINDOWPOS was a value type, this would be the sequence of events:
1) Copy from unmanaged to managed memory
WINDOWPOS wpos = Marshal.PtrToStructure(m.LParam, typeof(WINDOWPOS));
2) Second copy?
BeginInvoke(new ResizeChildDelegate(ResizeChild), wpos);
Wait! The signature of BeginInvoke is (Delegate, params object[]). That means wpos is going to get boxed. So yes, a second copy occurs here: The boxing operation.
BeginInvoke will add the delegate and object[] to an invocation list and post a registered window message. When that message is removed from the queue by the message pump, the delegate will be called with the object[] parameters.
3) Unbox and copy for ResizeChild call.
At this point you can see that the number of copies isn't even the issue. The fact that it gets converted to a reference type (boxed) means that we are better off making it a reference type to begin with.