差分

ナビゲーションに移動 検索に移動

C♯のモジュールからC++のDLLを呼び出してみる

2,252 バイト除去, 2019年7月26日 (金) 16:46
「外部からSSH接続したい」でも「IPアドレスが半固定」しかも「IPv6プラス」ってなると、なかなかIPv6に対応したDDNS(ダイナミックドメインネームサーバー)が見つからないです。<br/>今のところメジャーなのが「[https://i.open.ad.jp/ OPEN IPv6 ダイナミック DNS for フレッツ・光ネクスト]」みたいなので、これを利用して外部から接続できるようにしてみたいと思います。久しぶりにやったら、すっかり忘れていたので覚書…
== 現在のIPv6アドレスの確認 C++のDLLプロジェクト作成 ==外部に公開したいサーバーのIPv6アドレスを調べます。まず、C++のDLLを作成するときの注意点…作成するプロジェクトは、「Win32プロジェクト」を選択するんだ。<br/>googleで調べると「ifconfigで調べる」というのがたくさんヒットしますが、Debian9.9では「ifconfig」が非推奨になってしまったので、代わりに「ip」で調べます。(Raspbianは「ifconfig」で調べられます)<syntaxhighlight lang="bash">ip a</syntaxhighlight>で、以下のような結果が得られるかと思います。<syntaxhighlight lang="text">1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00[[ファイル:00 inet 127CShapeToCppDll-005.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever2: enxAABBCCDDEEFF: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether AA:BB:CC:DD:EE:FF brd ff:ff:ff:ff:ff:ff inet 192.168.1.2/24 brd 192.168.1.255 scope global enxAABBCCDDEEFF valid_lft forever preferred_lft forever inet6 HHHH:IIII:JJJJ:KKKK:LLLL:MMMM:NNNN:OOOO/64 scope global noprefixroute dynamic valid_lft 86398sec preferred_lft 14398sec inet6 PPPP:QQQQ:RRRR:SSSS:TTTT:UUUU/64 scope link valid_lft forever preferred_lft forever</syntaxhighlight>「inet6」の「scope global」となっている「HHHH:IIII:JJJJ:KKKK:LLLL:MMMM:NNNN:OOOO」がグローバルIPv6アドレスとなります。<br/>(セキュリティ的には危険ですが、試しにルーターの設定を「パススルーを許可する」にして、このアドレスにSSH接続してみると接続できるかと思います。もちろんアドレスが変わってしまえば接続できなくなりますが…)jpg]]
== 「OPEN IPv6 ダイナミック DNS for フレッツ・光ネクスト」に登録 ==
次に「OPEN IPv6 ダイナミック DNS for フレッツ・光ネクスト」に登録していきます。<br/>まずは「[https://i.open.ad.jp/ OPEN IPv6 ダイナミック DNS for フレッツ・光ネクスト]」にアクセスします。<br/>すると、画面下部に以下のような項目があるかと思いますので、入力していきます。<br/>[[ファイル:Ddns-setting-001.png | 400px]]
* ホスト名:任意のホスト名を入力しますあと…アプリケーションの設定では「DLL」と「空のプロジェクト」を選択してね。<br/>* 初期IPv6アドレス(オプション):前段で取得したIPv6アドレスを入力します* メールアドレス:ホストキー(後述します)紛失時に使用します。(登録しておいたほうが安全です)[[ファイル:CShapeToCppDll-001.jpg]]<br/>
入力し終わったら「作成」ボタンをクリックします。<br/>「新しい DDNS ホスト 「example.i.open.ad.jp」 の作成が完了しました。」と表示されればOKです。<br/>「OK」ボタンをクリックします。
すると以下の様な一覧が表示されます。(横長ではみ出てますwww)<br/>[[ファイル:Ddns-setting-002空のプロジェクトが作成されたら「cpp」「h」「def」ファイルを追加するんだ。今回は「CppDll.png]]<br/>* ホスト名(FQDN):IPアドレスの代わりに使うホスト名になります* IPv6アドレス:上の画像では「::1」と表示されてしまっていますが、実際は現在のIPv6アドレスが表示されます* ホストキー情報:ホストキーと合わせて、更新用の「ホスト名」「アドレス」「URL」が表示されています* 登録メールアドレス:登録時に入力したメールアドレスが表示されています* クエリ回数~操作:省略cpp」「CppDll.h」「CppDll.def」を追加したよ。
ちなみに、登録したWEBブラウザで「[https://i.open.ad.jp/ OPEN IPv6 ダイナミック DNS for フレッツ・光ネクスト]」を開くと登録したホスト一覧が表示されるのですが、別のWEBブラウザで開くとホスト一覧が表示されません…。<br/>そんなときは、「過去に作成した DDNS ホストをリストに追加」機能を利用して、表示させたい「ホストキー」を入力するとこで表示できるようになります。
また、その下の更新用「ホスト名」「アドレス」「URL」ですが、公開したいサーバーのIPv6アドレスが変わってしまったときに、公開したいサーバーから「ホスト名」「アドレス」にpingや「URL」にアクセスするとIPv6アドレスが最新の情報に更新されます。そしたら、プロジェクトのプロパティを開いて「構成プロパティ→リンカー→入力→モジュール定義ファイル」に「CppDll.def」を設定するんだ。<br/>(DebugとReleaseでそれぞれ設定する必要があるんだよ。)<br/>[[ファイル:CShapeToCppDll-002.jpg]]<br/>(ルーターからやっちゃダメですよ…ルーターのIPv6アドレスに書き換わっちゃうので…私はしばらくルーターからやってましたwww)
登録は以上です。== DLLのコード ===== ヘッダーファイル(*.h) ===<syntaxhighlight lang="cpp">#ifndef DLLAPI#define DLLAPI extern "C" __declspec(dllimport)#endif
== systemdでIPv6の更新をする ==DLLAPI long __stdcall _Sum(const long p_Number1, const long p_Number2);systemdを使用して、一定間隔で「アドレス」にpingをするようにしてみます。<br/>先ずは、公開したいサーバーにログインして、以下のコマンドを実行します。<syntaxhighlight lang="bash">sudo nano /etc/systemd/system/ddns-update.service</syntaxhighlight>新しくファイルを作成しているので、中身はなにもないはずです。<br/>ちなみに「/etc/systemd/system」ディレクトリは「systemd」で動かすサービスファイル置き場みたいです。<br/>つまり今回は「ddns-update」サービスを作成していきます。
次にファイルの中身ですが、以下の様にします。=== コードファイル(*.cpp) ===<syntaxhighlight lang="textcpp">[Unit]Description=ddns ipv6 update#define DLLAPI
[Service]Type=simpleExecStart=/bin/ping6 -c 1 [更新用IPv6アドレス]</syntaxhighlight>[更新用IPv6アドレス]は先程の一覧の「更新用アドレス」に置き換えてください。<br/>上記の一覧の例を反映すると…<syntaxhighlight lang=#include "bashCppDll.h">[Unit]Description=ddns ipv6 update
[Service]DLLAPI long __stdcall _Sum(const long p_Number1, const long p_Number2)Type=simple{ return p_Number1 + p_Number2;}ExecStart=/bin/ping -c 1 2409:11:c0e0:468:6b16:a7eb:503e:c23b</syntaxhighlight>となります。
「-c 1」は1回実行という意味です。=== モジュール定義ファイル(*.def) ===<br/syntaxhighlight lang="text">「Description」は好きな内容に変更しても大丈夫です。LIBRARY CppDll
次にこれを一定間隔で起動するタイマーを作成します。<br/>以下のコマンドを入力してください。EXPORTS<syntaxhighlight lang="bash">sudo nano /etc/systemd/system/ddns-update.timer</syntaxhighlight> _Sumこれも新規作成です。<br/>内容は以下の様にします。<syntaxhighlight lang="text">[Unit]Description=ddns ipv6 update timer
[Timer]== C♯のプロジェクト作成 ==OnCalendar=*-*-* *:00:00ほとんどそのまま作るんだけど…ソリューションのコンパイル対策をしておくよ。
[Install]=== ビルドイベントの設定 ===WantedBy=timers.targetC++のDLLはソリューションフォルダ直下の「Debug」や「Release」フォルダにDLLが格納されてしまうんだ。そうすると、デバッグするときにDLLが見つからないので、ビルドイベントを使ってコピーしてしまうよ。以下のように設定してね。(「Release」コンパイルするまでは「Release」フォルダがないのでコメントアウトしているよ)<br/syntaxhighlight>内容的には「ddns[[ファイル:CShapeToCppDll-update004.serviceを毎時0分0秒に起動する」と言って感じです。jpg]]<br/>「ddns-update.service」ファイルと同様に「Description」は好きな内容に変更しても大丈夫です。
準備ができたので、動かしてみます。<br/>動かすときは以下のコマンドを実行します。=== プロジェクトの依存関係の設定 ===コピーするにもちゃんとリコンパイルされた資源をコピーしないといけないので、プロジェクトの依存関係を設定することで、ビルドの順番を設定するよ。<syntaxhighlight lang="bash">sudo systemctl start ddns-update.timer<Br/syntaxhighlight>エラーが出なければOKです。ソリューションエクスプローラーからC♯のプロジェクトを右クリックして「ビルド依存関係」→「プロジェクト依存関係」を選択してね。<br/>エラーが出たときは内容を確認してください。<br/>内容が合っているのにエラーになるときは…「nano」上で改行をし直してみてください。<br/>(私は改行コードの違いでエラーが出ていることに気づくまで、結構時間がかかってしまいました)「依存関係」タブの依存先にC++のプロジェクトが表示されているはずだから、チェックを入れてOKボタンをクリックしてね。
正常に動いたなら、今度は起動時に動くようにします。<br/>以下のコマンドで起動時に動くようになります。== C♯のコード ==MVVMモデルでサンプルを作ったからビューモデルが入っているけど…DLLを呼ぶには必要ないから無視してね。=== モデル ===<syntaxhighlight lang="bashC#">sudo systemctl enable ddns-updateusing 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.timer</syntaxhighlight>Windows.Input;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Navigation;using System.Windows.Shapes;
ちなみに、止めたいときは…//追加using System.Runtime.InteropServices; namespace CSharpToCDLL{ /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : 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 _Sum(Int32 p_Number1,Int32 p_Number2);  /// <summary> /// 標準のコンストラクタ /// </summary> public MainWindow() { InitializeComponent(); }  /// <summary> /// ボタンクリックイベントハンドラ /// </summary> /// <syntaxhighlight langparam name="bashsender">sudo systemctl stop ddns-updateイベント送信元</param> /// <param name="e">イベント情報</param> private void Button_Click(object sender, RoutedEventArgs e) { //DLLの関数を呼び出す Int32 l_Result = _Sum(300, 500);  //計算結果の表示 MessageBox.timerShow("計算結果:" + l_Result.ToString()); } }}</syntaxhighlight>
起動時に動かないようにするには…=== ビュー ===<syntaxhighlight source lang="bashxml"><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}"/>sudo systemctl disable ddns-update <Button Grid.Row="1" Grid.timerColumn="3" Content="実行" Click="Button_Click"/> </Grid></Window></syntaxhighlightsource>=== ビューモデル ===<source lang="csharp">using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;
です。//追加using System.ComponentModel;
namespace CSharpToCDLL
{
class MainWindowViewModel : INotifyPropertyChanged
{
/// <summary>
/// ラベル表示用変数
/// </summary>
private String m_Label_Content;
ここで少し疑問に思う人がいるかも知れません… /// <brsummary> /// ラベル表示文字列 />「ddns-update.service」と「ddns-update.timer」と実行コマンドの「sudo systemctl start ddns-update」の3つを関連付ける設定をしていません。<br/>実は単純な話で、ファイル名だけで関連付いています。<br/>「ddns-update.service」と「ddns-update.timer」は「ddns-update」が同じになっています。<br/summary>これで「* public String Label_Content { set { this.service」ファイルと「*.timer」ファイルはセットだと認識しています。<br/>また実行コマンドは「ddns-updatem_Label_Content = value; this.timer」と指定するだけで「*OnPropertyChanged("Label_Content"); } get { return this.service」ファイルを探しに行きます。m_Label_Content; } }
== ルーターの設定 == /// <summary>「[[「OPEN IPv6 ダイナミック DNS for フレッツ・光ネクスト」の利用 /// プロパティ変更イベントハンドラ ///ArchLinux#ルーターの設定 | PiVPNによるOpenVPNのインストール#ルーターの設定]]」に書いているので、そちらを参照してください。<br/summary>sshのデフォルトポートを開放したいなら22番ポートを開放してください(22番ポートを開放するのは危険だと思いますが…) public event PropertyChangedEventHandler PropertyChanged;
= 参考サイト = /// <summary> /// プロパティ変更通知 /// </summary>[https: //i/ <param name="p_PropertyName">プロパティ名</param> public void OnPropertyChanged(String p_PropertyName) { if (this.openPropertyChanged != null) { this.ad.jpPropertyChanged(this, new PropertyChangedEventArgs(p_PropertyName)); } } }}</ OPEN IPv6 ダイナミック DNS for フレッツ・光ネクスト]source>
[[Category:ArchLinuxC♯]][[Category:DDNSC++]][[Category:dll]]

案内メニュー