Skip to content

Instantly share code, notes, and snippets.

@indication
Last active August 29, 2015 14:19
Show Gist options
  • Save indication/6b8d85c7da8cf6fd8f5c to your computer and use it in GitHub Desktop.
Save indication/6b8d85c7da8cf6fd8f5c to your computer and use it in GitHub Desktop.
バイナリ読み書き
/*.suo
/bin
/obj
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。
// アセンブリに関連付けられている情報を変更するには、
// これらの属性値を変更してください。
[assembly: AssemblyTitle("ContertBytes")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ContertBytes")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから
// 参照不可能になります。COM からこのアセンブリ内の型にアクセスする場合は、
// その型の ComVisible 属性を true に設定してください。
[assembly: ComVisible(false)]
// 次の GUID は、このプロジェクトが COM に公開される場合の、typelib の ID です
[assembly: Guid("5af128f7-2c7e-4865-9517-b5883f97b889")]
// アセンブリのバージョン情報は、以下の 4 つの値で構成されています:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// すべての値を指定するか、下のように '*' を使ってビルドおよびリビジョン番号を
// 既定値にすることができます:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{57D7FA48-CB3E-459F-9F9F-F14537AE25BA}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ContertBytes</RootNamespace>
<AssemblyName>ContertBytes</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ReadWriteStructWithAllocHGlobal.cs" />
<Compile Include="ReadWriteStructWithReflection.cs" />
<Compile Include="Form1.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Form1.Designer.cs">
<DependentUpon>Form1.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="AssemblyInfo.cs" />
<Compile Include="TestStructure.cs" />
<Compile Include="TestStructureInternal.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Express 2013 for Windows Desktop
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConvertBytes", "ConvertBytes.csproj", "{57D7FA48-CB3E-459F-9F9F-F14537AE25BA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{57D7FA48-CB3E-459F-9F9F-F14537AE25BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{57D7FA48-CB3E-459F-9F9F-F14537AE25BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{57D7FA48-CB3E-459F-9F9F-F14537AE25BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{57D7FA48-CB3E-459F-9F9F-F14537AE25BA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ContertBytes
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var item = new TestStructure();
item.param4 = new TestStructureInternal();
item.param1 = 1;
item.param2 = 2;
item.param3 = "test";
item.param4.chr = 'a';
item.param4.next = 6;
item.array1 = new byte[] {10,11,12};
item.array2 = new int[] { 255, 256 };
string filename = "outfile.out";
if (File.Exists(filename))
{
File.Delete(filename);
}
using (var file = new FileStream(filename, FileMode.CreateNew))
using (var writer = new BinaryWriter(file))
{
ReadWriteStructWithReflection.WriteTo(writer, item);
}
}
}
}
namespace ContertBytes
{
partial class Form1
{
/// <summary>
/// 必要なデザイナー変数です。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 使用中のリソースをすべてクリーンアップします。
/// </summary>
/// <param name="disposing">マネージ リソースが破棄される場合 true、破棄されない場合は false です。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows フォーム デザイナーで生成されたコード
/// <summary>
/// デザイナー サポートに必要なメソッドです。このメソッドの内容を
/// コード エディターで変更しないでください。
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(93, 35);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 0;
this.button1.Text = "export";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 262);
this.Controls.Add(this.button1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button button1;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ContertBytes
{
static class Program
{
/// <summary>
/// アプリケーションのメイン エントリ ポイントです。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace ContertBytes
{
/// <summary>
/// ポインタを得るためにMarshal.AllocHGlobalでバイト配列のコピー用領域を作成し、
/// Marshal.PtrToStructure・Marshal.StructureToPtrで変換して構造体の読み書きを行う
/// </summary>
/// <remarks>
/// 参照型のフィールドを持つ構造体は読み書きできない
/// http://smdn.jp/programming/netfx/tips/convert_struct_and_bytearray/
/// </remarks>
static class ReadWriteStructWithAllocHGlobal
{
public static void WriteTo<TStruct>(BinaryWriter writer, TStruct s) where TStruct : struct
{
var size = Marshal.SizeOf(typeof(TStruct));
var buffer = new byte[size];
var ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(s, ptr, false);
Marshal.Copy(ptr, buffer, 0, size);
}
finally
{
if (ptr != IntPtr.Zero)
Marshal.FreeHGlobal(ptr);
}
writer.Write(buffer);
}
public static TStruct ReadFrom<TStruct>(BinaryReader reader) where TStruct : struct
{
var size = Marshal.SizeOf(typeof(TStruct));
var ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(reader.ReadBytes(size), 0, ptr, size);
return (TStruct)Marshal.PtrToStructure(ptr, typeof(TStruct));
}
finally
{
if (ptr != IntPtr.Zero)
Marshal.FreeHGlobal(ptr);
}
}
}
}
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Text;
using System.Collections.Generic;
namespace ContertBytes
{
/// <summary>リフレクションとBitConverterクラスのバイト配列変換メソッドを使って構造体の読み書きを行う</summary>
/// <exception>フィールドの型がBitConverterでは変換できない型の場合はNotSupportedException</exception>
/// <remarks>
/// この実装は不完全で、フィールドが構造体の場合は再帰的に呼び出しを行うなどの処理を追加する必要がある。
/// 参照型のフィールドを持つ構造体は読み書きできない
/// http://smdn.jp/programming/netfx/tips/convert_struct_and_bytearray/
/// </remarks>
static class ReadWriteStructWithReflection
{
public static void WriteTo<TStruct>(BinaryWriter writer, TStruct s, bool isBigEndian = false) where TStruct : struct
{
writer.Write(GetBytes(s, isBigEndian));
}
public static byte[] GetBytes<TStruct>(TStruct s, bool isBigEndian) where TStruct : struct
{
var ret = new List<byte>();
var type = s.GetType();
// TStructの全フィールドを取得して、フィールドのオフセット順に並べ替え
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
.OrderBy(f => Marshal.OffsetOf(type, f.Name).ToInt64());
foreach (var field in fields)
{
var attr = field.GetCustomAttribute<MarshalAsAttribute>() as MarshalAsAttribute;
if (field.FieldType.IsArray)
{
var genericMethiod = typeof(ReadWriteStructWithReflection).GetMethod("GetFieldBytesArray");
var specMethod = genericMethiod.MakeGenericMethod(new[] { field.FieldType.GetElementType() });
ret.AddRange((byte[])specMethod.Invoke(null, new[] { attr, field.FieldType.GetElementType(), field.GetValue(s), isBigEndian }));
}
else
{
ret.AddRange(GetFieldBytes(attr, field.FieldType, field.GetValue(s), isBigEndian));
}
}
return ret.ToArray();
}
public static byte[] GetFieldBytesArray<T>(MarshalAsAttribute attr, Type type, T[] value, bool isBigEndian)
{
var ret = new List<byte>();
if (attr != null && value.Count() != attr.SizeConst)
{
value = (T[])value.Clone();
Array.Resize(ref value, attr.SizeConst);
}
foreach (var item in value)
{
ret.AddRange(GetFieldBytes(null, type, item, isBigEndian));
}
return ret.ToArray();
}
public static byte[] GetFieldBytes(MarshalAsAttribute attr, Type type, object value, bool isBigEndian)
{
// フィールドがbyteなら、そのまま書き込む
if (type == typeof(byte) || type == typeof(Byte))
{
return new[] { (byte)value };
}
else if (type == typeof(string))
{
var chr = ((string)value).ToArray();
if (attr != null)
{
Array.Resize(ref chr, attr.SizeConst);
}
return Encoding.ASCII.GetBytes(chr);
}
else
{
// フィールドがbyte以外なら、BitConverter.GetBytesメソッドでbyte[]に変換して書き込む
var getBytes = typeof(BitConverter).GetMethod("GetBytes", new[] { type });
if (getBytes == null)
{
if (attr != null)
{
var genericMethiod = typeof(ReadWriteStructWithReflection).GetMethod("GetBytes");
var specMethod = genericMethiod.MakeGenericMethod(new[] { type });
return (byte[])specMethod.Invoke(null, new[] { value, isBigEndian });
}
else
{
throw new NotSupportedException("unsupported field type: " + type.FullName);
}
}
else
{
var ret = (byte[])getBytes.Invoke(null, new[] { value });
if (isBigEndian)
{
var data = new List<byte>();
data.AddRange(ret);
data.Reverse();
ret = data.ToArray();
}
return ret;
}
}
}
public static TStruct ReadFrom<TStruct>(BinaryReader reader) where TStruct : struct
{
var type = typeof(TStruct);
var ret = default(TStruct);
object retBoxed = ret; // boxing
// TStructの全フィールドを取得して、フィールドのオフセット順に並べ替え
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
.OrderBy(f => Marshal.OffsetOf(type, f.Name).ToInt64());
foreach (var field in fields)
{
if (field.FieldType == typeof(byte))
{
// フィールドがbyteなら読み込んだ値をそのままフィールドの値とする
field.SetValue(retBoxed, reader.ReadByte());
}
else
{
// フィールドがbyte以外なら、BitConverter.ToXXXメソッドで該当する型に変換した値をフィールドの値とする
var toXXX = typeof(BitConverter).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(m => m.Name.StartsWith("To") && m.ReturnType == field.FieldType)
.FirstOrDefault();
if (toXXX == null)
throw new NotSupportedException("unsupported field type: " + field.FieldType.FullName);
var size = Marshal.SizeOf(field.FieldType);
field.SetValue(retBoxed, toXXX.Invoke(null, new object[] { reader.ReadBytes(size), 0 }));
}
}
return (TStruct)retBoxed; // unboxing
}
}
}
using System.Runtime.InteropServices;
namespace ContertBytes
{
[StructLayout(LayoutKind.Sequential)]
struct TestStructure
{
public byte param1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
public byte[] array1;
public int param2;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public int[] array2;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
public string param3;
[MarshalAs(UnmanagedType.Struct)]
public TestStructureInternal param4;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ContertBytes
{
[StructLayout(LayoutKind.Sequential)]
struct TestStructureInternal
{
public int next;
public char chr;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment