バイナリに読み書きを行えるようにするクラスをさらに拡張するテスト http://smdn.jp/programming/netfx/tips/convert_struct_and_bytearray/
Last active
August 29, 2015 14:19
-
-
Save indication/6b8d85c7da8cf6fd8f5c to your computer and use it in GitHub Desktop.
バイナリ読み書き
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*.suo | |
/bin | |
/obj |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8" ?> | |
<configuration> | |
<startup> | |
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> | |
</startup> | |
</configuration> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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")] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| |
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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