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 ModuleFormi, 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')
endRox 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ää Bitmapia.
Näin saat muutettua yksittäisen Bitmapin 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 Modulejalski 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
WriteResourcetahtoo sisällensäBitmap-taulukon, mutta tarjoat sille pelkkääBitmapia.
Näin saat muutettua yksittäisenBitmapin 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 FunctionHennkka 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.