20200919のC#に関する記事は5件です。

Visual Studio 2019のC#にて、Bitmapオブジェクトを、Android環境下において直接Windows Bitmapファイルにバイナリを叩いて出力する。コードのサンプルです。

表題の通り
かつてのWindows版に続いて
https://qiita.com/oyk3865b/items/58abd56c5c1edcc84118

今回は
Visual Studio 2019のC#にて、
Android環境下において
Bitmapオブジェクトを、直接Windows Bitmapファイルにバイナリを叩いて出力する。
コードのサンプルでございます。

特段、NuGetなどで他のプラグインや
別途外部dllを必要としないようにしています。

要は、バイナリを以下のように、直接叩いています。
1.Bitmapのピクセル情報をバイナリ配列に書き出す。
2.Bitmapのヘッダーの書き込み。
3.4の倍数のルールに従いBitmapバイナリをファイルに書き出す。

Android開発環境では、
bitmap.Compress(Bitmap.CompressFormat.Png, 100, fos);みたく
bitmap.Compress(Bitmap.CompressFormat.Bmp, 100, fos);という画像の保存方法が
使用できないため、

Androidアプリで
RGB888・Bitmap形式にて出力したい場合に
自力でBitmapをファイルに書き出すことにしたため
作成いたしました。

※私は素人ですし
今回のコードは、あくまで最低限の動作する部分にとどめていますので、汚いです。
例外処理や、解放など至らぬ点が、多々ございます。ご了承ください。
もし実際に参考にされる際は、必要個所を必ず訂正・加筆してからにしてください。

※動作は沢山メモリを使うので重いです。
その辺は度外視しています。

下の画像は、今回コードでの作成例です。
参考画像
●もともとBitmap形式の画像を取り込んで(変換前)、
今回のコードにて出力した結果(変換後)との
バイナリ比較画像です

[参考サイト]
Bitmapデータを、バイナリレベルで触るにあたって調べたレポート1
※リンク先は、私のブログです。気を付けてください。

clsBMPF.cs
using System;
using System.Collections.Generic;
using Android.Graphics;

    class clsBMPF
    {
        //画像はARGB8888で扱う
        Android.Graphics.Bitmap.Config bitmapConfig = Android.Graphics.Bitmap.Config.Argb8888;

        //■ヘッダー情報関係
        //http://oyk3865b.blog13.fc2.com/blog-entry-1394.html
        //△冒頭の構文=BfType(ファイルタイプ)------------------------------------------------------------------------------------------------------------
        readonly byte[] bmp_header_start = new byte[] { 0x42, 0x4D };


        //△ヘッダー間隔用のオブジェクト------------------------------------------------------
        readonly byte[] bmp_1st_obj = new byte[] { 0, 0, 0, 0 };


        //△BfOffBits------------------------------------------------------
        readonly byte[] bmp_2nd_obj = new byte[] { 0x36, 0, 0, 0 };


        //△BiSize------------------------------------------------------
        readonly byte[] bmp_3rd_obj = new byte[] { 0x28, 0, 0, 0 };


        //△BiPlanes_BiBitCount------------------------------------------------------
        readonly byte[] bmp_4th_obj = new byte[] { 0x1, 0, 0x18, 0 };

        public void Output_Bitmap_Image(Bitmap bitmap, string Output_Path)
        {

            //Windows Bitmapを自分で作る
            //bitmap→出力する画像の元データ
            //Output_Path→出力するパス(重複の場合、上書きする)
            //https://qiita.com/oyk3865b/items/58abd56c5c1edcc84118

            //色設定をARGB8888に統一する。
            //https://stackoverflow.com/questions/7320392/how-to-draw-text-on-image
            bitmap = bitmap.Copy(bitmapConfig, true);

            //Bitmapのバイナリ置き換えの準備
            //https://qiita.com/aymikmts/items/7139fa6c4da3b57cb4fc
            string err_str = "byteBuffer";
            Java.Nio.ByteBuffer byteBuffer = Java.Nio.ByteBuffer.Allocate(bitmap.ByteCount);
            err_str = "CopyPixelsToBuffer";
            bitmap.CopyPixelsToBuffer(byteBuffer);
            err_str = "Flip";
            byteBuffer.Flip();
            err_str = "bmparr";

            //基礎Bitmapのバイナリへの置き換え
            byte[] bmparr = new byte[byteBuffer.Capacity()];
            err_str = "Get";
            byteBuffer.Duplicate().Get(bmparr);
            err_str = "Clear";
            byteBuffer.Clear();


            //ファイルサイズの算出(横は4の倍数でないといけない)
            int width_size = bitmap.Width * 3; //幅1行のバイナリサイズを算出(幅px*3色)
                                           //幅バイナリ値は4の倍数に直してから、高さpxをかけ本体サイズを算出。
            Int32 bitmap_filesize = ((((width_size + 3) / 4) * 4) * bitmap.Height);
            //出来るBitmapの全ファイル・サイズを格納
            Int32 bitmap_all_filesize = bitmap_filesize + 54; //ヘッダーなど本体以外で54バイト消費


            //■出力bmpバイナリの初期化
            //出力bmpのバイナリ格納用。
            List<byte> ary_bmp_file = new List<byte>();
            //出力bmpのバイナリ各オブジェクトの、バイト開始位置を格納用。
            List<byte> ary_bmp_byte_head = new List<byte>();


            //■ファイルを作成して書き込む 
            //ファイルが存在しているときは、消してから書き込みする 
            if (System.IO.File.Exists(Output_Path)) { System.IO.File.Delete(Output_Path); }

            using (System.IO.FileStream fs = new System.IO.FileStream(Output_Path,
                System.IO.FileMode.Create,
                System.IO.FileAccess.Write))
            {
                //◎冒頭の書き込み
                ary_bmp_file.AddRange(bmp_header_start);

                //◎全ファイルサイズの格納(BfSize)
                //ファイルサイズ値を、バイト配列に変換
                ary_bmp_byte_head.AddRange(BitConverter.GetBytes(bitmap_all_filesize));
                if (ary_bmp_byte_head.Count < 4)
                { //必ず、4バイトにする。
                  //空隙は0で埋める。
                    for (int i = ary_bmp_byte_head.Count + 1; i <= 4; i++)
                    {
                        ary_bmp_byte_head.Add(0);
                    }
                }
                ary_bmp_file.AddRange(ary_bmp_byte_head.ToArray()); //書き加える。
                ary_bmp_byte_head.Clear(); //不要情報を、クリア。

                //◎空白4バイトの書き込み
                ary_bmp_file.AddRange(bmp_1st_obj);

                //◎BfOffBitsの書き込み
                ary_bmp_file.AddRange(bmp_2nd_obj);

                //◎BiSizeの書き込み
                ary_bmp_file.AddRange(bmp_3rd_obj);


                //◎画像幅の指定(BiWidth)
                //幅の値を、バイト配列に変換
                ary_bmp_byte_head.AddRange(BitConverter.GetBytes(bitmap.Width));
                if (ary_bmp_byte_head.Count < 4)
                { //必ず、4バイトにする。
                    for (int i = ary_bmp_byte_head.Count + 1; i <= 4; i++)
                    {
                        ary_bmp_byte_head.Add(0);
                    }
                }
                ary_bmp_file.AddRange(ary_bmp_byte_head); //書き加える。
                ary_bmp_byte_head.Clear(); //不要情報を、クリア。


                //◎画像高さの指定(BiHeight)
                //高さの値を、バイト配列に変換
                ary_bmp_byte_head.AddRange(BitConverter.GetBytes(bitmap.Height));
                if (ary_bmp_byte_head.Count < 4)
                {    //必ず、4バイトにする。
                    for (int i = ary_bmp_byte_head.Count + 1; i <= 4; i++)
                    {
                        ary_bmp_byte_head.Add(0);
                    }
                }
                ary_bmp_file.AddRange(ary_bmp_byte_head); //書き加える。
                ary_bmp_byte_head.Clear(); //不要情報を、クリア。

                //◎BiPlanes_BiBitCountの書き込み
                ary_bmp_file.AddRange(bmp_4th_obj);

                //◎空白4バイトの書き込み
                ary_bmp_file.AddRange(bmp_1st_obj);

                //◎画像ファイルサイズの格納(BiSizeImage)
                //ファイルサイズ値を、バイト配列に変換
                ary_bmp_byte_head.AddRange(BitConverter.GetBytes(bitmap_filesize));
                for (int i = ary_bmp_byte_head.Count + 1; i <= 4; i++)
                {
                    ary_bmp_byte_head.Add(0);
                }
                ary_bmp_file.AddRange(ary_bmp_byte_head); //書き加える。
                ary_bmp_byte_head.Clear(); //不要情報を、クリア。

                //◎空白4バイトの書き込み×4
                for (int i = 0; i < 4; i++)
                {
                    ary_bmp_file.AddRange(bmp_1st_obj);
                }

                //一旦、バイト型配列の内容をファイルに書き出す
                fs.Write(ary_bmp_file.ToArray(), 0, ary_bmp_file.Count);

                //配列を、一旦、クリアする。
                ary_bmp_file.Clear();
                //■ヘッダーの作成------------ここまで-----------------

                //Bitmapは、左下から書き込む
                for (int bmp_y = bitmap.Height - 1; bmp_y >= 0; bmp_y--)
                { //縦軸分のループ(下から)

                    //横幅の分だけ配列にコピーする。
                    //このコピー部分は、ループでなくて、配列のコピーでも良い
                    for (int bmp_x = 0; bmp_x <= (bitmap.Width - 1); bmp_x++)
                    {   //横軸分のループ(左から)
                        //その座標の点にある、ピクセル(ドット)の色を取得する。
                        long pos = bmp_y * (bitmap.Width * 4) + bmp_x * 4;
                        //ary_bmp_file.Add(bmparr[pos + 3]); //AはRGB888形式で出力するため入れない
                        ary_bmp_file.Add(bmparr[pos + 2]); //R
                        ary_bmp_file.Add(bmparr[pos + 1]); //G
                        ary_bmp_file.Add(bmparr[pos + 0]); //B
                    }

                    //4の倍数でない場合
                    if (ary_bmp_file.Count % 4 != 0)
                    {
                        //横方向は、4の倍数とする。
                        while ((ary_bmp_file.Count % 4) != 0)
                        {
                            ary_bmp_file.Add(0); //0で埋める
                        }
                    }


                    //一旦、バイト型配列の内容をファイルに書き出す
                    fs.Write(ary_bmp_file.ToArray(), 0, ary_bmp_file.Count);


                    //配列を、一旦、クリアする。
                    ary_bmp_file.Clear();
                }


                //■書き込みの後処理
                fs.Close(); //ファイルを閉じる 
            }

            //バイナリ・メモリの占有を開放する。
            ary_bmp_file.Clear();
            ary_bmp_file = null;

            ary_bmp_byte_head.Clear();
            ary_bmp_byte_head = null;


            //画像の解放
            Array.Clear(bmparr,0, bmparr.Length);
            bmparr = null;
            if (bitmap != null) { bitmap.Dispose(); }
            bitmap  = null;


        }
    }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C#】シンプルな有限ステートマシンの実装。

久しぶりの投稿になります.

実装

有限ステートマシンの実装になります.

StateMachine.cs
/// <summary>
/// 有限ステートマシン(FSM), where T : class
/// </summary>
public class StateMachine<T> 
    where T : class
{
    private T m_Owner;
    private State<T> m_CurrentState;
    public State<T> currentState { get {return m_CurrentState;} set{ m_CurrentState = value;} }
    private State<T> m_PreviousState;
    public State<T> previousState { get {return m_PreviousState;} set{ m_PreviousState = value;} }
    private State<T> m_GlobalState;
    public State<T> globalState { get {return m_GlobalState;} set{ m_GlobalState = value;} }
    /// <summary>
    /// コンストラクタ
    /// </summary>
    public StateMachine(T owner){
        m_Owner = owner;
        m_CurrentState = new NullState<T>();
        m_PreviousState = new NullState<T>();
        m_GlobalState = new NullState<T>();
    }
    /// <summary>
    /// 現在の状態を実行する
    /// </summary>
    public void Update(){
        m_GlobalState.Execute(m_Owner);
        m_CurrentState.Execute(m_Owner);
    }
    /// <summary>
    /// 現在のStateを変更する
    /// </summary>
    public void ChangeState(State<T> newState){
        // Assert(newState != null);
        m_PreviousState = m_CurrentState;
        m_CurrentState.Exit(m_Owner);
        m_CurrentState = newState;
        m_CurrentState.Enter(m_Owner);
    }

    /// <summary>
    /// 前のStateに変更する
    /// </summary>
    public void RevertToPreviousState(){
        ChangeState(m_PreviousState);
    }
}

ステートの実装になります.

State.cs
public interface State<T>
    where T : class
{
    /// <summary>
    /// このStateになった時に呼ばれる
    /// </summary>
    void Enter(T t);
    /// <summary>
    /// このState中はずっと呼ばれる
    /// </summary>
    void Execute(T t);
    /// <summary>
    /// このStateから変わる時に呼ばれる
    /// </summary>
    void Exit(T t);
}

ヌルステートの実装になります.

NullState.cs
/// <summary>
/// null避けのためのクラス
/// </summary>
public class NullState<T>: State<T> 
    where T : class
{
    public void Enter(T t){}
    public void Execute(T t){}
    public void Exit(T t){}
}

使用方法

Main.cs
void main(){
    // 遷移を管理するクラス
    MyClass myClass = new MyClass();
    // ステートマシンの宣言
    StateMachine<MyClass> stateMachine = new StateMachine<MyClass>(myClass);
    // stateインターフェースを実装したクラス
    State<MyClass> myState = new MyState();
    // 現在の状態を変更
    stateMachine.ChangeState(myState);
    // 現在のステートを実行
    stateMachine.Update();
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# - SendInputでマルチバイト文字列を送信する

経緯

MS Wordの書式なしコピーの振る舞いが気に食わなかった(MS Office特有の余計なお世話を色々としてくれる)ので、キー入力を模擬することでプレーンテキストを入力できるようなプログラム素材を作っておこうと思いたった。

キャプチャ

image.png

Send text ボタンを押すと、カウントダウンを開始し、6秒後に直下のテキストボックス内の文字列をSendInputで送信します。(カウントダウン中に、SendInputを流し込みたいウィンドウやコントロールにフォーカスを合わせてください。)

※受け取り側によっては対応していない可能性があります。
ちなみにWordではIMEの状態によって挙動が変わるっぽい。(「あ」(全角)の状態だと、SendInputが行われたあとに、変換候補の選択状態になった。)

ソースコード

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

class MainForm : Form
{
    TextBox txt;
    Button btn;
    System.Windows.Forms.Timer tmr;
    int _counter;

    MainForm()
    {
        _counter = 0;

        tmr = new System.Windows.Forms.Timer();
        tmr.Interval = 200;
        tmr.Tick += (s,e)=>{
            if ( _counter > 0 ) {
                _counter--;
                int inMsec = _counter * tmr.Interval;
                Text = (inMsec/1000).ToString() + "." + ((inMsec%1000)/100).ToString();
            }
            else{
                tmr.Stop();
                txt.ReadOnly = false;
                btn.Enabled = true;
                SendKeyInput(txt.Text);
            }
        };

        btn = new Button(){
            Size = new Size(100,30),
            Text = "Test",
        };
        btn.Click += (s,e)=>{
            txt.ReadOnly = true;
            btn.Enabled = false;
            if ( !tmr.Enabled ) {
                _counter = 30;
                tmr.Start();
            }
        };
        Controls.Add(btn);


        txt = new TextBox(){
            Location = new Point(0, 30),
            Size = new Size(100,30),
            Text = "Test",
        };
        Controls.Add(txt);
    }

    private static class NativeMethods
    {
        [DllImport("user32.dll", SetLastError = true)]
        public extern static void SendInput(int nInputs, Input[] pInputs, int cbsize);

        //[DllImport("user32.dll", EntryPoint = "MapVirtualKeyA")]
        //public extern static int MapVirtualKey(int wCode, int wMapType);

        //[DllImport("user32.dll", SetLastError = true)]
        //public extern static IntPtr GetMessageExtraInfo();
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        public int x;
        public int y;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct MouseInput
    {
        public int X;
        public int Y;
        public int Data;
        public int Flags;
        public int Time;
        public IntPtr ExtraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct KeyboardInput
    {
        public short VirtualKey;
        public short ScanCode;
        public int Flags;
        public int Time;
        public IntPtr ExtraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct HardwareInput
    {
        public int uMsg;
        public short wParamL;
        public short wParamH;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct Input
    {
        public int Type;
        public InputUnion ui;
    }

    [StructLayout(LayoutKind.Explicit)]
    struct InputUnion
    {
        [FieldOffset(0)]
        public MouseInput Mouse;
        [FieldOffset(0)]
        public KeyboardInput Keyboard;
        [FieldOffset(0)]
        public HardwareInput Hardware;
    }

    private const int KEYEVENTF_EXTENDEDKEY = 0x0001;
    private const int KEYEVENTF_KEYUP = 0x0002;
    private const int KEYEVENTF_SCANCODE = 0x0008;
    private const int KEYEVENTF_UNICODE = 0x0004;

    private const int MAPVK_VK_TO_VSC = 0;
    // private const int MAPVK_VSC_TO_VK = 1;

    void SendKeyInput(string s)
    {
        BeginInvoke(
            (MethodInvoker)delegate(){
                SendInputKeyPressAndRelease(s);
            }
        );
    }

    private static void SendInputKeyPressAndRelease(string s)
    {
        Input[] inputs = new Input[2*s.Length];

        for(int k=0; k<s.Length; k++) {
            int tk = s[k];
            //int vsc = NativeMethods.MapVirtualKey(tk, MAPVK_VK_TO_VSC);

            inputs[2*k] = new Input();
            inputs[2*k].Type = 1; // KeyBoard = 1
            inputs[2*k].ui.Keyboard.VirtualKey = 0;
            inputs[2*k].ui.Keyboard.ScanCode = (short)tk;
            inputs[2*k].ui.Keyboard.Flags = KEYEVENTF_UNICODE;
            inputs[2*k].ui.Keyboard.Time = 0;
            inputs[2*k].ui.Keyboard.ExtraInfo = IntPtr.Zero;

            inputs[2*k+1] = new Input();
            inputs[2*k+1].Type = 1; // KeyBoard = 1
            inputs[2*k+1].ui.Keyboard.VirtualKey = 0;
            inputs[2*k+1].ui.Keyboard.ScanCode = (short)tk;
            inputs[2*k+1].ui.Keyboard.Flags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
            inputs[2*k+1].ui.Keyboard.Time = 0;
            inputs[2*k+1].ui.Keyboard.ExtraInfo = IntPtr.Zero;
        }

        NativeMethods.SendInput(inputs.Length, inputs, Marshal.SizeOf(inputs[0]));
    }


    [STAThread]
    static void Main(string[] args)
    {
        Application.Run(new MainForm());
    }
}

参考サイト

https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity(C#)】画像データと一緒に関連するデータを紐づけて保存する

はじめに

【Unity(C#)】テクスチャを画像データに変換して端末に保存、読み込み
↑前回の続きです。

今回は保存した画像データに文字列の情報も付与して保存したいと思います。

考え方

画像と文字列を1つの塊として構造体を定義します。
その構造体をJson形式として保存します。
読み込み時にはその構造体をデシリアライズします。

構造体.png

コード

コードの構成は下記です。
①構造体
②Json変換Utilityクラス
③構造体を保存、読み込みするクラス


構造体

まずは構造体を定義します。
画像のバイナリデータはFile.WriteAllBytesで書き込むので
構造体に持たせるデータは画像データの保存先のパスとしました。

using System;

/// <summary>
/// 画像データの構造体
/// </summary>
[Serializable]
public struct ImageData
{
    /// <summary>
    /// 画像の保存先パス
    /// </summary>
    public string ImageSavePath;

    /// <summary>
    /// 画像の名前
    /// </summary>
    public string ImageName;

    /// <summary>
    /// コンストラクタ
    /// </summary>
    /// <param name="imageSavePath">保存先のパス</param>
    /// <param name="imageName">保存名</param>
    public ImageData(string imageSavePath,string imageName)
    {
        this.ImageSavePath = imageSavePath;
        this.ImageName = imageName;
    }
}

Json変換Utilityクラス

次に構造体をJson形式に変換するクラスを定義します。
【参考リンク】:【Unity(C#)】JsonUtilityを使ったセーブ、ロードの実装

using System;
using System.IO;
using UnityEngine;

/// <summary>
/// Save、Load機能の実装
/// </summary>
public static class JsonDataUtility
{
    /// <summary>
    /// 書き込み機能
    /// </summary>
    /// <param name="imageData">シリアライズするデータ</param>
    public static void Save(ImageData imageData,string path)
    {
        //シリアライズ実行
        string jsonSerializedData = JsonUtility.ToJson(imageData);
        Debug.Log(jsonSerializedData);

        //実際にファイル作って書き込む
        using (var sw = new StreamWriter (path, false)) 
        {
            try
            {
                //ファイルに書き込む
                sw.Write (jsonSerializedData);
            }
            catch (Exception e) //失敗した時の処理
            {
                Debug.Log (e);
            }
        }
    }

    /// <summary>
    /// 読み込み機能
    /// </summary>
    /// <returns>デシリアライズした構造体</returns>
    public static ImageData Load(string path)
    {
        ImageData jsonDeserializedData = new ImageData();

        try 
        {
            //ファイルを読み込む
            using (FileStream fs = new FileStream (path, FileMode.Open))
            using (StreamReader sr = new StreamReader (fs)) 
            {
                string result = sr.ReadToEnd ();
                Debug.Log(result);

                //読み込んだJsonを構造体に入れる
                jsonDeserializedData = JsonUtility.FromJson<ImageData>(result);
            }
        }
        catch (Exception e) //失敗した時の処理
        {
            Debug.Log (e);
        }

        //デシリアライズした構造体を返す
        return jsonDeserializedData;
    }
}

構造体を保存、読み込みするクラス

構造体を保存、読み込みするクラスです。
他にもいろいろやってますが、サンプルなので良しとします。

using System.IO;
using UniRx;
using UniRx.Triggers;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// 構造体を保存、読み込み
/// </summary>
public class UseStructData : MonoBehaviour
{
    [SerializeField] private Button _saveButton;
    [SerializeField] private Button _loadButton;
    [SerializeField] private Button _resetButton;
    [SerializeField] private InputField _inputField;
    [SerializeField] private Text _inputText;
    [SerializeField] private Text _loadText;
    [SerializeField] private Image _paintImage;
    [SerializeField] private Image _loadImage;
    [SerializeField] private Painter _painter;

    private const string IMAGE_SAVE_FOLDER = "Image";
    private const string IMAGE_DATA_SAVE_FOLDER = "ImageData";
    private const string PNG = ".png";
    private const string JSON = ".json";

    ///  /// <summary>
    /// 保存先のパス取得
    /// </summary>
    /// <param name="folderName">区切りのフォルダ名</param>
    /// <returns>保存先のパス</returns>
    private string GetSavePath(string folderName,string fileName,string type)
    {
        string directoryPath = Application.persistentDataPath + "/" + folderName + "/";

        if (!Directory.Exists(directoryPath))
        {
            //まだ存在してなかったら作成
            Directory.CreateDirectory(directoryPath);
            return directoryPath + fileName + type;
        }

        return directoryPath + fileName + type;
    }

    private void Start()
    {
        //セーブボタン
        _saveButton.OnPointerClickAsObservable().Subscribe(_ =>
            {
                SaveImageData("SoftCream");

                //リセット
                _painter.ResetTexture();
                _inputField.text = "";
            })
            .AddTo(this);
        //ロードボタン
        _loadButton.OnPointerClickAsObservable().Subscribe(_ => LoadImageData("SoftCream")).AddTo(this);
        //リセットボタン
        _resetButton.OnPointerClickAsObservable().Subscribe(_ => _painter.ResetTexture()).AddTo(this);;
    }

    /// <summary>
    /// 構造体をシリアライズ
    /// </summary>
    private void SaveImageData(string fileName)
    {
        string pngPath = GetSavePath(IMAGE_SAVE_FOLDER, fileName, PNG);
        string jsonPath = GetSavePath(IMAGE_DATA_SAVE_FOLDER, fileName, JSON);

        //構造体にパス、名前を入れる これでひとつの塊
        ImageData imageData =new ImageData(pngPath,_inputText.text);
        //Pngに変換
        byte[] bytes = _paintImage.sprite.texture.EncodeToPNG();
        //保存
        File.WriteAllBytes(pngPath, bytes);
        //構造体をJsonに変換
        JsonDataUtility.Save(imageData,jsonPath);
    }

    /// <summary>
    /// テクスチャに変換&読み込み
    /// </summary>
    private void LoadImageData(string fileName)
    {
        string jsonPath = GetSavePath(IMAGE_DATA_SAVE_FOLDER, fileName, JSON);
        ImageData imageData = JsonDataUtility.Load(jsonPath);
        //読み込み
        byte[] bytes = File.ReadAllBytes(imageData.ImageSavePath);
        //画像をテクスチャに変換
        Texture2D loadTexture = new Texture2D(2, 2);
        loadTexture.LoadImage(bytes);
        //テクスチャをスプライトに変換
        _loadImage.sprite = Sprite.Create(loadTexture, new Rect(0, 0, loadTexture.width, loadTexture.height), Vector2.zero);
        //画像の名前を表示
        _loadText.text = imageData.ImageName;
    }
}

Directory.Exists

新しくディレクトリを作って保存先のパスとして利用する方法が地味に詰まりました。
Directory.Existsでディレクトリが存在しているか確認して
分岐処理を用意しないとエラーが出ました。

        if (!Directory.Exists(directoryPath))
        {
            //まだ存在してなかったら作成
            Directory.CreateDirectory(directoryPath);
            return directoryPath + fileName + type;
        }

デモ

上品なイラストとそのイラストの名前として入力した文字列を保存し、
読み込むことに成功しました。

Qiita画像保存2.gif

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C#でMySQLを使う - 1.Visual Studio 2019+MySQL 5.7 Connectorsインストール編

前提と準備

C#の記事

最近私がプライベートで、三浦半島の一眼レフ写真や小さなアトラクションを通し、多くの方が自然と歴史などを通して感じていかれるWebサイトを制作しておりまして、ロリポップ!レンタルサーバーにPHP+MySQLを使用しています(*´꒳`*)

そのMySQLのデータ登録ですが、インターネットでPHPからMySQLを登録できるとなると、PHP側で登録用のアカウントを作らなければならなかったり、ログインなどのセキュリティを強固にする必要があっても、やはりインターネット上である以上は限度があるため、ローカルでMySQLにデータ登録し、その生成したSQLパッチを運用中のサーバーに適用しています。そうなるとインターネット上に管理画面を作る必要がなく、多少はマシになるものの、ローカルでMySQLデータ登録のプログラムを作成しなければなりません。

そこで、私の場合は、WindowsプログラムをC#で制作し、そこからMySQLに接続し、SQLパッチをテキストとして生成するというやり方を採っています。その際にどうしても必要なのは、C#(.NET)からMySQLに接続できるための外部ライブラリを使い、Visual Studioには標準で用意されていないため、サードパーティー製のコネクタを用意する必要があったので、インストールしました(˶ ・ᴗ・ )੭

環境

  • OS:Windows 10 Pro (インストール再現のためWindowsサンドボックス使用)
  • 開発環境:Visual Studio 2019
  • データベース:MySQL 5.7

前提

特になし

作業手順

今回は、開発環境側でインストールをしてみます。私のメインの環境にすでにC#でMySQLが使える状態だったので、インストールはWindowsサンドボックスで再現することにしました(* ॑꒳ ॑* )⋆*

Visual StudioとMySQLのインストール

Visual Studio 2019のインストール

Visual Studioは短期間に2015→2017→2019…とサイクル期間が短いようですが、たぶん他のVisual Studioのバージョンでもいけると思いますが、私は2019を使っています

Visual Studio 2019

そのままデフォルトでインストールしました( ˙꒳​˙ᐢ )
ちなみにC#を使う項目はすべてインストール選択しています

MySQLのインストール

MySQL 8.0はLinuxでサーバー構築で使いまくったけど、Windows用に置いてあるのが5.7だったので、5.7の方を使いました

MySQL 5.7

「Developer Default」を選択してインストール。開発向けデフォルトでは、画面のように「MySQL Connectors」が項目としてピックアップされていて、Javaや.NET、C/C++などに対応しています。

Visual Studioで使用する

参照を追加してみる

Visual Studioを起動して、新規のC#フォームアプリケーションプロジェクトを作成。
そうすると、ソリューションエクスプローラーに「参照」があるので、参照を追加して「参照マネージャー」を開く。

MySql.Data

「アセンブリ」の中に実際自動で認識してくれるので、参照マネージャーで、右上の検索画面に「mysql」を入力すると、↑の画面のように「MySql.Data」が何行も出てくるので、とりあえず1つだけ、どれでもいいので選択して(行左にマウスを当てると出てくるチェックボックスをON)「OK」を選択すると…

MySQLを使う

MySql.Data」が追加されました(*˘꒳˘*)
この中にMySQLを扱うC#オブジェクトが入っているのです。

ソースで記述する

実際にソースで使うには、最初に「using」で指定しておくと便利だったので、私はそうしました

sample1.cs
using System;
using System.text;
(中略)

using MySql.Data.MySqlClient;   // これがMySQLを扱うためのオブジェクトを含んでいる

namespace WindowsForms1
{
    class sample1
    {
        public void sampleMethod()
            try {
                // 接続情報を文字列で与える
                string connStr = "server=127.0.0.1;user id=test;password=test1;database=manutest";
                MySqlConnection conn = new MySqlConnection(connStr);

                // 接続を開く
                conn.Open();

                // ここにSQLの処理を書く

                // 接続を閉じる
                conn.Close();
            }
            catch (MySqlException mse)
            {
                // MySqlの接続エラー処理をここに書く
            }
        }
    }
}

こういう流れでソースを書いていくもので、実際にMySQL ConnectorsのC#に関する公式の根拠が出回っていなかったので、第三者の情報と実際Visual Studioのコーディングでポップアップされるトピックを頼りにするしかなかったのです…( ´ •̥ ̫ •̥ ` )

using

MySQL接続オブジェクトが認識できた

実際エラーがなく基本的なコーディングの流れはつかめた(˶ ・ᴗ・ )੭⚐⚑

次回

Visual StudioにMySQLを導入できたので、次は実際にMySQLに接続して、データのやりとりも記事にまとめる予定です٩(.› ‹.♡)۶

参考文献

 1. MySQL Connector/NETを使ってみよう
 2. C#からMySQLに接続する - 電脳産物
 3. 【Visual Studio】C#からMySqlの使い方 接続するには?SQL文を実行するには? - 困ったー
 4. C#でMySQLからSELECTした結果を取り出したい

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む