差分

ナビゲーションに移動 検索に移動
型付DataSetに列挙型(enum)を取り込む場合、色々な方法があると思うんだ。一番簡単に想像できるのは、DataSetデザイナーで特定のテーブルの列の型を、ネームスペース含めて列挙型をしてしまうことなんだけど…。僕がやった限り、一回これをやってしまうとデータセットデザイナーが正しく開けなくなっちゃうんだよ。で、色々調べて・試してその結果たどり着いた(僕が)最善と思っている方法を紹介するよ。久しぶりにやったら、すっかり忘れていたので覚書…
== 考え方 C++のDLLプロジェクト作成 ==色々やった結果…実はDataSetに列挙型を取り込むこと自体を諦めたんだよ…(^_^;)でも、コード上で毎回キャストしたくはなかったんだ…。そこで注目したのは、自動生成される型付DataSetはpartialクラスってことなんだ…。つまり、DataSetの定義は「Int32」でしておいて、DataSetのpartialを作って、そのクラスに列挙型で出し入れできるプロパティを作ってしまおうって事なんだよ。まず、C++のDLLを作成するときの注意点…作成するプロジェクトは、「Win32プロジェクト」を選択するんだ。<br/>[[ファイル:CShapeToCppDll-005.jpg]]
=== partialクラスってなに??? ===
partialクラスって言うのは、クラス定義を分割して定義している(ないしはできる)クラスのことを言うんだよ。たとえば、以下のようなクラスがあったとするよね…
あと…アプリケーションの設定では「DLL」と「空のプロジェクト」を選択してね。<br/>[[ファイル:CShapeToCppDll-001.jpg]]<br/>  空のプロジェクトが作成されたら「cpp」「h」「def」ファイルを追加するんだ。今回は「CppDll.cpp」「CppDll.h」「CppDll.def」を追加したよ。  そしたら、プロジェクトのプロパティを開いて「構成プロパティ→リンカー→入力→モジュール定義ファイル」に「CppDll.def」を設定するんだ。<br/>(DebugとReleaseでそれぞれ設定する必要があるんだよ。)<br/>[[ファイル:CShapeToCppDll-002.jpg]]<br/> == DLLのコード ===== ヘッダーファイル(*.h) ===<syntaxhighlight lang="C#cpp">namespace CommonCtrlLib#ifndef DLLAPI{#define DLLAPI extern "C" __declspec(dllimport) public class Class1#endif { private Int32 m_field_ADLLAPI long __stdcall _Sum(const long p_Number1, const long p_Number2);</syntaxhighlight>
public Class1() {=== コードファイル(*.cpp) === this.m_field_A <syntaxhighlight lang= -1;"cpp"> }#define DLLAPI
public Int32 field_A { get { return this#include "CppDll.m_field_A; } set { this.m_field_A = value; } }h"
public String field_BDLLAPI long __stdcall _Sum(const long p_Number1, const long p_Number2) { get { return this.m_field_A.ToString(); } set { this.m_field_A = Int32.Parse(value)p_Number1 + p_Number2; } } }
}
</syntaxhighlight>
クラス内の定義って言うのはclass{}の中に記述しないといけないから、つまるところ1ファイルの中にすべて書かないといけないって事になっちゃうんだよね。でも、諸事情により別のファイルに書きたいときもあると思うんだ。自動生成されたDataSetのファイルなんかは特にそうで…自動生成されたファイルをカスタマイズしちゃうと、また自動生成したときにカスタマイズが消えちゃったりするんだ。そんなときに、自分のカスタマイズ部分だけ別のファイルに書ければ…ってことになるんだよ。=== モジュール定義ファイル(*.def) ===<syntaxhighlight lang="text">LIBRARY CppDll EXPORTS _Sum</syntaxhighlight> == C♯のプロジェクト作成 ==ほとんどそのまま作るんだけど…ソリューションのコンパイル対策をしておくよ。 === ビルドイベントの設定 ===C++のDLLはソリューションフォルダ直下の「Debug」や「Release」フォルダにDLLが格納されてしまうんだ。そうすると、デバッグするときにDLLが見つからないので、ビルドイベントを使ってコピーしてしまうよ。以下のように設定してね。(「Release」コンパイルするまでは「Release」フォルダがないのでコメントアウトしているよ)<br/>[[ファイル:CShapeToCppDll-004.jpg]]<br/> === プロジェクトの依存関係の設定 ===コピーするにもちゃんとリコンパイルされた資源をコピーしないといけないので、プロジェクトの依存関係を設定することで、ビルドの順番を設定するよ。<Br/>ソリューションエクスプローラーからC♯のプロジェクトを右クリックして「ビルド依存関係」→「プロジェクト依存関係」を選択してね。<br/>「依存関係」タブの依存先にC++のプロジェクトが表示されているはずだから、チェックを入れてOKボタンをクリックしてね。 == C♯のコード ==MVVMモデルでサンプルを作ったからビューモデルが入っているけど…DLLを呼ぶには必要ないから無視してね。=== モデル ===<source lang="csharp">using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Navigation;using System.Windows.Shapes;
そんなときはclassをpartial型で定義するとできちゃうんだよ。//追加using System.Runtime.InteropServices;
ファイル1<syntaxhighlight lang="C#">namespace TestLibraryCSharpToCDLL
{
/// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class Class1MainWindow : Window
{
/// <summary> /// DLLの関数定義 /// </summary> /// <param name="p_Number1">数値1</param> /// <param name="p_Number2">数値2</param> /// <returns>合計</returns> [DllImport("CppDll.dll")] private extern static Int32 m_field_A_Sum(Int32 p_Number1,Int32 p_Number2);
/// <summary> /// 標準のコンストラクタ /// </summary> public Class1MainWindow()
{
this.m_field_A = -1InitializeComponent();
}
public Int32 field_A/// <summary> /// ボタンクリックイベントハンドラ /// </summary> /// <param name="sender">イベント送信元</param> /// <param name="e">イベント情報</param> private void Button_Click(object sender, RoutedEventArgs e)
{
get { return this.m_field_A//DLLの関数を呼び出す Int32 l_Result = _Sum(300, 500); }  set { this//計算結果の表示 MessageBox.Show("計算結果:" + l_Result.m_field_A = valueToString()); }
}
}
}
</syntaxhighlightsource>ファイル2=== ビュー ===<syntaxhighlight source lang="C#xml"><Window x:Class="CSharpToCDLL.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="77.056" Width="525"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="5"/> <RowDefinition Height="24"/> <RowDefinition/> <RowDefinition Height="5"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="5"/> <ColumnDefinition/> <ColumnDefinition Width="5"/> <ColumnDefinition Width="75"/> <ColumnDefinition Width="5"/> </Grid.ColumnDefinitions> <Label Grid.Row="1" Grid.Column="1" Content="{Binding Label_Content}"/> <Button Grid.Row="1" Grid.Column="3" Content="実行" Click="Button_Click"/> </Grid></Window></source>=== ビューモデル ===<source lang="csharp">using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks; //追加using System.ComponentModel; namespace TestLibraryCSharpToCDLL
{
public partial class Class1MainWindowViewModel : INotifyPropertyChanged
{
/// <summary> /// ラベル表示用変数 /// </summary> private String m_Label_Content;  /// <summary> /// ラベル表示文字列 /// </summary> public String field_BLabel_Content
{
get set { return this.m_field_Am_Label_Content = value; this.ToStringOnPropertyChanged("Label_Content"); } set get { return this.m_field_A = Int32.Parse(value)m_Label_Content; }
}
}
}
</syntaxhighlight>
 
こうやって定義すると、2つのファイルに分かれているclass定義でも、1つのclassとして扱うことができるんだ。
 
== 型付DataSetの作成 ==
列挙型を取り込みたいって悩んでいる人には、このステップは用済みかもしれないけど一応書いておくね。VisualStudioを起動してプロジェクトを開いている前提で手順をあげるよ。
 
<ol>
<li>ソリューションエクスプローラーからDataSetを追加したいプロジェクトを右クリックする。</li>
<li>メニューから「追加」→「新しい項目」を選択する。</li>
<li>(C♯の)一覧からデータセットを選択し、名前に適当なファイル名を入れ、「追加」ボタンをクリックする。</li>
<li>デザイナーが開くので、ツールボックスからDataTableをデザイナーにドラッグアンドドロップしてテーブルを追加する。テーブル名は適当に変更する。</li>
<li>デザイナーのテーブルを右クリックして、「追加」→「列」を選択し、適当な列を追加する。</li>
<li>必要に応じてキーを設定する</li>
</ol>
 
ここで、列挙型を保存したい列の型を「System.Int32」に変更するんだよ。
デザイナーで列を選択すると、プロパティウインドウに列のプロパティが表示されるんだ。その中の「DataType」プロパティの値を「System.Int32」に変更するんだよ。
 
== partialクラスの追加 ==
partialクラスを追加する方法もインターネットを調べると色々出てくるんだけど…どうやらMicrosoftはこの方法を推奨しているみたいなんだよ。
<ol>
<li>データセットデザイナを表示する。</li>
<li>データセットデザイナの何も書かれていないところ(余白?)をダブルクリックする。</li>
<li>以下のようなコードが表示される(ネームスペースが「TestLibrary」で、データセットクラス名が「DataSet1」の場合)
<syntaxhighlight lang="C#">
namespace TestLibrary {
public partial class DataSet1 {
}
}
</syntaxhighlight>
</li>
</ol>
これでDataSetのpartialクラスが追加されたんだよ。ソリューションエクスプローラーを見ても「xxxxx.xsd」ファイルの配下にこのpartialクラスのファイルが入っていて、見た目にもすっきりしていると思うんだ。
== partialクラスにメソッドを追加 == /// <summary>作成されたpartialクラスに列挙型で出し入れできる、プロパティを追加するよ。手順とかじゃなくて、追加したpartialクラスを以下の様に修正するんだよ。 /// プロパティ変更イベントハンドラ;namespace:TestLibrary;DataSet名:DataSet1;DataTable名:DataTable1;DataColumn名:DataColumn1 /// </summary> public event PropertyChangedEventHandler PropertyChanged;列挙型名:e_Test
/// <source langsummary> /// プロパティ変更通知 /// </summary> /// <param name="csharpp_PropertyName">プロパティ名</param>namespace TestLibrary { public partial class DataSet1 { partial class DataTable1Rowvoid OnPropertyChanged(String p_PropertyName)
{
public e_Test EnumDataColumn1if (this.PropertyChanged != null)
{
get { return (e_Test)this.DataColumn1; } set { PropertyChanged(this.DataColumn1 = , new PropertyChangedEventArgs(Int32p_PropertyName))value; }
}
}
}
</source>
 
これで、列挙型で出し入れできるメソッドを追加することができたよ。もしWPFでバインドしたいときなどは、Int32で取り出すメソッド(この場合「DataColumn1」)でバインドすれば、特に悩むことなく実現できると思うんだ。
 
== 参考サイト ==
[https://msdn.microsoft.com/ja-jp/library/ms171896.aspx MSDN - 方法 : データセットの機能を拡張する]<br/>
[http://gushwell.ldblog.jp/archives/50810947.html Gushwell's C# Dev Notes - DataSetのカラムで列挙型を扱うには]
[[Category:C♯]]
[[Category:DatasetC++]][[Category:dll]]

案内メニュー