Eli, oon yrittänyt tässä jo jonkun aikaa lisätä uuden resurssin ohjelmallisesti, käyttäen Visual Basic.NETiä. Eli; oon nyt saanut aikaan sen että pakkaan/kompressoin kuvan(bmp), ja nyt yritän lisätä sitä PE-tiedoston resursseihin, ja jäin jumiin virheeseen:
lainaus:
Value of type 'System.Drawing.Bitmap' cannot be converted to '1-dimensional array of System.Drawing.Bitmap'
Ja tässä pseudokoodi:
Module1:
Imports System.Runtime.InteropServices Module ResourceWriter Private Function ToPtr(ByVal data As Object) As IntPtr Dim h As GCHandle = GCHandle.Alloc(data, GCHandleType.Pinned) Dim ptr As IntPtr Try ptr = h.AddrOfPinnedObject() Finally h.Free() End Try Return ptr End Function <DllImport("kernel32.dll", SetLastError:=True)> _ Private Function UpdateResource(ByVal hUpdate As IntPtr, ByVal lpType As String, ByVal lpName As String, ByVal wLanguage As UShort, ByVal lpData As IntPtr, ByVal cbData As UInteger) As Boolean End Function <DllImport("kernel32.dll", SetLastError:=True)> _ Private Function BeginUpdateResource(ByVal pFileName As String, <MarshalAs(UnmanagedType.Bool)> ByVal bDeleteExistingResources As Boolean) As IntPtr End Function <DllImport("kernel32.dll", SetLastError:=True)> _ Private Function EndUpdateResource(ByVal hUpdate As IntPtr, ByVal fDiscard As Boolean) As Boolean End Function Public Function WriteResource(ByVal filename As String, ByVal bmp As Bitmap()) As Boolean Try Dim handle As IntPtr = BeginUpdateResource(filename, False) Dim file1 As Bitmap() = bmp Dim fileptr As IntPtr = ToPtr(file1) Dim res As Boolean = UpdateResource(handle, "BitMaps", "0", 0, fileptr, Convert.ToUInt32(file1.Length)) EndUpdateResource(handle, False) Catch ex As Exception Return False End Try Return True End Function End Module
Formi, buttonin alla:
'... Dim bmp1 As Bitmap = Compressed WriteResource("C:\Users\Admin\Desktop\file.exe", bmp1)
Eikä luonnistu. Tajusin jo siis sen etten voi sitä noin sinne resursseihin laittaa, enkä saanut sitä käännettyä byte arrayksi taikka memorystreamiksi niin että se jopa toimisi. Tässä vielä yksi koodinpätkä mitä yritin, saamatta mitään kuitenkaan aikaan:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click '... Moduuli vaihdetty tähän sopivaksi, eli "As Bitmap()" -> As Byte() Dim bmp1 As Bitmap = Compressed Dim Converted = ConvertToByteArray(bmp1) WriteResource("C:\Users\Admin\Desktop\file.exe", Converted) End Sub Public Shared Function ConvertToByteArray(ByVal value As Bitmap) As Byte() Dim bitmapBytes As Byte() Using stream As New System.IO.MemoryStream value.Save(stream, value.RawFormat) bitmapBytes = stream.ToArray End Using Return bitmapBytes End Function
...Ja alkaa ideat loppua. Miten saan laitettua kuvan toisen ohjelman resursseihin, ilman sen pakatun kuvan tallentamista kovalevylle? En halua käyttää 'hallittuja' resursseja(managed resource).
Ideoita, vinkkejä, koodia... jotain ?
Rox kirjoitti:
Eikä luonnistu. Tajusin jo siis sen etten voi sitä noin sinne resursseihin laittaa, enkä saanut sitä käännettyä byte arrayksi taikka memorystreamiksi niin että se jopa toimisi. Tässä vielä yksi koodinpätkä mitä yritin, saamatta mitään kuitenkaan aikaan:
Tallenna kuva streamiksi, konvertoi byte arrayksi ja kirjoittele sieltä?
Alla esimerkkinä vanha Component Pascalilla kirjoiteltu koodin pätkä, jota käytin joskus kuvatiedostoista koostettujen animoitujen GIF-tiedostojen kirjoitteluun Fortran ohjelmasta.
MODULE AnimGif; IMPORT Sys := "[mscorlib]System", IO := "[mscorlib]System.IO", Cpm := "[system]System.ComponentModel", Wfm := "[system.windows.forms]System.Windows.Forms", Drw := "[system.drawing]System.Drawing", Im := "[system.drawing]System.Drawing.Imaging", RTS; TYPE AnimatedGif* = POINTER TO RECORD files*: POINTER TO ARRAY OF Sys.String; delay*: INTEGER (* delay in 1/100 s *) END; PROCEDURE ValidateImagefile*(IN filepath: Sys.String): BOOLEAN; VAR NativeType: RTS.NativeType; NativeTypeName: Sys.String; image: Drw.Image; BEGIN image := Drw.Bitmap.FromFile(filepath); RETURN TRUE; RESCUE (exc) NativeType := TYPEOF(exc); NativeTypeName := MKSTR(RTS.TypeName(NativeType)^); IF NativeTypeName = "System.OutOfMemoryException" THEN RETURN FALSE ELSE THROW (exc) END END ValidateImagefile; PROCEDURE (gif: AnimatedGif) ScanImages*(IN dirpath: Sys.String; IN filter: Sys.String), NEW; VAR NativeType: RTS.NativeType; NativeTypeName: Sys.String; rslt: Wfm.DialogResult; PROCEDURE sort(VAR a: ARRAY OF Sys.String); VAR i, j: INTEGER; s: Sys.String; BEGIN FOR i := 1 TO LEN(a)-1 DO s := a[i]; j := i; WHILE (j > 0) & (a[j-1] > s) DO a[j] := a[j-1]; DEC(j) END; a[j] := s END END sort; BEGIN gif.files := IO.Directory.GetFiles(dirpath, filter); IF LEN(gif.files) > 0 THEN sort(gif.files) END; RESCUE (exc) NativeType := TYPEOF(exc); NativeTypeName := MKSTR(RTS.TypeName(NativeType)^); IF NativeTypeName = "System.IO.IOException" THEN rslt := Wfm.MessageBox.Show("Path: " + dirpath + " is a filename.", "Error", Wfm.MessageBoxButtons.OK, Wfm.MessageBoxIcon.Error) ELSIF NativeTypeName = "System.IO.DirectoryNotFoundException" THEN rslt := Wfm.MessageBox.Show("Path: " + dirpath + " not found.", "Error", Wfm.MessageBoxButtons.OK, Wfm.MessageBoxIcon.Error) ELSE THROW (exc) END END ScanImages; PROCEDURE (gif: AnimatedGif) Output*(IN target: Sys.String), NEW; VAR NativeType: RTS.NativeType; NativeTypeName: Sys.String; rslt: Wfm.DialogResult; i: INTEGER; memStream: IO.MemoryStream; binWriter: IO.BinaryWriter; image: Drw.Image; wstream: IO.FileStream; buf1: POINTER TO ARRAY OF UBYTE; buf2: POINTER TO ARRAY OF UBYTE; buf3: POINTER TO ARRAY OF UBYTE; BEGIN IF LEN(gif.files) = 0 THEN RETURN ELSE FOR i := 0 TO LEN(gif.files)-1 DO IF ~IO.File.Exists(gif.files[i]) THEN rslt := Wfm.MessageBox.Show("File: " + gif.files[i] + " not found.", "Error", Wfm.MessageBoxButtons.OK, Wfm.MessageBoxIcon.Error); RETURN END; IF ~ValidateImagefile(gif.files[i]) THEN rslt := Wfm.MessageBox.Show("File: " + gif.files[i] + " is not a valid image.", "Error", Wfm.MessageBoxButtons.OK, Wfm.MessageBoxIcon.Error); RETURN END END; IF IO.File.Exists(target) THEN rslt := Wfm.MessageBox.Show("File: " + target + " already exists, overwrite?", "File already exists", Wfm.MessageBoxButtons.YesNo, Wfm.MessageBoxIcon.Question); IF rslt = Wfm.DialogResult.No THEN RETURN ELSE IO.File.Delete(target) END END END; NEW(memStream); NEW(buf2,19); NEW(buf3,8); buf2[0] := 33; (*extension introducer*) buf2[1] := 255; (*application extension*) buf2[2] := 11; (*size of block*) buf2[3] := 78; (*N*) buf2[4] := 69; (*E*) buf2[5] := 84; (*T*) buf2[6] := 83; (*S*) buf2[7] := 67; (*C*) buf2[8] := 65; (*A*) buf2[9] := 80; (*P*) buf2[10] := 69; (*E*) buf2[11] := 50; (*2*) buf2[12] := 46; (*.*) buf2[13] := 48; (*0*) buf2[14] := 3; (*size of block*) buf2[15] := 1; (**) buf2[16] := 0; (**) buf2[17] := 0; (**) buf2[18] := 0; (*Block terminator*) buf3[0] := 33; (*extension introducer*) buf3[1] := 249; (*Graphic control extension*) buf3[2] := 4; (*size of block*) buf3[3] := 8; (*no transparency*) buf3[4] := USHORT(ORD(BITS(gif.delay)*BITS(0FFH))); (*Delay time low byte*) buf3[5] := USHORT(ASH(ORD(BITS(gif.delay)*BITS(0FF00H)),-16)); (*Delay time high byte*) buf3[6] := 0; (*no transparency*) buf3[7] := 0; (*Block terminator*) wstream := IO.FileStream.init(target, IO.FileMode.Create); binWriter := IO.BinaryWriter.init(wstream); FOR i:=0 TO (LEN(gif.files)-1) BY 1 DO image := Drw.Bitmap.FromFile(gif.files[i]); image.Save(memStream, Im.ImageFormat.get_Gif()); buf1 := memStream.ToArray(); IF i = 0 THEN binWriter.Write(buf1,0,781); (*Header & global color table*) binWriter.Write(buf2, 0, 19) (*Application extension*) END; binWriter.Write(buf3, 0, 8); (*Graphic extension*) binWriter.Write(buf1, 789, LEN(buf1) - 790); (*Image data*) IF i = (LEN(gif.files)-1) THEN binWriter.Write(";") (*Image terminator*) END; memStream.SetLength(0); END; binWriter.Close(); rslt := Wfm.MessageBox.Show("Wrote " + Sys.Convert.ToString(LEN(gif.files)) + " images into animated GIF file: " + target, "Success", Wfm.MessageBoxButtons.OK, Wfm.MessageBoxIcon.Information); RESCUE (exc) NativeType := TYPEOF(exc); NativeTypeName := MKSTR(RTS.TypeName(NativeType)^); IF NativeTypeName = "System.IO.DirectoryNotFoundException" THEN rslt := Wfm.MessageBox.Show("Output directory in path: " + target + " not found.", "Error", Wfm.MessageBoxButtons.OK, Wfm.MessageBoxIcon.Error) ELSE THROW (exc) END END Output; PROCEDURE WriteAnimatedGif*(IN sourcedir: Sys.String; IN filter:Sys.String; delay: INTEGER; IN odir: Sys.String; IN ofile: Sys.String); VAR agif: AnimatedGif; BEGIN NEW(agif); agif.delay := delay; agif.ScanImages(sourcedir, filter); agif.Output(odir + "\" + ofile) END WriteAnimatedGif; END AnimGif.
Käyttö FTN95 Fortranilla tapahtui siis:
module UseAnimGif implicit none ! Methods for Object "AnimGif.AnimatedGif" ASSEMBLY_EXTERNAL(name="AnimGif.AnimatedGif.ScanImages") ScanImages ASSEMBLY_EXTERNAL(name="AnimGif.AnimatedGif.Output") OutAnimGIF end module UseAnimGif winapp use UseAnimGif implicit none object("AnimGif.AnimatedGif") :: agif object("System.String[]") source source = new@("System.String[]",5) source(0) = "in\e.jpg" source(1) = "in\d.jpg" source(2) = "in\c.jpg" source(3) = "in\b.jpg" source(4) = "in\a.jpg" !AnimGif.AnimatedGif object can be used like a user defined type agif = new@("AnimGif.AnimatedGif") ! Allocate new AnimatedGif object agif%delay = 100 ! Set animation delay in 1/100 s agif%files = source ! Set source image files call OutAnimGIF(agif, 'out\testi1.gif') ! Output animated GIF ! Scan directory for image files, sort them alphabetically and output animated gif call ScanImages(agif, 'in', '*.jpg') call OutAnimGIF(agif, 'out\testi2.gif') end
Rox kirjoitti:
lainaus:
Value of type 'System.Drawing.Bitmap' cannot be converted to '1-dimensional array of System.Drawing.Bitmap'
Public Function WriteResource(ByVal filename As String, ByVal bmp As Bitmap()) As Boolean ' -- End FunctionDim bmp1 As Bitmap = Compressed WriteResource("C:\Users\Admin\Desktop\file.exe", bmp1)
No ainakin virheesi johtuu siitä, että funktiosi WriteResource
tahtoo sisällensä Bitmap
-taulukon, mutta tarjoat sille pelkkää Bitmap
ia.
Näin saat muutettua yksittäisen Bitmap
in taulukoksi:
WriteResource("C:\Users\Admin\Desktop\file.exe", New Bitmap() {bmp1})
Toinen vaihtoehto voi tietenkin olla, että funktiosi määrittely on väärin, jolloin siellä on ylimääräiset sulut ja funktio menisi tähän tapaan:
Imports System.Runtime.InteropServices Module ResourceWriter ' -- Public Function WriteResource(ByVal filename As String, ByVal bmp As Bitmap) As Boolean ' -- Dim file1 As Bitmap = bmp ' -- End Function End Module
jalski kirjoitti:
Rox kirjoitti:
Eikä luonnistu. Tajusin jo siis sen etten voi sitä noin sinne resursseihin laittaa, enkä saanut sitä käännettyä byte arrayksi taikka memorystreamiksi niin että se jopa toimisi. Tässä vielä yksi koodinpätkä mitä yritin, saamatta mitään kuitenkaan aikaan:
Tallenna kuva streamiksi, konvertoi byte arrayksi ja kirjoittele sieltä?
Taitaa olla just tämä kohta missä tökkii, vaikkei kovin vaikea pitäis olla. Eli jos joku viittis pienen esimerkin näyttää miten tämä tapahtuu niin se olis ihan jees..
Hennkka kirjoitti:
Rox kirjoitti:
lainaus:
Value of type 'System.Drawing.Bitmap' cannot be converted to '1-dimensional array of System.Drawing.Bitmap'
Public Function WriteResource(ByVal filename As String, ByVal bmp As Bitmap()) As Boolean ' -- End FunctionDim bmp1 As Bitmap = Compressed WriteResource("C:\Users\Admin\Desktop\file.exe", bmp1)No ainakin virheesi johtuu siitä, että funktiosi
WriteResource
tahtoo sisällensäBitmap
-taulukon, mutta tarjoat sille pelkkääBitmap
ia.
Näin saat muutettua yksittäisenBitmap
in taulukoksi:WriteResource("C:\Users\Admin\Desktop\file.exe", New Bitmap() {bmp1})Toinen vaihtoehto voi tietenkin olla, että funktiosi määrittely on väärin, jolloin siellä on ylimääräiset sulut ja funktio menisi tähän tapaan:
Imports System.Runtime.InteropServices Module ResourceWriter ' -- Public Function WriteResource(ByVal filename As String, ByVal bmp As Bitmap) As Boolean ' -- Dim file1 As Bitmap = bmp ' -- End Function End Module
Koitin tuota jo ennenkuin postasin, ei toimi ei.
Edit: Tässä vielä mitä äsken koitin, kuitenkaan toimimatta..
Kannattaa miettiä, mitkä parametrit ovat järjellisiä tuolle funktiolle. Jos sillä kirjoitetaan aina tasan yksi kuva tiedostoon, niin tietenkin parametrin kuuluu olla kuva eikä taulukollinen kuvia tai tavuja! Tässä koodi, jolla sain lisättyä kuvia exe-tiedostoon (tosin ResEdit ei tunnistanut niitä kuvista, liekö sitten väärä sijainti ja/tai muoto). Saat itse pähkäillä oikean tallennusmuodon ja paikan. Vihjeenä voinen sanoa, että kannattaa tutustua MAKEINTRESOURCE makroon sekä joihinkin sen vakioihin.
Public Function WriteResource(ByVal filename As String, ByVal bmp As Bitmap) As Boolean Try Dim handle As IntPtr = BeginUpdateResource(filename, False) ' Tiedostona otsikoineen Dim ms As New MemoryStream bmp.Save(ms, ImageFormat.Bmp) ms.Position = 0 Dim msDataPtr As IntPtr = Marshal.AllocHGlobal(CInt(ms.Length)) Marshal.Copy(ms.ToArray(), 0, msDataPtr, ms.Length) Dim res1 As Boolean = UpdateResource(handle, "BitMaps1", "0", 0, msDataPtr, Convert.ToUInt32(ms.Length)) Marshal.FreeHGlobal(msDataPtr) ' Vain pikselidata Dim bmpData = bmp.LockBits(New Rectangle(0, 0, bmp.Width, bmp.Height), Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat) Dim bmpDataPtr As IntPtr = bmpData.Scan0 Dim res2 As Boolean = UpdateResource(handle, "BitMaps2", "0", 0, bmpDataPtr, Convert.ToUInt32(bmpData.Height * bmpData.Stride)) EndUpdateResource(handle, False) Catch ex As Exception Return False End Try Return True End Function
Hennkka kirjoitti:
...
Kiitosta! Toimii täydellisesti. Joo, C++:ssa on tullut tutustuttua MAKEINTRESOURCE makroon, mutta jostain syystä ei VB:llä luonnistunut. Kiitos! :)
Aihe on jo aika vanha, joten et voi enää vastata siihen.