Help with GDI+ interop in .Net - c#

I'm using some code to enable me to render rotated text with the TextRenderer.DrawText method. (By default, DrawText can only copy a straight forward x and y transform from a graphics object).
The code (C#) is from: connect.microsoft.com. See below for a VB conversion.
The code takes a graphics object, creates a device context and copies the transform matrix from the graphics object. It works, but I'd like, also, to set the TextRenderingHint, so I tried:
<DllImport("gdiplus.dll", CharSet:=CharSet.Unicode, SetLastError:=True, ExactSpelling:=True)> _
Public Shared Function GdipSetTextRenderingHint(ByVal graphics As HandleRef, ByVal textRenderingHint As System.Drawing.Text.TextRenderingHint) As Integer
End Function
And then after the SetClip statement, I place: GdipSetTextRenderingHint(hDC, someHint)
This gives me a memory access violation error, so I think I should be using something other than hDC as the argument.
I can get it to work by creating the device context from the original graphics object, and then creating another graphics object from the device context. I then set the hint on the new graphics object. This seems a bit convoluted so I was wondering if it was possible through interop.
VB.Net code conversion:
Friend Class TextRendererDC
Implements IDeviceContext
Implements IDisposable
Private graphics As Graphics
Private dc As IntPtr
Private Sub New()
End Sub
Public Sub New(ByVal g As Graphics)
Me.graphics = g
End Sub
Public Function GetHdc() As IntPtr Implements System.Drawing.IDeviceContext.GetHdc
Dim xform As NativeMethods.XFORM
Dim clipRgn As IntPtr
Using transf As Matrix = Me.graphics.Transform
xform = New NativeMethods.XFORM(transf)
End Using
Using clip As Region = Me.graphics.Clip
clipRgn = clip.GetHrgn(Me.graphics)
End Using
Me.dc = Me.graphics.GetHdc()
Dim hDC As New HandleRef(Me, Me.dc)
Dim hRegion As New HandleRef(Nothing, clipRgn)
SetTransform(hDC, xform)
SetClip(hDC, hRegion)
// The below call creates a memory access violation.
NativeMethods.GdipSetTextRenderingHint(hDC, System.Drawing.Text.TextRenderingHint.AntiAliasGridFit)
Return Me.dc
End Function
Public Sub ReleaseHdc() Implements System.Drawing.IDeviceContext.ReleaseHdc
If Me.dc <> IntPtr.Zero Then
Me.graphics.ReleaseHdc()
Me.dc = IntPtr.Zero
End If
End Sub
Public Sub Dispose() Implements System.IDisposable.Dispose
ReleaseHdc()
End Sub
Private Shared Sub SetTransform(ByVal hdc As HandleRef, ByVal xform As NativeMethods.XFORM)
NativeMethods.SetGraphicsMode(hdc, NativeMethods.GM_ADVANCED)
NativeMethods.SetWorldTransform(hdc, xform)
End Sub
Private Shared Sub SetClip(ByVal hdc As HandleRef, ByVal hRegion As HandleRef)
NativeMethods.SelectClipRgn(hdc, hRegion)
End Sub
Private Class NativeMethods
Public Const GM_ADVANCED As Integer = 2
<DllImport("Gdi32")> _
Public Shared Function SetGraphicsMode(ByVal hdc As HandleRef, ByVal mode As Integer) As Integer
End Function
<DllImport("Gdi32")> _
Public Shared Function SetWorldTransform(ByVal hDC As HandleRef, ByVal xform As NativeMethods.XFORM) As Boolean
End Function
<DllImport("Gdi32")> _
Public Shared Function SelectClipRgn(ByVal hDC As HandleRef, ByVal hRgn As HandleRef) As Integer
End Function
<DllImport("gdiplus.dll", CharSet:=CharSet.Unicode, SetLastError:=True, ExactSpelling:=True)> _
Public Shared Function GdipSetTextRenderingHint(ByVal graphics As HandleRef, ByVal textRenderingHint As System.Drawing.Text.TextRenderingHint) As Integer
End Function
<StructLayout(LayoutKind.Sequential)> _
Public Class XFORM
Public eM11 As Single
Public eM12 As Single
Public eM21 As Single
Public eM22 As Single
Public eDx As Single
Public eDy As Single
Public Sub New()
Me.eM11 = 1.0!
Me.eM22 = 1.0!
End Sub
Public Sub New(ByVal transform As Matrix)
Me.eM11 = 1.0!
Me.eM22 = 1.0!
Me.eM11 = transform.Elements(0)
Me.eM12 = transform.Elements(1)
Me.eM21 = transform.Elements(2)
Me.eM22 = transform.Elements(3)
Me.eDx = transform.Elements(4)
Me.eDy = transform.Elements(5)
End Sub
End Class
End Class
End Class

Wow, that fits the "a little knowledge could be dangerous" mold. Not even the native C++ programmers call the gdiplus entry point directly, they use the C++ wrapper in <gdiplus.h>
The failure mode here is that your program is loading the wrong version of gdiplus.dll, the one in c:\windows\system32. The legacy version. The right one is in the Windows side-by-side cache, .NET's System.Drawing assembly contains code to make sure it gets the right version of the DLL from the cache.
Not the one you get. Yours isn't even initialized, GdiplusStartup was never called. Kaboom.
No clue what you're trying to accomplish. The Graphics class has a TextRenderingHint property, no need for the killer poke.

Related

Calling Form2 from Form1 in VB6 using instances problem

Actually I wanted to use cefsharp with my VB6 project and I am having following two problems:-
My project runs from let's say form1 and I need to call form2 where I have initialized the chromium browser using cefsharp by making use of C# class library.
Now when I call form2 from form1 like
form2.showme
(Where showme is just a custom function to set size etc) the chromium browser is able to get initialized without any issues, but when I call form2 like
Public ofrm2 AS form2
Public Sub function()
Set ofrm2=new form2
call ofrm2.showme
End Sub
then even though no error log or error of any kind is given and form2 is shown successfully, but there is no cefsharp chromium browser object on it this time.
I am also using SetParent and SetWindowPos to set up the length and width of my chromium browser in form2. I really am unable to find the issue, please suggest some fix.
The second problem is VB6 Editor does not allow debugging as soon as my code Initializes chromium. The only way to test the application for me was to make an exe and then run the application and try debugging using logs.
Edit:- ****************************************************************
My C# dll containing cefsharp packages
[PermissionSet(SecurityAction.Demand, Name ="FullTrust")]
[System.Runtime.InteropServices.ComVisibleAttribute(true)]
[ClassInterface(ClassInterfaceType.None)]
public partial class Form1 : Form, IForm1
{
private ChromiumWebBrowser _browser;
public Form1()
{
InitializeComponent();
//initializeChromium();
}
public int initializeChromium()
{
CefSettings settings = new CefSettings();
if (!Cef.IsInitialized)
{
CefSharp.Cef.Initialize(settings);
}
_browser = new ChromiumWebBrowser("http://127.0.0.1:24125/ABC/DEF.html");
_browser.RegisterJsObject("External", this);
this.Controls.Add(_browser);
_browser.BringToFront();
_browser.Dock = DockStyle.Fill;
this.Show();
return 0;
}
}
My Form1:-
Private Sub Form_Load()
Call launch ------ This one does not work. definition is given in my module below
'frmHTML.Show -----This one works
End Sub
Private Sub Form_UnLoad(cancel As Integer)
Set ofrmHtml = Nothing
End Sub
ModDec.bas:-
Option Explicit
Public Declare Function SetParent Lib "user32" (ByVal hWndChild As Long, ByVal hWndNewParent As Long) As Long
Public Declare Function SetWindowPos Lib "user32" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Public ofrmHtml As frmHTML
Public Sub launch()
Set ofrmHtml = New frmHTML
Call ofrmHtml.InitChromium1
ofrmHtml.ShowMe
'call ofrmHtml.ShowMe
End Sub
My form2(frmHTML):-
Option Explicit
Public cSharpObj As New cSharp.Form1
Private Sub Form_Load()
'Call InitChromium1
Exit Sub
End Sub
Public Sub InitChromium1()
' On Error GoTo Err_Hndlr
SetParent cSharpObj.getHandle, frmHTML.hWnd
SetWindowPos cSharpObj.getHandle, 0, 0, 0, 512, 480, 0
Dim i As Integer
i = -1
i = cSharpObj.initializeChromium()
Exit Sub
End Sub
Public Sub ShowMe()
Call Me.Show
End Sub

i want to get live stream from an ip cam.. and after that i want pictures from that live stream....in vb.net

Imports AForge.Video
Imports AForge.Video.VFW
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Try
Dim Stream As AForge.Video.MJPEGStream = New AForge.Video.MJPEGStream("http://192.168.1.250/mjpeg.cgi")
Stream.Login = ("admin")
Stream.Password = ("admin")
Stream.Start()
Dim st As String = ""
Catch ex As Exception
MsgBox(ex.Message.ToString(), MsgBoxStyle.OkOnly, "Error")
Exit Sub
End Try
End Sub
End Class
this is what i have done so far....to get stream started...
know i want know.
how can i show this stream in picture box? and how to get images from this stream.
The MJEGStream includes events. Declare your Stream object on class level as
Private WithEvents Stream As AForge.Video.MJPEGStream
Then add an event handler to the MJPEGStream.NewFrame event with a sub like
Private Sub Handle_NewFrame(sender as Object, e as AForge.Video.NewFrameEventArgs)
Picturebox1.Image = e.Frame
End Sub
Start the stream like you have done in your code.
Don't forget to initialize the Source object.
Source = New AForge.Video.MJPEGStream("http://192.168.1.250/mjpeg.cgi")
The eventargs variable contains the new frame as an image that you can then display in a picturebox.
It's been a while since I have worked with AForge, but it should be ok.

Convert C# to Vb Net, Use a 'RaiseEvent' statement to raise an event

i converted a c# class to vb net, but there are some events that couldnt find out these are the delegates declarations:
Public Delegate Sub IndentChangedEventHandler(NewValue As Integer)
Public Delegate Sub MultiIndentChangedEventHandler(LeftIndent As Integer, HangIndent As Integer)
Public Delegate Sub MarginChangedEventHandler(NewValue As Integer)
Public Delegate Sub TabChangedEventHandler(args As TabEventArgs)
Public Event LeftHangingIndentChanging As IndentChangedEventHandler
Public Event LeftIndentChanging As IndentChangedEventHandler
Public Event RightIndentChanging As IndentChangedEventHandler
Public Event BothLeftIndentsChanged As MultiIndentChangedEventHandler
Public Event LeftMarginChanging As MarginChangedEventHandler
Public Event RightMarginChanging As MarginChangedEventHandler
Public Event TabAdded As TabChangedEventHandler
Public Event TabRemoved As TabChangedEventHandler
Public Event TabChanged As TabChangedEventHandler
'this is the function converted on vb net
Private Sub AddTab(pos As Single)
Dim rect As New RectangleF(pos, 10.0F, 8.0F, 8.0F)
tabs.Add(rect)
If TabAdded IsNot Nothing Then
TabAdded.Invoke(CreateTabArgs(pos))
End If
End Sub
the sentence on c# was
if (TabAdded != null)
TabAdded.Invoke(CreateTabArgs(pos));
what should it be the correct way to call the delegate?
if (TabAdded != null)
TabAdded.Invoke(CreateTabArgs(pos));
Events have three accessors in vb.net: add, remove and raise. C# doesn't support the raise accessor so you have to explicitly test for null. That is not necessary in vb.net, and not allowed, just use the RaiseEvent statement directly without testing for Nothing:
Private Sub AddTab(ByVal pos As Single)
Dim rect As New RectangleF(pos, 10.0F, 8.0F, 8.0F)
tabs.Add(rect)
RaiseEvent TabAdded(CreateTabArgs(pos))
End Sub
I think you should use Address Of. Conversion of method to delegate is implicitly in C#. In VB.Net its explicitly and we use Address Of. Converters forget this thing.
For More read here
Strange error in code converted to VB.NET from C#
Method group in VB.NET?

Why is this .net UIAutomation app leaking/pooling?

I've got an app using .net UIAutomation, it eventually runs out of memory and crashes just monitoring windows being shown and closed. Seemed easier to show this in VB than C# but happens the same either way. It appears to be a leak/pool in the underlying proxy objects. Most of the memory is not being shown as in use as .net memory.
Any ideas on how to get this to stop leaking and still monitor StructureChangedEvents?
Imports System.Windows.Automation
Public Class Form1
Delegate Sub AddListCallback(ByVal Text As String)
Dim UIAeventHandler As StructureChangedEventHandler
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
BtnStartStop.Text = "Stop"
Subscribe()
End Sub
Private Sub BtnStartStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnStartStop.Click
If "Start" = BtnStartStop.Text Then
BtnStartStop.Text = "Stop"
Subscribe()
Else
BtnStartStop.Text = "Start"
Unsubscribe()
lbEvents.Items.Clear()
GC.GetTotalMemory(True)
End If
End Sub
Public Sub Subscribe()
UIAeventHandler = New StructureChangedEventHandler(AddressOf OnUIAutomationEvent)
Automation.AddStructureChangedEventHandler(AutomationElement.RootElement, TreeScope.Descendants, UIAeventHandler)
End Sub
Public Sub Unsubscribe()
Automation.RemoveStructureChangedEventHandler(AutomationElement.RootElement, UIAeventHandler)
End Sub
''' <summary>
''' AutomationEventHandler delegate.
''' </summary>
''' <param name="src">Object that raised the event.</param>
''' <param name="e">Event arguments.</param>
Private Sub OnUIAutomationEvent(ByVal src As Object, ByVal e As StructureChangedEventArgs)
' Make sure the element still exists. Elements such as tooltips can disappear
' before the event is processed.
If e.StructureChangeType = StructureChangeType.ChildrenInvalidated Then
Exit Sub
End If
Dim sourceElement As AutomationElement
Try
sourceElement = DirectCast(src, AutomationElement)
Catch ex As ElementNotAvailableException
Exit Sub
End Try
' TODO Handle any other events that have been subscribed to.
Console.WriteLine( "Element : """ & sourceElement.Current.LocalizedControlType & """ """ & sourceElement.Current.Name _
& """ Struct Change Type : " & [Enum].GetName(GetType(StructureChangeType), e.StructureChangeType) _
& " Runtime ID : " & getString(e.GetRuntimeId()))
End Sub
Private Function getString(ByVal ints As Integer()) As String
getString = ""
For Each i As Integer In ints
getString = getString & " " & i.ToString
Next
End Function
End Class
I would do something like this when you unsubscribe:
Automation.RemoveAllEventHandlers();
UIAeventHandler = null;
UIAutomation will keep some threads alive as long as you've got an AutomationEventHandler object around. Its pretty much a black box to me, but the above has fixed all issues that I had.
#jaws is correct (there are some additional things to think about), but it is in C#. for VB you need to do the following:
Automation.RemoveStructureChangedEventHandler(AutomationElement.RootElement, UIAeventHandler)
UIAeventHandler = Nothing
Because you do not set the UIAeventHandler to nothing, when you unsubscribe then, you are re-assigning the event handler which is already in memory.
To think about. Notice that you are calling subscribe when the form loads and subscribe/unsubscribe with a button. When the form loads then, the event handler is assigned and should work fine, that is until you hit unsubscribe/subscribe (in that order). When you did not set the UIAeventHandler to Nothing then, you are causing a memory leak because you then set the UIAeventHandler to a NEW instance on subscribe. Therefore, you could also do the following as suggested by #jaws :
Imports System.Windows.Automation
Public Class Form1
Dim UIAeventHandler as StructureChangedEventHandler
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
BtnStartStop.Text = "Stop"
Subscribe()
End Sub
Public Sub Subscribe()
UIAeventHandler = New StructureChangedEventHandler(AddressOf OnUIAutomationEvent)
Automation.AddStructureChangedEventHandler(AutomationElement.RootElement, TreeScope.Descendants, UIAeventHandler)
End Sub
Public Sub Unsubscribe()
Automation.RemoveStructureChangedEventHandler(AutomationElement.RootElement, UIAeventHandler)
UIAeventHandler = Nothing 'Here is the Important part!
End Sub
End Class

How do i set a callback for end of song/file with bass?

I am using Bass. I would like to set a callback so when the song reaches to end i can play another song directly after.
Its not C#, but here's some VB.Net code you should be able to convert easily enough:
Set up the callback
' Mixer handle to the bass synch callback when the current track in the mixer ends
Private m_MixerSynchProc As Un4seen.Bass.SYNCPROC
Private m_MixerSyncHandle As Int32 = 0
' Create a new callback for when the current track in the mixer has ended
m_MixerSynchProc = New Un4seen.Bass.SYNCPROC(AddressOf CurrentTrackEnded)
m_MixerSyncHandle = Bass.BASS_ChannelSetSync(m_MixerHandle, Un4seen.Bass.BASSSync.BASS_SYNC_END Or Un4seen.Bass.BASSSync.BASS_SYNC_MIXTIME, 0, m_MixerSynchProc, 0)
A delegate sub that BASS will call when the track is ended
' Mixer sync proc callback for when the current track has ended
Private Sub CurrentTrackEnded(ByVal MixerHandle As Int32, ByVal Channel As Int32, ByVal Data As Int32, ByVal User As IntPtr)
' Do stuff here when the track ends
End Sub

Categories

Resources