2

Good morning everyone!

I'm with a new problem here. I have to write down a data that comes from a struct that I declared in my sistem.

The struct I created has only two fields and I use to to later conevrt it into bytes.

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct MyStructData
{
    public short Id;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
    public string Name;
}

I convert this struct in bytes with the following code:

private byte[] getBytes(MyStructData aux)
{
    int length = Marshal.SizeOf(aux);
    IntPtr ptr = Marshal.AllocHGlobal(length);
    byte[] myBuffer = new byte[length];

    Marshal.StructureToPtr(aux, ptr, true);
    Marshal.Copy(ptr, myBuffer, 0, length);
    Marshal.FreeHGlobal(ptr);

    return myBuffer;
}

This function is made to transform each element inside in a List structure of MyStructData type elements where I have all the registers I want to send to another machine, and I do it with the code pasted below:

string saveToFilePath = "D:\\" + filename;
Stream myStream = myFtpRequest.GetRequestStream();

using (FileStream myFileStream = new FileStream(saveToFilePath, FileMode.Create))
{
    foreach (MyStructData myData in myStructDataList)
    {
        int length = 2048;
        byte[] newBuffer = new byte[length];
        newBuffer = getBytes(myCust);

        int len = 0;
        int bytesRead = 0;

        myFileStream.Write(newBuffer, 0, len);
        bytesRead += len;
    }

    myFileStream.Close();
}

My problem comes that I see that my new file is empty and I can't see why it doesn't gets the information of my bytes. I already checked if the List comes with data or not and I also checked that my byte convertion function works also perfectly, but I can't get to the point to see what's causing that my file is empty.

If someone knows the light at the end of the tunnel I would appreciate a lot your help!

EDIT Now I have another method to write the data into the file and it works good:

using (Stream stream = new FileStream(saveToFilePath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
{
    using (BinaryWriter writer = new BinaryWriter(stream, Encoding.Default))
    {
        // get all data
        foreach (MyStructData myData in myStructDataList)
        {
            byte[] newBuffer = getBytes(cd);
            writer.Write(newBuffer);
        }
    }
}
0

2 Answers 2

4

The third parameter to FileStream.Write is the count of bytes to write. It should not be 0.

string saveToFilePath = "D:\\" + filename;
Stream myStream = myFtpRequest.GetRequestStream();

int bytesWritten = 0;
using (FileStream myFileStream = new FileStream(saveToFilePath, FileMode.Create))
{
    foreach (MyStructData myData in myStructDataList)
    {
        newBuffer = getBytes(myData);
        myFileStream.Write(newBuffer, 0, newBuffer.Length);
        bytesWritten += newBuffer.Length;
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

When I write the second line in my code it gives me an error. It recognizes the function Write in myFileStream as void, so it doesn't return any int value at all.
Thanks!!! This works well, now I go with my next problem so I hope I don't bother you all
4

I don't think you should be writing the data to a file like this. For a simple struct like that, you would better off using a BinaryFormatter.

Here's an example. First you need to make MyStructData serializable by adding the [Serializable] attribute:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
[Serializable]
public struct MyStructData
{
    public short Id;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
    public string Name;
}

Then you can write a list of those structs to a file like so:

List<MyStructData> data = new List<MyStructData>();

for (short i = 0; i < 100; ++i)
    data.Add(new MyStructData{Id = i, Name = i.ToString()});

string filename = "C:\\test\\test.data";

using (var file = File.OpenWrite(filename))
{
    var writer = new BinaryFormatter();
    writer.Serialize(file, data); // Writes the entire list.
}

And you can read them back like so:

using (var file = File.OpenRead(filename))
{
    var reader = new BinaryFormatter();
    data = (List<MyStructData>) reader.Deserialize(file); // Reads the entire list.
}

foreach (var item in data)
    Console.WriteLine("Id = {0}, Name = {1}", item.Id, item.Name);

If the only reason you added the marshalling to the struct was so you can write and read it to and from a file, you could then remove it so your struct would look like this:

[Serializable]
public struct MyStructData
{
    public short Id;
    public string Name;
}

[EDIT]

I come from the future to note that BinaryFormatter is now considered insecure:

The BinaryFormatter type is dangerous and is not recommended for data processing. Applications should stop using BinaryFormatter as soon as possible, even if they believe the data they're processing to be trustworthy. BinaryFormatter is insecure and can't be made secure.

A much better modern binary serialization format to use is MessagePack-CSharp available via NuGet.

5 Comments

This looks well... I dunno if I should use it because this is a test to send the file and maybe I have to change the struct to something more complex.
@MarialvyMartínez BinaryFormatter can handle pretty complicated things. What it can't handle is things relating to unmanaged resources (e.g. a window handle) but I shouldn't think you'd be serializing those. I only suggest BinaryFormatter because it's fairly simple to use. For greater control you can use XmlSerializer. Either way, you really shouldn't be marshalling the data yourself just to serialize it! Marshalling is really for passing data to unmanaged code.
I strongly recommend you read this before making a decision: msdn.microsoft.com/en-us/library/vstudio/ms233843.aspx One thing I can say: Using Marshal to do serialization is very wrong.
I found another way to do it but with the BinaryWriter, I tried it now and it works perfectly also. I have my reservations because I read somewhere here that the BinaryFormatter has a few problems also. But I think the choices are made because of what you're confortable with :)
Either use BinaryFormatter or XmlSerializer (along with DataContacts) - the latter are the most "modern", but XmlSerializer will increase the data size so you might need to also compress it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.