Question How do you delete a temporary font file that you created?

Germcloud

Member
Joined
Jun 6, 2010
Messages
9
Programming Experience
1-3
My program uses embed fonts, I had trouble getting it to work at first, but one way I was able to get it to work was to save the font from My.Resources.FontName into a temporary file and use the file to load a font into the program. But the problem I have is that when I load the program again and I change the fonts around, it loads the same files that were created last when the program was last run, making it were I can not change the fonts to something else.

I've tried deleting the temp files in my class in Sub Finalize(), but it keeps telling me access is denied. What do I need to do to delete these files when my program closes.


Public Class clsFont
Private pfc As New System.Drawing.Text.PrivateFontCollection
Private intIndex As Integer
Private intTotal As Integer = -1
Private intFontSize() As Integer
Private fsStyle() As Drawing.FontStyle

Public Function GetFont(ByVal FontIndex As Integer) As Font

Return New Font(pfc.Families(FontIndex), intFontSize(FontIndex), fsStyle(FontIndex))

End Function


Public Function AddFont(ByVal FontResource As Byte(), ByVal FontSize As Integer, ByVal Style As Drawing.FontStyle)


intTotal = intTotal + 1

Using fs As New System.IO.FileStream("TempFile" & intTotal.ToString, IO.FileMode.Create)
fs.Write(FontResource, 0, FontResource.Length)
End Using

ResizeArrays()

intFontSize(intTotal) = FontSize
fsStyle(intTotal) = Style

'Use the resulting File as a font
pfc.AddFontFile("TempFile" & intTotal.ToString)



Return intIndex


End Function

Private Sub ResizeArrays()

ReDim Preserve intFontSize(intTotal)
ReDim Preserve fsStyle(intTotal)

End Sub

Protected Overrides Sub Finalize()

For i As Integer = 0 To intTotal
System.IO.File.Delete("TempFile" & i.ToString)
Next

MyBase.Finalize()

End Sub
End Class

Public Class Form1

Dim MyFonts As New clsFont

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load


MyFonts.AddFont(My.Resources.Appetite, 22, FontStyle.Regular)
MyFonts.AddFont(My.Resources.BENIGHTE, 22, FontStyle.Regular)
MyFonts.AddFont(My.Resources.atlandsketchesbb_reg, 22, FontStyle.Regular)


Label1.Font = MyFonts.GetFont(0)
Label2.Font = MyFonts.GetFont(1)

End Sub

End Class
 
Most likely the FontFamily (or PrivateFontCollection) need to be disposed before the file can be removed. Disposing the PrivateFontCollection is something you have to do anyway. But you don't need to use a temporary file, the font can be loaded from memory, here's an article about it How to embed a True Type font and here's a more relevant code sample:
VB.NET:
Dim fontdata() As Byte = My.Resources.Abduction
Dim data As System.IntPtr = Marshal.AllocCoTaskMem(fontdata.Length)
Marshal.Copy(fontdata, 0, data, fontdata.Length)
pfc.AddMemoryFont(data, fontdata.Length)
Marshal.FreeCoTaskMem(data)
Remember pfc.Dispose(), you can do this for example from Disposed event of form.
 
Thanks for your quick reply! I had come across that article before, it worked, but I wanted to put it as part of a class to make it easier to load the fonts, but the problem I kept coming across, was that for some reason, some fonts would display and some would not.

I came across this method to use a temporary file instead, and it worked perfectly, only I can't delete the files.

Though, when you break it down for me with your code sample it seems to make a little more since. I'll give it another shot, but I'd still like to figure out how to delete these files.

I have pfc used within a class, so I tried creating a MyClass.Dispose() Method that will call pfc.Dispose within the class, and then delete the files. I make this call in Form1_Disposed, however, when I do this, I don't get an error or anything, the program closes, but the files are still there, they still will not delete!

I tried stepping through the program, and once it gets to System.IO.File.Delete the program closes, it doesn't look like it executes as it doesn't go to the next line of code.

What else could be causing this problem?
 
but the problem I kept coming across, was that for some reason, some fonts would display and some would not.

I came across this method to use a temporary file instead, and it worked perfectly, only I can't delete the files.
Sounds strange there would be a difference in functionality.

I have pfc used within a class, so I tried creating a MyClass.Dispose() Method that will call pfc.Dispose within the class, and then delete the files. I make this call in Form1_Disposed, however, when I do this, I don't get an error or anything, the program closes, but the files are still there, they still will not delete!
Didn't notice that at first, you should implement IDisposable in that class, line after "class clsFont" write "implements IDisposable" and press enter and all is generated. Where is says "TODO: dispose managed state (managed objects)" you do cleanup. You will also see that "override Finalize() only if Dispose has code to free unmanaged resources", that means you should move the file deletes to same place (managed cleanup) and don't override Finalize method (read about why in help). Try-Catch the File.Delete calls.
I tried stepping through the program, and once it gets to System.IO.File.Delete the program closes, it doesn't look like it executes as it doesn't go to the next line of code.
Finalizers may be called as a secondary step in garbage collection and may happen at a later stage. Garbage Collection

If you do want to write the font bytes to file you can use File.WriteAllBytes instead of the FileStream.

About the file deletes, I gave it a test round and it proved more difficult than I though, after disposing the PrivateFontCollection you actually need to force garbage collection and wait for finalizers to complete before you can delete the files, like below:
VB.NET:
pcf.Dispose()
GC.Collect()
GC.WaitForPendingFinalizers()
' now you can delete the files
Also disposing each FontFamiliy made no difference, mind you, help indicates that is not necessary when disposing the PrivateFontCollection ("this method releases all resources held by any managed objects that this FontCollection references."), so the GC calls was the only solution I found to free the files in time.
 
Wow, thank you so much for your help!!!

All of this is a little above my head, but I tried to do what you said, I'm thinking I missed something, here is what I have.

From Form1:
VB.NET:
Public Class Form1
    Dim MyFonts As New clsFont

    Private Sub Form1_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed

        MyFonts.Dispose()

    End Sub





    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load


        MyFonts.AddFont(My.Resources.Appetite, 22, FontStyle.Regular)
        MyFonts.AddFont(My.Resources.BENIGHTE, 22, FontStyle.Regular)


        Label1.Font = MyFonts.GetFont(0)
        Label2.Font = MyFonts.GetFont(1)

    End Sub

 
End Class

From clsFont

VB.NET:
    Implements IDisposable


    Private disposed As Boolean = False

    Private pfc As New System.Drawing.Text.PrivateFontCollection
    Private intIndex As Integer
    Private intTotal As Integer = -1
    Private intFontSize() As Integer
    Private fsStyle() As Drawing.FontStyle

    Public Function GetFont(ByVal FontIndex As Integer) As Font

        Return New Font(pfc.Families(FontIndex), intFontSize(FontIndex), fsStyle(FontIndex))

    End Function


    Public Function AddFont(ByVal FontResource As Byte(), ByVal FontSize As Integer, ByVal Style As Drawing.FontStyle)


        intTotal = intTotal + 1

        System.IO.File.WriteAllBytes("TempFile" & intTotal.ToString, FontResource)

        'Using fs As New System.IO.FileStream("TempFile" & intTotal.ToString, IO.FileMode.Create)
        ' fs.Write(FontResource, 0, FontResource.Length)
        ' End Using

        ResizeArrays()

        intFontSize(intTotal) = FontSize
        fsStyle(intTotal) = Style

        'Use the resulting File as a font
        pfc.AddFontFile("TempFile" & intTotal.ToString)



        Return intIndex


    End Function

    Private Sub ResizeArrays()

        ReDim Preserve intFontSize(intTotal)
        ReDim Preserve fsStyle(intTotal)

    End Sub



    Private disposedValue As Boolean = False        ' To detect redundant calls

    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                pfc.Dispose()
                GC.Collect()
                GC.WaitForPendingFinalizers()
                ' now you can delete the files

                Try
                    For i As Integer = 0 To intTotal
                        System.IO.File.Delete("TempFile" & i.ToString)
                    Next i
                Catch ex As Exception
                    MsgBox(ex.Message)
                End Try

            End If

            ' TODO: free your own state (unmanaged objects).
            ' TODO: set large fields to null.
        End If
        Me.disposedValue = True
    End Sub

#Region " IDisposable Support "
    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region

The files are still not deleting. The try catch doesn't display the MsgBox I put in either, but when I step through it, it says: "Access to path is denied."

I thought I would try at this point to delete it in explorer when it was at this point in the program, I get a message saying that the file is in use by another program, and this is after I had called:

VB.NET:
pcf.Dispose()
GC.Collect()
GC.WaitForPendingFinalizers()
 
Last edited by a moderator:
Instead of
VB.NET:
Try
   For
      File.Delete
do
VB.NET:
For
   Try
      File.Delete
Instead of trying the whole loop block try delete each file, any single failure will not affect the other file.delete attempts. This will not solve the problem, but it is a good programming practise ;)

About your file problem I didn't actually load a Font object from any of the private families, so when trying that now I also get 'access denied'. I'm out of ideas and I think you're stuck with the temp files. You can delete them from Windows Explorer after the application has exited. By the way, to save you the trouble, I did also try disposing the used fonts first, and doing the file deletes from both applications ShutDown event and the Application.ApplicationExit event, none the wiser. I'd prefer not creating those files in the first place.

As for arrays, you should look into collections, they make any common array operations much easier and more efficient.
 
Thank you so much for your time and effort in helping me! I have learned a lot of things just from this post alone! Take Care.
 
I think I have finally found my solution. After reviewing this post and some long hours of searching the web, I found that it was something really simple.

Before, when I had just tried to load the resource font into memory, before the temporary file version, I realized that if the font was already installed in windows, it would work, but if it wasn't, it wouldn't.

The reason, which I'm not sure why exactly, is because the "UseCompatibleTextRendering" property for the Label, has to be set to "True" and it loads and displays the embedded font, but set to "False", it uses the default font for the Label.

Here is what I'm using now:

Module: modMain
VB.NET:
Imports System.Runtime.InteropServices
Imports System.Drawing.Text

Module modMain

    Public Function GetFont(ByVal FontResource() As Byte, ByVal Size As Integer, ByVal Style As FontStyle) As Font

        Dim fontdata() As Byte = FontResource
        Dim data As System.IntPtr = Marshal.AllocCoTaskMem(fontdata.Length)
        Dim pfc As New PrivateFontCollection

        fontdata = FontResource
        data = Marshal.AllocCoTaskMem(fontdata.Length)

        Marshal.Copy(fontdata, 0, data, fontdata.Length)

        pfc.AddMemoryFont(data, fontdata.Length)

        Marshal.FreeCoTaskMem(data)

        Return New Font(pfc.Families(0), Size, Style)

    End Function
End Module

Form: Form1
VB.NET:
Public Class Form1


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Label1.Font = GetFont(My.Resources.aescrawl, 22, FontStyle.Regular)
        Button1.Font = GetFont(My.Resources.Appetite, 33, FontStyle.Bold)

    End Sub


End Class

It works great! The only problem I have now, is when I drag the sides to resize the form, the label control and button control turns into red X's. But I've got what I need, my application will not give the user that option, so hopefully it won't be a problem.

It's intriguing to me, however, that the temporary file version method I came up with, didn't require you to set the "UseCompatibleTextRendering" to True. Not sure why that is, but I suppose it will remain a mystery.
 
Dim pfc As New PrivateFontCollection
You still need to dispose this! But you actually need to preserve the private font (families) for as long as the application is using them, otherwise you get this:
error said:
System.AccessViolationException was unhandled
Message="Attempted to read or write protected memory. This is often an indication that other memory has been corrupted."
Source="System.Drawing"
Not disposing the local pcf in your GetFont method only causes the reference to go out of scope, and be collected eventually, some time later. But you do need to keep this at module (class) level.
Dim fontdata() As Byte = FontResource
Dim data As System.IntPtr = Marshal.AllocCoTaskMem(fontdata.Length)


fontdata = FontResource
data = Marshal.AllocCoTaskMem(fontdata.Length)
Remove the duplicate calls (last two lines). The first one is no problem, it's just redundant, the second is a memory leak.

however, that the temporary file version method I came up with, didn't require you to set the "UseCompatibleTextRendering" to True. Not sure why that is, but I suppose it will remain a mystery.
Yes, it's strange there would be a difference in functionality between these two approaches. I did notice that too, but the font was ok in Gdi drawing and I went on to test the loading from files, so it wasn't followed up. UseCompatibleTextRendering was a good tip, I would never have guessed, though help for AddMemoryFont explains this:
help said:
To use the memory font, text on a control must be rendered with GDI+. Use the SetCompatibleTextRenderingDefault method, passing true, to set GDI+ rendering on the application, or on individual controls by setting the control's UseCompatibleTextRendering property to true. Some controls cannot be rendered with GDI+.
only problem I have now, is when I drag the sides to resize the form, the label control and button control turns into red X's
I was not able to reproduce this problem, perhaps you're using Windows XP? (it paints differently)
 
Back
Top