-
GDI+ PInvoke madness
Hi to all, I am a very frustrated programmer!! In an attempt to speed up the performance of rendering to a high performance application I am writing I had the great idea of natively calling GDI+ through PInvoke in order to cut out the checking that the framework does.
I can create brushes, pens graphics paths but the PInvoke call to actually draw rectangle, paths etc always results in a weird Access violation. Furthermore if I create a routine to do the painting from a win32 .dll and PInvoke that from my application it works just fine. Same HDC by the way. Infact any valid HDC works from win32, but not when called from a .net app. Have microsoft locked GDI+ from within the framework, if so then how can it be possible to successfully create the brushes etc...
Any explanation would be gratefully recieved, i am an experienced c++ programmer porting to c#. For the life of me I can't understand why this exception occurs.
-
Re: GDI+ PInvoke madness
I'm not entirely surprised. However it should be possible even if you have to hack around a bit to make it work. Can you show us any of your code so we can fiddle with it?
In the meantime, take care that you are marshalling your structures properly in the .net app. If that's not right then you'll definitely get nasty results.
-
Re: GDI+ PInvoke madness
I'd be glad to show you some code. It's pretty simple
PInvoke Signatures..
Code:
[DllImport("gdiplus.dll", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
public static extern int GdipCreatePen1(int argb, float width, int unit, out IntPtr pen);
[DllImport("gdiplus.dll", CharSet=CharSet.Unicode, SetLastError=true, ExactSpelling=true)]
internal static extern int GdipDrawLine(IntPtr graphics, IntPtr pen, float x1, float y1, float x2, float y2);
The signatures are correct, as I have already checked in gdiplusflat.h and all arguments map to the correct .Net type.
Creating the pen is no problem, it seems to be a problem with GDI+ access the HDC provided. I have tried with all manner of different Dc's and still get the same access violation. The reason I am starting to think that microsoft locked GDI+ somehow is that the drawing routines defined in System.Drawing.Graphics all use PInvoke to access the functions in Gdiplus.dll. The HDC used in the graphics class is the one returned from the call to BeginPaint in the base class control so it isn't doing anything different to what I am, or so i would think. Here is what I have tried so far..
GetWindowDC
Graphics.GetHDC - Didn't expect that one to work :-)
CreateCompatibleDC & CreateCompatibleBitmap
Used GetFieldInfo to retreive the actual HDC that the graphics class uses to PInvoke Gdiplus.dll
Used LoadLibrary with GetProcAddress and Marshall.GetDelegateForFunctionPointer.
I'm all out if ideas on this one, it'd just be interesting to see if it's even possible. Thanks...
-
Re: GDI+ PInvoke madness
If you use GetHDC then you need to have a matching call to ReleaseHDC (after you've finished drawing to it), otherwise you'll get among other things, access violations. We need to see some of algorithm source code, not just a bunch of API declarations. :)
-
Re: GDI+ PInvoke madness
Yes, ReleaseDC would be assumed based on my background, i just left it out for simplicity, however it never actually get as far as ReleaseDC as the following code throws the exception on the GdipDrawLine.
Code:
[DllImport("gdiplus.dll", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
public static extern int GdipCreatePen1(int argb, float width, int unit, out IntPtr pen);
[DllImport("gdiplus.dll", CharSet=CharSet.Unicode, SetLastError=true, ExactSpelling=true)]
internal static extern int GdipDrawLine(IntPtr graphics, IntPtr pen, float x1, float y1, float x2, float y2);
[DllImport("user32.dll",EntryPoint="GetWindowDC")]
public static extern IntPtr GetWindowDC(IntPtr HWND);
[DllImport("user32.dll",EntryPoint="ReleaseDC")]
public static extern IntPtr ReleaseDC(IntPtr hWnd,IntPtr hDc);
IntPtr HDC = GetWindowDC(Base.Handle);
Intptr HPEN = IntPtr.Zero;
GdipCreatePen1(Color.Blue.ToARGB(), 1.0F, 0, out HPEN);
// HPEN is populated with a valid pointer here.
GdipDrawline(HDC, HPEN, (float)10.0, (float)10.0, (float)20.0, (float)10.0);
// Exception thrown on the above line.
ReleaseDC(base.Handle, HDC);
//Never gets to the above line.
I have initialized the GDI+ runtime using the correct token, however this didn't work, and frankly I should have to because System.Drawing does this implicitly. Work perfectly when I wrote a function in a Win32 .dll that takes the DC as a parameter and PInvoked from .Net. Doesn't work from within .Net.
-
Re: GDI+ PInvoke madness
Just incase anyone encounters the same thing, I believe I have found the problem. Unfortunately it can't be resolved... I don't think.
When System.Drawing initializes Gdiplus it also installs an Atom in the Kernal32 atom table that is associated with the current thread id of System.Drawing. Gdiplus must check for this atom internally and only allow calls that match this unique atom.
Interesting approach from Microsoft, But i'm sure they had their reasons.:)
-
Re: GDI+ PInvoke madness
There's always a way.
If you can throw together a project that demonstrates this exception then I can have a look on Monday when I'm using my windows PC. I'm on linux at the moment so I can't set this up right now.