Addition of byte or bytes into an byte array by combining in C#

Hi,

I am running across an issue and I'm not complete sure here. I have an image that I converted into a byte array. Now, I want to add to that byte array. Let's say I want to add an "I" for image on to the beginning of the byte array.  Or lets say I have a statement of "Hello World" that I want to add.

I currently have it setup like this:

byte[] aryTemp = new byte[aryByteFirst.Length + aryByteSecond.Length];
            try
            {
                System.Buffer.BlockCopy(aryByteFirst, 0, aryTemp, 0, aryByteFirst.Length);
                System.Buffer.BlockCopy(aryByteSecond, 0, aryTemp, aryByteFirst.Length, aryByteSecond.Length);

            }
            catch (Exception ex)
            {
                return null;
            }

Open in new window


This will perform the BlockCopy and the aryTemp array is properly sized. However, if I have the example of adding an "I" which is a byte of 73 to the beginning to an image that was converted into a byte array. It is properly sized and I see the "I" byte of 73 in the beginning and all the other image bytes after.

The problem is if I try to convert it to a memorystream and try to load the image I get a "Parameter is invalid".

If I instead perform a File.WriteAllBytes of aryTemp to a file and try to load the image from the file. I then get an "Out of Memory" error.

How can I perform this action then?

Any information on this would be very much appreciated!
LVL 1
davismAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

käµfm³d 👽Commented:
The problem is if I try to convert it to a memorystream and try to load the image I get a "Parameter is invalid".
You've got return null; in there. Are you sure your code isn't throwing an exception, and null is being passed to the MemoryStream?

If I instead perform a File.WriteAllBytes of aryTemp to a file and try to load the image from the file. I then get an "Out of Memory" error.

Open in new window

Do you have both the original image and the newly-written image (i.e. the file) in memory at the same time? Perhaps that's why you're running out?
0
chaauCommented:
I think when you add an "I" to the beginning of the image byte array you corrupt the image structure. Usually the image files start with the header that has a predefined structure. The header usually describes what is the type of the image, its compression, size, bits, etc. If you add the "I" to the beginning you shift the position of the header elements making the file completely unreadable. It is a perfect way to corrupt the file structure. Can you explain why you are doing this?
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
käµfm³d 👽Commented:
If you add the "I" to the beginning you shift the position of the header elements making the file completely unreadable.
That's a bit of an exaggeration. Sure, the file becomes unreadable to image viewers, but that doesn't mean that his code can't read the image. So long as he strips the extra bits back out before passing the rest of the data to the viewer, the image data itself--including the header--is not corrupted.
0
Get your problem seen by more experts

Be seen. Boost your question’s priority for more expert views and faster solutions

chaauCommented:
The author did not mention that they are reading it from the second position
0
davismAuthor Commented:
No, it is not throwing an exception. It's actually executing that just fine.

I have narrowed it down to these statements, actually.

For instance, aryByteSecond is a byte array of an image. The aryByteFirst is like a byte for the letter "I".

If I just use the aryByteSecond byte array and I write to a file called C:\MyTest\MyTestImage.txt using File.WriteAllBytes and either use that in a memorystream or do like:

Stream ms = new MemoryStream();
Image returnImage = Image.FromFile("C:\\MyTest\\MyTestImage.txt");
returnImage.Save(ms, System.Drawing.Imaging.ImageFormat.Tiff);

Image img = System.Drawing.Image.FromStream(ms);

img.Save("C:\\MyTest" + "\\myImage.tiff", System.Drawing.Imaging.ImageFormat.Tiff);

Open in new window


It WILL produce an image.

However, if I do it after those System.Buffer.BlockCopy statements combined it will NOT produce an image that THAT is when the exception is thrown with "Out Of Memory" which is not correct. Remember the first byte array is has the "I" (I know I am using an array just for a char but this is just an example) and the second is the image.
0
käµfm³d 👽Commented:
You're not going to be able to use the Image class because adding data to the file will "corrupt" the file as chaau mentions. The Image class expects an actual image the same way a viewer would. If you read/write the data manually via a FileStream, then you should not have an issue.
0
davismAuthor Commented:
chaau,

I am converting a Delphi application to .net.

Based on what you said, on the header one would associate that the Delphi would corrupt it too. However, it is not and does not.

Basically, the header of a file the should be/would be agnostic of language being that it is NTFS. So, in that case, why would it be ok in Delphi yet not in .NET?
0
chaauCommented:
I think when you load the MemoryStream from file you need to use just normal IO functions instead of the Image.FromFile().
0
chaauCommented:
Delphi and .Net may have different libraries that process images. Delphi may have a built-in corrector that fixes the header problems. I recall having a production issue with an imaging system written in MS Access. It stopped processing some image files. As it turned the files were saved as jpg that had in fact the bmp structure. When you open the files using an image viewer they would display without errors. I have just by chance figured out that the files are indeed the bmp files by opening them in IrfanViewer. The program had literally told me that the file is a bmp with the jpg extension. The program also asked me if I wanted to fix the file format or the extension. When I fixed them it started to work in Access. And you are asking: "why would it be ok in Delphi yet not in .NET"
0
davismAuthor Commented:
chaau,

And when I did that is when a different exception is thrown which is the "Parameter is invalid.".
0
davismAuthor Commented:
And chaau, yes, that is correct. the byte array if that is a jpg, gif, tiff, etc still opens fine in like the Win Image Viewer.

If Delphi let say has a auto-corrector for the header what would be the equivalent to the BlockRead and Move for .NET? It was my userstanding that the System.Buffer.BlockCopy was equivalent but seems as though it isn't.

How could that be header be adjusted in .NET then?
0
chaauCommented:
It would be great if you show us all code. It is hard to figure out from the chunks you posted here
0
davismAuthor Commented:
What I provided is the main situation where the problem is. Really comes down to IF by adding the "I" (or whatever else) makes the image corrupt in .NET because it's changing the file header then what options are there to "fix" it in the language/framework?

If I piece extract it like Kaumed, mentioned, then would one know where to stop? (if the replacement was something other than an "I"?)

I would think the header is in different aspect anyway because there are no other attributed of the file listed. Or maybe when you refer to header you are not talking the file header per say, but maybe like a content header.
0
ArkCommented:
Can you explain the goal?
Add "I" as a watermark to image?
Hide "I" inside image using steganography?
Make image file unreadable by other programs except yours?
0
ArkCommented:
OK, here is an example of "corrupted" files:
    Private mySignature As String = "Hello World!"

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        SaveCorrupted("c:\Windows\Web\Wallpaper\Landscapes\img8.jpg", "C:\test.txt")
    End Sub

    Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
        Dim s = ""
        PictureBox1.Image = LoadCorrupted("C:\test.txt", mySignature.Length, s)
        MsgBox("Signature=" & s)
    End Sub
    'Create corrupted file with prefix=mySignature (Hello World)
    Private Sub SaveCorrupted(realPath As String, destPath As String)
        Dim realBytes() = IO.File.ReadAllBytes(realPath)
        Dim extraBytes = System.Text.Encoding.ASCII.GetBytes(mySignature)
        Using fs As New IO.FileStream(destPath, IO.FileMode.OpenOrCreate, IO.FileAccess.ReadWrite)
            fs.Write(extraBytes, 0, extraBytes.Length)
            fs.Write(realBytes, 0, realBytes.Length)
        End Using
    End Sub
    'Read "corrupted" file, check signature and, if match, return real image
    Private Function LoadCorrupted(path As String, sigLength As Integer, ByRef Signature As String) As Image
        Dim imgBytes(), sigBytes() As Byte
        Using fs As New IO.FileStream(path, IO.FileMode.Open, IO.FileAccess.Read)
            ReDim sigBytes(sigLength - 1)
            fs.Read(sigBytes, 0, sigLength)
            Signature = System.Text.Encoding.ASCII.GetString(sigBytes)
            If Signature <> mySignature Then Return Nothing
            ReDim imgBytes(fs.Length - sigLength - 1)
            fs.Read(imgBytes, 0, fs.Length - sigLength)
        End Using
        Using ms As New IO.MemoryStream(imgBytes)
            Return Image.FromStream(ms).Clone
        End Using
    End Function

Open in new window

0
ArkCommented:
PS
Adding WaterMark sample
    Private Sub AddWaterMark(srcPath As String, destPath As String, wmText As String)
        Dim bm = New Bitmap(srcPath)
        Using g = Graphics.FromImage(bm)
            g.DrawString(wmText, New Font("Verdana", 30), Brushes.AliceBlue, 0, 0)
        End Using
        bm.Save(destPath)
    End Sub

    Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
        AddWaterMark("c:\Windows\Web\Wallpaper\Landscapes\img8.jpg", "C:\test.jpg", "Hellow World")
    End Sub

Open in new window

0
davismAuthor Commented:
Ark,

The examples you provided are essentially skipping over that byte position using the "I" or "Hello World".  Albeit, one still does not know the length of the "I" or the "Hello World" without it being conveyed to them.

If one were to know then, what you have is one option the other option is to reverse the Buffer.BlockCopy aspect.

Which by the way, I believe yields better performance.

The reasoning for the "I" is are giving the payload to a third-party and that is their requirement again, albeit that they could have just looked at the first few bytes to determine the type of image but they do not. I can see reasons why to not do that and go with the "I" or whatever, but I don't know what/how they are doing it and it's really not warranted to question and suggest. I am following the requirement. Yet, they are saying the image is corrupt which outside of that "I" positioning the image is NOT corrupt and then again the Delphi program does that and it works for them.
0
ArkCommented:
Each image format starts with image header which contains info about image(length, size, compression etc.) When you add anything at the beginnig of file - you corrupt this header and image operating programs cannot read header correctly - so you need skip that extra bytes. You can add your own header, which contains lehgth of extra bytes at the beginning:
    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        SaveCorrupted("c:\Windows\Web\Wallpaper\Landscapes\img8.jpg", "C:\test.txt", "Hello World!")
    End Sub

    Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
        Dim s = ""
        PictureBox1.Image = LoadCorrupted("C:\test.txt", s)
        MsgBox("Signature=" & s)
    End Sub
    'Create corrupted file
    Private Sub SaveCorrupted(realPath As String, destPath As String, signature As String)
        Dim realBytes() = IO.File.ReadAllBytes(realPath)
        Dim extraBytes = System.Text.Encoding.ASCII.GetBytes(signature)
        Dim sigLength = extraBytes.Length
        Using fs As New IO.FileStream(destPath, IO.FileMode.OpenOrCreate, IO.FileAccess.ReadWrite)
            fs.Write(BitConverter.GetBytes(sigLength), 0, 4) 'write signature length as integer (4-byte)
            fs.Write(extraBytes, 0, sigLength) 'write signature
            fs.Write(realBytes, 0, realBytes.Length) 'write image bytes
        End Using
    End Sub
    'Read "corrupted" file
    Private Function LoadCorrupted(path As String, ByRef Signature As String) As Image
        Dim imgBytes(), sigBytes() As Byte
        Dim lengthBytes(3) As Byte
        Using fs As New IO.FileStream(path, IO.FileMode.Open, IO.FileAccess.Read)
            fs.Read(lengthBytes, 0, 4) 'read first 4 bytes - signature length
            Dim sigLength = BitConverter.ToUInt32(lengthBytes, 0)
            ReDim sigBytes(sigLength - 1)
            fs.Read(sigBytes, 0, sigLength) 'read signature
            Signature = System.Text.Encoding.ASCII.GetString(sigBytes)
            ReDim imgBytes(fs.Length - sigLength - 1)
            fs.Read(imgBytes, 0, fs.Length - sigLength) 'read real image
        End Using
        Using ms As New IO.MemoryStream(imgBytes)
            Return Image.FromStream(ms).Clone
        End Using
    End Function

Open in new window

Another option is to add extra bytes to the end of image, starting with some predefined string, say "MySignatureStartsHere":
    Private sigHeader = "MySignatureStartHere"
    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        SaveCorrupted("c:\Windows\Web\Wallpaper\Landscapes\img8.jpg", "C:\test.txt", "Hello World!")
    End Sub

    Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
        Dim s = ""
        PictureBox1.Image = LoadCorrupted("C:\test.txt", s)
        MsgBox("Signature=" & s)
    End Sub
    'Create corrupted file
    Private Sub SaveCorrupted(realPath As String, destPath As String, signature As String)
        Dim realBytes() = IO.File.ReadAllBytes(realPath)
        Dim extraBytes = System.Text.Encoding.ASCII.GetBytes(sigHeader & signature)
        Dim sigLength = extraBytes.Length
        Using fs As New IO.FileStream(destPath, IO.FileMode.OpenOrCreate, IO.FileAccess.ReadWrite)
            fs.Write(realBytes, 0, realBytes.Length) 'write image bytes
            fs.Write(extraBytes, 0, sigLength) 'write signature with sigHeader
        End Using
    End Sub
    'Read "corrupted" file
    Private Function LoadCorrupted(path As String, ByRef Signature As String) As Image
        Dim img = Image.FromFile(path)
        Using sr As New IO.StreamReader(path)
            Dim s = sr.ReadToEnd()
            Dim pos = s.IndexOf(sigHeader)
            If pos > -1 Then
                pos += sigHeader.length
                Signature = s.Substring(pos)
            End If
        End Using
        Return img
    End Function

Open in new window

In this case image processing apps will read image correctly
0
davismAuthor Commented:
Thanks all for the information! Greatly appreciated.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C#

From novice to tech pro — start learning today.