Hi John,

I don't have time to take a look at the code, but take a look at
System.Runtime.InteropServices.HandleRef and GC.KeepAlive and see if
you're having a premature GC problem.

Cheers,
Stoyan

On 7/7/06, John Whattam <[EMAIL PROTECTED]> wrote:
I have written a Combobox-based control for displaying all the raster and
truetype fonts available on the user's system. It makes extensive use of
the EnumFontFamiliesEx API for two purposes:
1.When InitialiseFontCombo is called in the dialog's load event,
EnumFontFamiliesEx is called to enumerate all fonts on the system and
determine which are raster and which are truetype.
2. When the user then selects a given font, I again call
EnumFontFamiliesEx to retrieve all valid sizes, styles and scripts for the
selected font name.
The actual calls to EnumFontFamiliesEx appear to work well, giving back
the expected information. To stress test this control, I ran a test
whereby I looped through all 58 fonts listed on my system a total of 5000
times so that EnumFontFamiliesEx was called 300,000 times and the
information collected by this call printed to the immediate window. This
worked without problems however during use of the dialog, I occasionally
get random errors – sometimes after 10 minutes of use and sometimes after
hours of use. The FatalExecutionError exception MDA is displayed telling
me I have a fatal error with code 0xC0000005 and the thread and therefore
the application is then aborted. This always occurs on the call to
EnumFontFamiliesEx for determining sizes, styles etc for the selected
font. I have the following code:

At the class level I have declared the API call itself and a delegate type
for the callbacks as follows:

       Private Declare Auto Function EnumFontFamiliesEx Lib "gdi32.dll"
(<InAttribute()> ByVal hdc As IntPtr, <InAttribute()> ByRef lpLogfont As
LOGFONT, <InAttribute()> ByVal lpEnumFontFamExProc As
EnumFontFamExProcDelegate, <InAttribute()> ByVal lParam As IntPtr, ByVal
dwFlags As UInteger) As Integer

       Private Delegate Function EnumFontFamExProcDelegate(<InAttribute()
> ByRef lpelfe As ENUMLOGFONTEXDV, <InAttribute()> ByRef lpntme As
ENUMTEXTMETRIC, <InAttribute()> ByVal FontType As FontTypes, <InAttribute()
> ByVal lParam As IntPtr) As Integer

At the class level there are two delegate instances pointing to the
appropriate functions to use for enumerating font names and font
information:
       Private mEnumFontInfoCallback As EnumFontFamExProcDelegate = New
EnumFontFamExProcDelegate(AddressOf EnumFontInfoCallback)
       Private mEnumFontNamesCallback As EnumFontFamExProcDelegate = New
EnumFontFamExProcDelegate(AddressOf EnumFontNamesCallback)

In the InitialiseFontCombo function I call to obtain the font names as
follows (note I have removed all indenting):
Private Sub GetAvailableNames()
Dim pGraphics As Graphics = Nothing
Dim phDC As IntPtr
Dim pResult As Integer = 0
Dim pLogFont As LOGFONT = New LOGFONT

Try
pGraphics = Graphics.FromHwnd(MyBase.Handle)
mVerticalResolution = pGraphics.DpiY
phDC = pGraphics.GetHdc
' The following is a custom collection which stores font name and
' font type – raster or truetype.
mFontInfo.Clear()

' Enumerate all fonts and all styles.
With pLogFont
.lfCharSet = CharSets.Default_CharSet
.lfFaceName = Nothing
.lfPitchAndFamily = 0
End With
' The callback function fills the collection with names and raster or
truetype.
pResult = EnumFontFamiliesEx(phDC, pLogFont, mEnumFontNamesCallback,
IntPtr.Zero, 0)

' Then there is code to fill combobox from the collection.

Finally
If Not phDC.Equals(IntPtr.Zero) Then
pGraphics.ReleaseHdc(phDC)
End If
If pGraphics IsNot Nothing Then
pGraphics.Dispose()
End If
End Try
End Sub

In the SelectedIndexChanged event I call to obtain the font information
for the selected font as follows:
Private Overloads Sub GetAvailableFontInfo(ByVal FontName As String)
Dim pLogFont As LOGFONT = New LOGFONT
Dim pGraphics As Graphics = Nothing
Dim phDC As IntPtr
Dim pResult As Integer = 0
Dim pRegular As Boolean = False
Dim pBold As Boolean = False
Dim pItalic As Boolean = False
Dim pBoldItalic As Boolean = False

Try
pGraphics = Graphics.FromHwnd(MyBase.Handle)
mVerticalResolution = pGraphics.DpiY
phDC = pGraphics.GetHdc

' Clear three class level arrays for holding iszes, styles and scripts.

With pLogFont
.lfCharSet = CharSets.Default_CharSet
.lfPitchAndFamily = 0
.lfFaceName = FontName
End With

' The callback fills arrays with all available font information including
font styles, font sizes and available scripts.
' ***************** THIS IS WHERE IT STOPS **************************
pResult = EnumFontFamiliesEx(phDC, pLogFont, mEnumFontInfoCallback,
IntPtr.Zero, 0)

Finally
If Not phDC.Equals(IntPtr.Zero) Then
pGraphics.ReleaseHdc(phDC)
End If
If pGraphics IsNot Nothing Then
pGraphics.Dispose()
End If
End Try
End Sub

For the life of me, I cannot figure out what is causing this error - I
feel it may be either a timing issue or the GC is collecting a portion of
the memory which the callback is using. Is there anything obviously wrong
with what I am doing? Can anyone give me some pointers on how to go about
solving this problem? Like I said, the error is random in nature and the
MDA doesn't provide much information.

Regards,
John Whattam.

===================================
This list is hosted by DevelopMentor®  http://www.develop.com

View archives and manage your subscription(s) at http://discuss.develop.com

Reply via email to