VB.NET Ionic.Zip as EmbeddedResource

Aug 3, 2009 at 1:58 AM

Been playing with this for hours, can't figure out what I'm missing for the life of me.

 

        Dim myNameSpace As String = Assembly.GetEntryAssembly.GetName().Name.ToString()
        Dim myAsm As Assembly
        myAsm = Assembly.GetExecutingAssembly
        Dim msIonicZip As System.IO.UnmanagedMemoryStream
        msIonicZip = myAsm.GetManifestResourceStream(myNameSpace & ".Ionic.Zip.dll")
        Dim bIonicZip() As Byte = New Byte(msIonicZip.Length) {}
        Dim testRead As Boolean = msIonicZip.Read(bIonicZip, 0, msIonicZip.Length)
        System.Reflection.Assembly.Load(bIonicZip)

I've tried going the ILMerge route, but I'm targeting the 3.5 Framework and ILMerge errors out.

Coordinator
Aug 3, 2009 at 4:45 AM
Edited Aug 3, 2009 at 7:00 AM

I don't know why ILMerge wouldn't work.  Are you aware that if you ilmerge, you lose the strong name on Ionic.Zip.dll?  In that case the references you compiled against may not be satisfied by the merged assembly.

You said that you're having problems.  What's the problem you're having? 

I just tried something very similar and it worked for me. 

        Dim resourceName As String = "Ionic.Zip.dll"
        
        Dim a1 As Assembly = Assembly.GetEntryAssembly()
'         Dim names As String()
'         names = a1.GetManifestResourceNames()
        
'         If names Is Nothing Then
'             Throw new Exception("GetManifestResourceNames returns Nothing.")
'         End If
        
'         For Each n As String in names
'             Console.WriteLine("rsrc: {0}", n)
'         Next

        Dim s As Stream = a1.GetManifestResourceStream(resourceName)
        Dim block As Byte() = New Byte(s.Length) {}
        s.Read(block, 0, block.Length)
        Dim a2 As Assembly = Assembly.Load(block)

        '' at this point you can call into the loaded assembly

        Dim o As System.Object
        o = a2.CreateInstance("Ionic.Zip.ZipFile", _
                    false, _
                    System.Reflection.BindingFlags.Public OR _
                    System.Reflection.BindingFlags.Instance OR _
                    System.Reflection.BindingFlags.InvokeMethod, _
                    Nothing, _
                    Nothing, _
                    Nothing, _
                    Nothing)

 

When I've had trouble loading resources, usually it is a problem with the resource name.   In the above code, I added the resource on the VB compiler command line with the /resource: option.  If you use Visual Studio, then the resource name is going to be different.   In that case, make sure you have it marked as "Embedded Resource"  in the VS project. 

You may want to verify the resource names by using code similar to that which is commented out above.

 

Aug 3, 2009 at 6:04 AM
Edited Aug 3, 2009 at 6:06 AM

The error:

 

************** Exception Text **************
System.IO.FileNotFoundException: Could not load file or assembly 'Ionic.Zip, Version=1.7.2.12, Culture=neutral, PublicKeyToken=edbe51ad942a3f5c' or one of its dependencies. The system cannot find the file specified.
File name: 'Ionic.Zip, Version=1.7.2.12, Culture=neutral, PublicKeyToken=edbe51ad942a3f5c'

 

Using the commented code, I did verify it's entire name is "GearMod4.Ionic.Zip.dll".

Am I not supposed to Imports Ionic.Zip when using embedded resourced?

The code and calling methods are:

 

    Private Sub winMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim resourceName As String = "Ionic.Zip.dll"
        Dim myNameSpace As String = Assembly.GetEntryAssembly.GetName().Name.ToString()

        Dim a1 As Assembly = Assembly.GetEntryAssembly()
        'Dim names As String()
        'names = a1.GetManifestResourceNames()

        'If names Is Nothing Then
        '    Throw New Exception("GetManifestResourceNames returns Nothing.")
        'End If

        'For Each n As String In names
        '    'Console.WriteLine("rsrc: {0}", n)
        '    MsgBox("Item: " & n)
        'Next


        Dim s As UnmanagedMemoryStream = a1.GetManifestResourceStream("GearMod4." & resourceName)
        'MsgBox(s.Length.ToString())
        Dim block As Byte() = New Byte(s.Length) {}
        s.Read(block, 0, block.Length)
        Dim a2 As Assembly = Assembly.Load(block)

        '' at this point you can call into the loaded assembly

        Dim o As System.Object
        o = a2.CreateInstance("Ionic.Zip.ZipFile", _
                    False, _
                    System.Reflection.BindingFlags.Public Or _
                    System.Reflection.BindingFlags.Instance Or _
                    System.Reflection.BindingFlags.InvokeMethod, _
                    Nothing, _
                    Nothing, _
                    Nothing, _
                    Nothing)

 

And the calling methods:

 

        Try
            Using zip As Ionic.Zip.ZipFile = ZipFile.Read("Themes\" & objIniFile.GetString("Default", "Theme", "DangerGirl") & ".G4T")
                Dim ms As New System.IO.MemoryStream
                zip.Password = themeTicket

 

Lastly, I'm using an UnmanagedMemoryStream because MemoryStream errs out:

Unable to cast object of type 'System.IO.UnmanagedMemoryStream' to type 'System.IO.MemoryStream'.

Coordinator
Aug 3, 2009 at 6:54 AM
Edited Aug 3, 2009 at 6:55 AM

The use of UnmanagedMemoryStream is irrelevant.  It seems to me the only thing you do is a single Read() on the stream.  You can just as well use a plain vanilla Stream type.   Using an UnmanagedMemoryStream, if that is the actual type returned by GetManifestResourceStream(), is also fine.

The Imports is required to satisfy symbols at compile time.  The imports statement is not relevant at runtime.  

I'm still trying to figure out what you're doing and the problem you're having.  My suspiscion at this point is that the problem has nothing to do with DotNetZip, but instead has to do with your use of assemblies and how you load them.

If you load an assembly at runtime, regardless whether it is DotNetZip or some other assembly, you have a couple options:

  • write an AssemblyResolve event
  • use Reflection to call into the loaded assembly

Your initial code, and the example I provided, is an example of the latter.  Your subsequent code appears to require the former. 

In other words, if you use code like

Using zip as Ionic.Zip.ZipFile = ZipFile.Read(...)
    '' ...
End Using

...then you need to have an AssemblyResolve event.  If you are using reflection, then your app code will construct a ZipFile like this:

        Dim o As System.Object
        o = a2.CreateInstance("Ionic.Zip.ZipFile", _
                    false, _
                    System.Reflection.BindingFlags.Public OR _
                    System.Reflection.BindingFlags.Instance OR _
                    System.Reflection.BindingFlags.InvokeMethod, _
                    Nothing, _
                    Nothing, _
                    Nothing, _
                    Nothing)

 

Aug 3, 2009 at 7:00 AM
Edited Aug 3, 2009 at 7:11 AM

Using the Reflection method, how do I actually pass calls to the object?

I think that's the problem I'm having, because I'm Using zip As Ionic.Zip.ZipFile, rather than the method you showed.

Reflection's insanely new to me, so I'm not sure how I go from o = a2.CreateInstance() to actually using 'o'.

EDIT:-

Nevermind, scratch that last part, added a one liner just to check vs. my existing code and this works perfectly.

Dim zip As Object = o.Read("Themes\" & objIniFile.GetString("Default", "Theme", "Default") & ".G4T")
zip.Password("SomethingHere")
Thank you Cheeso!

 

 

Coordinator
Aug 3, 2009 at 7:15 AM

yes, Reflection is much harder and usually only makes sense when you are not sure of the version of the assembly you will load at runtime. In other words, if your app runs from a directory and there could be one of any number of assemblies avaialble to the application and you don't know the version until you load it.  In your case, you seem to be relying on an embedded assembbly, which would recommend the AssemblyResolve evvent. 

The AssemblyResolve event is not difficult.  It allows you to keep the application code, outside of the assembly resolution logic, very simple.  For example,

Dim zip as ZipFile
zip = New Zipfile
AddHandler zip.AddProgress, AddressOf AddProgress
zip.StatusMessageTextWriter = Console.Out
zip.AddDirectory(directoryToZip)
AddHandler zip.SaveProgress, AddressOf SaveProgress
zip.BufferSize = &H80000
zip.UseZip64WhenSaving = Zip64Option.Always
zip.Save(zipPath)
zip.Dispose

In contrast, if you use reflection, your app code to use a zipfile will look like this:

Private Sub CreateZipFirstTime()
        Dim o As Object = a2.CreateInstance("Ionic.Zip.ZipFile", False, (BindingFlags.InvokeMethod Or (BindingFlags.Public Or BindingFlags.Instance)), Nothing, Nothing, Nothing, Nothing)
        Dim t As Type = o.GetType
        If _version.VersionGreaterThanOrEqualTo("1.8.3.29") Then
            ' AddProgress event
            Dim eInfo As EventInfo = t.GetEvent("AddProgress")
            Dim addProgressMethodInfo As MethodInfo = .AddEventHandlerAssembly.GetType("DynamicallyCompiled.AddEventHandler").GetMethod("AddProgress", (BindingFlags.Public Or BindingFlags.Static))
            Dim d As Delegate = Delegate.CreateDelegate(eInfo.EventHandlerType, addProgressMethodInfo)
            eInfo.AddEventHandler(o, d)
        Else
            t.InvokeMember("StatusMessageTextWriter", (BindingFlags.SetProperty Or (BindingFlags.Public Or BindingFlags.Instance)), Nothing, o, New Object() { Console.Out })
        End If
        Dim e As Object = t.InvokeMember("AddDirectory", (BindingFlags.InvokeMethod Or (BindingFlags.Public Or BindingFlags.Instance)), Nothing, o, New Object() { _directoryToZip, "" })
        If _version.VersionGreaterThanOrEqualTo("1.7.0.0") Then
            ' set up a SaveProgress event
            Dim eInfo2 As EventInfo = t.GetEvent("SaveProgress")
            Dim saveProgressMethodInfo As MethodInfo = SaveEventHandlerAssembly.GetType("DynamicallyCompiled.SaveEventHandler").GetMethod("SaveProgress", (BindingFlags.Public Or BindingFlags.Static))
            Dim d2 As Delegate = Delegate.CreateDelegate(eInfo2.EventHandlerType, saveProgressMethodInfo)
            eInfo2.AddEventHandler(o, d2)
        End If
        If _version.VersionGreaterThanOrEqualTo("1.8.0.0") Then
            ' set the BufferSize property
            t.InvokeMember("BufferSize", (BindingFlags.SetProperty Or (BindingFlags.Public Or BindingFlags.Instance)), Nothing, o, New Object() { &H80000 })
        End If
        If _version.VersionGreaterThanOrEqualTo("1.7.0.0") Then
            Dim zip64OptionType As Type = LateBoundZipCreator.LateBoundAssembly.GetType("Ionic.Zip.Zip64Option")
            t.InvokeMember("UseZip64WhenSaving", (BindingFlags.SetProperty Or (BindingFlags.Public Or BindingFlags.Instance)), Nothing, o, New Object() { Enum.Parse(zip64OptionType, "Always") })
        End If
        t.InvokeMember("Save", (BindingFlags.InvokeMethod Or (BindingFlags.Public Or BindingFlags.Instance)), Nothing, o, New Object() { _zipPath })
        t.InvokeMember("Dispose", (BindingFlags.InvokeMethod Or (BindingFlags.Public Or BindingFlags.Instance)), Nothing, o, Nothing)
End Sub

 

 

Aug 3, 2009 at 7:27 AM

I don't see why it would have to look so intricate when I'm only reading from the zip and writing to a MemoryStream.

My application is working perfectly without the DLL, using only the Resource.

 

            zip = zip.Read("Themes\" & objIniFile.GetString("Default", "Theme", "ThemeName") & ".G4T")
            Dim ms As New System.IO.MemoryStream
            zip.Password = themeTicket
            zip.Extract("Authenticate", ms)
            If Not AuthenticateTheme(ms) Then suicide()
            ms.Dispose()

            ms = New System.IO.MemoryStream
            zip.Extract("BackGround.png", ms)
            Dim bmpBack As Bitmap = Bitmap.FromStream(ms)
            Dim tWidth As Integer = bmpBack.Width
            Dim tHeight As Integer = bmpBack.Height
            Me.Height = tHeight
            Me.Width = tWidth
            Me.BackgroundImage = bmpBack
            Me.Update()
'many more extractions, near duplicate code as above.

Coordinator
Aug 3, 2009 at 12:10 PM

ok, well if it's working for you . .. ..

Aug 3, 2009 at 12:27 PM

Ran into another doozie, if you've got anything for this one, I'd really appreciate it.  Google is turning up nothing lol

 

    Private Declare Function BASSMOD_Init Lib "bassmod.dll" (ByVal device As Long, ByVal freq As Long, ByVal flags As Long) As Integer
    Private Declare Function BASSMOD_MusicLoad Lib "bassmod.dll" (ByVal mem As Integer, ByVal pfile As String, ByVal offset As Long, ByVal Length As Long, _
            ByVal flags As Long) As Integer
    Private Declare Function BASSMOD_MusicPlay Lib "bassmod.dll" () As Integer
    Private Declare Function BASSMOD_MusicStop Lib "bassmod.dll" () As Integer
    Private Declare Sub BASSMOD_Free Lib "bassmod.dll" ()
Trying to get BASSMOD working, was hoping to (after I get it playing sound at all) embed this dll as well.

Coordinator
Aug 3, 2009 at 12:28 PM

No, I can't help you with that.