ページ「「OPEN IPv6 ダイナミック DNS for フレッツ・光ネクスト」の利用/ArchLinux」と「C♯のモジュールからC++のDLLを呼び出してみる」の間の差分

提供: とある社畜の頭脳整理
(ページ間の差分)
ナビゲーションに移動 検索に移動
 
 
1行目: 1行目:
「外部からSSH接続したい」でも「IPアドレスが半固定」しかも「IPv6プラス」ってなると、なかなかIPv6に対応したDDNS(ダイナミックドメインネームサーバー)が見つからないです。<br/>今のところメジャーなのが「[https://i.open.ad.jp/ OPEN IPv6 ダイナミック DNS for フレッツ・光ネクスト]」みたいなので、これを利用して外部から接続できるようにしてみたいと思います。
+
久しぶりにやったら、すっかり忘れていたので覚書…
  
== 現在のIPv6アドレスの確認 ==
+
== C++のDLLプロジェクト作成 ==
外部に公開したいサーバーのIPv6アドレスを調べます。<br/>googleで調べると「ifconfigで調べる」というのがたくさんヒットしますが、Debian9.9では「ifconfig」が非推奨になってしまったので、代わりに「ip」で調べます。(Raspbianは「ifconfig」で調べられます)
+
まず、C++のDLLを作成するときの注意点…
<syntaxhighlight lang="bash">ip a</syntaxhighlight>
+
作成するプロジェクトは、「Win32プロジェクト」を選択するんだ。<br/>
で、以下のような結果が得られるかと思います。
+
[[ファイル:CShapeToCppDll-005.jpg]]
<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 127.0.0.1/8 scope host lo
 
      valid_lft forever preferred_lft forever
 
    inet6 ::1/128 scope host
 
      valid_lft forever preferred_lft forever
 
2: 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接続してみると接続できるかと思います。もちろんアドレスが変わってしまえば接続できなくなりますが…)
 
  
== 「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/>
+
空のプロジェクトが作成されたら「cpp」「h」「def」ファイルを追加するんだ。今回は「CppDll.cpp」「CppDll.h」「CppDll.def」を追加したよ。
[[ファイル:Ddns-setting-002.png]]<br/>
 
* ホスト名(FQDN):IPアドレスの代わりに使うホスト名になります
 
* IPv6アドレス:上の画像では「::1」と表示されてしまっていますが、実際は現在のIPv6アドレスが表示されます
 
* ホストキー情報:ホストキーと合わせて、更新用の「ホスト名」「アドレス」「URL」が表示されています
 
* 登録メールアドレス:登録時に入力したメールアドレスが表示されています
 
* クエリ回数~操作:省略
 
  
ちなみに、登録したWEBブラウザで「[https://i.open.ad.jp/ OPEN IPv6 ダイナミック DNS for フレッツ・光ネクスト]」を開くと登録したホスト一覧が表示されるのですが、別のWEBブラウザで開くとホスト一覧が表示されません…。<br/>そんなときは、「過去に作成した DDNS ホストをリストに追加」機能を利用して、表示させたい「ホストキー」を入力するとこで表示できるようになります。
 
  
また、その下の更新用「ホスト名」「アドレス」「URL」ですが、公開したいサーバーのIPv6アドレスが変わってしまったときに、公開したいサーバーから「ホスト名」「アドレス」にpingや「URL」にアクセスするとIPv6アドレスが最新の情報に更新されます。<br/>(ルーターからやっちゃダメですよ…ルーターのIPv6アドレスに書き換わっちゃうので…私はしばらくルーターからやってましたwww)
+
そしたら、プロジェクトのプロパティを開いて「構成プロパティ→リンカー→入力→モジュール定義ファイル」に「CppDll.def」を設定するんだ。<br/>
 +
(DebugとReleaseでそれぞれ設定する必要があるんだよ。)<br/>
 +
[[ファイル:CShapeToCppDll-002.jpg]]<br/>
  
登録は以上です。
+
== 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>
<syntaxhighlight lang="bash">sudo nano /etc/systemd/system/ddns-update.service</syntaxhighlight>
 
新しくファイルを作成しているので、中身はなにもないはずです。<br/>ちなみに「/etc/systemd/system」ディレクトリは「systemd」で動かすサービスファイル置き場みたいです。<br/>つまり今回は「ddns-update」サービスを作成していきます。
 
  
次にファイルの中身ですが、以下の様にします。
+
=== コードファイル(*.cpp) ===
<syntaxhighlight lang="text">[Unit]
+
<syntaxhighlight lang="cpp">
Description=ddns ipv6 update
+
#define DLLAPI
  
[Service]
+
#include "CppDll.h"
Type=simple
 
ExecStart=/bin/ping6 -c 1 [更新用IPv6アドレス]</syntaxhighlight>
 
[更新用IPv6アドレス]は先程の一覧の「更新用アドレス」に置き換えてください。<br/>上記の一覧の例を反映すると…
 
<syntaxhighlight lang="bash">[Unit]
 
Description=ddns ipv6 update
 
  
[Service]
+
DLLAPI long __stdcall _Sum(const long p_Number1, const long p_Number2)
Type=simple
+
{
ExecStart=/bin/ping -c 1 2409:11:c0e0:468:6b16:a7eb:503e:c23b</syntaxhighlight>
+
return p_Number1 + p_Number2;
となります。
+
}
 +
</syntaxhighlight>
  
「-c 1」は1回実行という意味です。<br/>「Description」は好きな内容に変更しても大丈夫です。
+
=== モジュール定義ファイル(*.def) ===
 +
<syntaxhighlight lang="text">
 +
LIBRARY CppDll
  
次にこれを一定間隔で起動するタイマーを作成します。<br/>以下のコマンドを入力してください。
+
EXPORTS
<syntaxhighlight lang="bash">sudo nano /etc/systemd/system/ddns-update.timer</syntaxhighlight>
+
_Sum
これも新規作成です。<br/>
+
</syntaxhighlight>
内容は以下の様にします。
 
<syntaxhighlight lang="text">[Unit]
 
Description=ddns ipv6 update timer
 
  
[Timer]
+
== C♯のプロジェクト作成 ==
OnCalendar=*-*-* *:00:00
+
ほとんどそのまま作るんだけど…ソリューションのコンパイル対策をしておくよ。
  
[Install]
+
=== ビルドイベントの設定 ===
WantedBy=timers.target</syntaxhighlight>
+
C++のDLLはソリューションフォルダ直下の「Debug」や「Release」フォルダにDLLが格納されてしまうんだ。そうすると、デバッグするときにDLLが見つからないので、ビルドイベントを使ってコピーしてしまうよ。以下のように設定してね。(「Release」コンパイルするまでは「Release」フォルダがないのでコメントアウトしているよ)<br/>
内容的には「ddns-update.serviceを毎時0分0秒に起動する」と言って感じです。<br/>「ddns-update.service」ファイルと同様に「Description」は好きな内容に変更しても大丈夫です。
+
[[ファイル:CShapeToCppDll-004.jpg]]<br/>
  
準備ができたので、動かしてみます。<br/>動かすときは以下のコマンドを実行します。
+
=== プロジェクトの依存関係の設定 ===
<syntaxhighlight lang="bash">sudo systemctl start ddns-update.timer</syntaxhighlight>
+
コピーするにもちゃんとリコンパイルされた資源をコピーしないといけないので、プロジェクトの依存関係を設定することで、ビルドの順番を設定するよ。<Br/>
エラーが出なければOKです。<br/>エラーが出たときは内容を確認してください。<br/>内容が合っているのにエラーになるときは…「nano」上で改行をし直してみてください。<br/>(私は改行コードの違いでエラーが出ていることに気づくまで、結構時間がかかってしまいました)
+
ソリューションエクスプローラーからC♯のプロジェクトを右クリックして「ビルド依存関係」→「プロジェクト依存関係」を選択してね。<br/>
 +
「依存関係」タブの依存先にC++のプロジェクトが表示されているはずだから、チェックを入れてOKボタンをクリックしてね。
  
正常に動いたなら、今度は起動時に動くようにします。<br/>以下のコマンドで起動時に動くようになります。
+
== C♯のコード ==
<syntaxhighlight lang="bash">sudo systemctl enable ddns-update.timer</syntaxhighlight>
+
MVVMモデルでサンプルを作ったからビューモデルが入っているけど…DLLを呼ぶには必要ないから無視してね。
 +
=== モデル ===
 +
<syntaxhighlight lang="C#">
 +
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;
  
ちなみに、止めたいときは…
+
//追加
<syntaxhighlight lang="bash">sudo systemctl stop ddns-update.timer</syntaxhighlight>
+
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>
 +
        /// <param name="sender">イベント送信元</param>
 +
        /// <param name="e">イベント情報</param>
 +
        private void Button_Click(object sender, RoutedEventArgs e)
 +
        {
 +
            //DLLの関数を呼び出す
 +
            Int32 l_Result = _Sum(300, 500);
 +
 
 +
            //計算結果の表示
 +
            MessageBox.Show("計算結果:" + l_Result.ToString());
 +
        }
 +
    }
 +
}
 +
</syntaxhighlight>
  
起動時に動かないようにするには…
+
=== ビュー ===
<syntaxhighlight lang="bash">sudo systemctl disable ddns-update.timer</syntaxhighlight>
+
<source lang="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 CSharpToCDLL
 +
{
 +
    class MainWindowViewModel : INotifyPropertyChanged
 +
    {
 +
        /// <summary>
 +
        /// ラベル表示用変数
 +
        /// </summary>
 +
        private String m_Label_Content;
  
ここで少し疑問に思う人がいるかも知れません…<br/>「ddns-update.service」と「ddns-update.timer」と実行コマンドの「sudo systemctl start ddns-update」の3つを関連付ける設定をしていません。<br/>実は単純な話で、ファイル名だけで関連付いています。<br/>「ddns-update.service」と「ddns-update.timer」は「ddns-update」が同じになっています。<br/>これで「*.service」ファイルと「*.timer」ファイルはセットだと認識しています。<br/>また実行コマンドは「ddns-update.timer」と指定するだけで「*.service」ファイルを探しに行きます。
+
        /// <summary>
 +
        /// ラベル表示文字列
 +
        /// </summary>
 +
        public String Label_Content
 +
        {
 +
            set
 +
            {
 +
                this.m_Label_Content = value;
 +
                this.OnPropertyChanged("Label_Content");
 +
            }
 +
            get { return this.m_Label_Content; }
 +
        }
  
== ルーターの設定 ==
+
        /// <summary>
「[[「OPEN IPv6 ダイナミック DNS for フレッツ・光ネクスト」の利用/ArchLinux#ルーターの設定 | PiVPNによるOpenVPNのインストール#ルーターの設定]]」に書いているので、そちらを参照してください。<br/>
+
        /// プロパティ変更イベントハンドラ
sshのデフォルトポートを開放したいなら22番ポートを開放してください(22番ポートを開放するのは危険だと思いますが…)
+
        /// </summary>
 +
        public event PropertyChangedEventHandler PropertyChanged;
  
= 参考サイト =
+
        /// <summary>
[https://i.open.ad.jp/ OPEN IPv6 ダイナミック DNS for フレッツ・光ネクスト]
+
        /// プロパティ変更通知
 +
        /// </summary>
 +
        /// <param name="p_PropertyName">プロパティ名</param>
 +
        public void OnPropertyChanged(String p_PropertyName)
 +
        {
 +
            if (this.PropertyChanged != null)
 +
            {
 +
                this.PropertyChanged(this, new PropertyChangedEventArgs(p_PropertyName));
 +
            }
 +
        }
 +
    }
 +
}
 +
</source>
  
[[Category:ArchLinux]]
+
[[Category:C♯]]
[[Category:DDNS]]
+
[[Category:C++]]
 +
[[Category:dll]]

2019年7月26日 (金) 07:46時点における版

久しぶりにやったら、すっかり忘れていたので覚書…

C++のDLLプロジェクト作成

まず、C++のDLLを作成するときの注意点… 作成するプロジェクトは、「Win32プロジェクト」を選択するんだ。
CShapeToCppDll-005.jpg


あと…アプリケーションの設定では「DLL」と「空のプロジェクト」を選択してね。
CShapeToCppDll-001.jpg


空のプロジェクトが作成されたら「cpp」「h」「def」ファイルを追加するんだ。今回は「CppDll.cpp」「CppDll.h」「CppDll.def」を追加したよ。


そしたら、プロジェクトのプロパティを開いて「構成プロパティ→リンカー→入力→モジュール定義ファイル」に「CppDll.def」を設定するんだ。
(DebugとReleaseでそれぞれ設定する必要があるんだよ。)
CShapeToCppDll-002.jpg

DLLのコード

ヘッダーファイル(*.h)

#ifndef DLLAPI
#define DLLAPI extern "C" __declspec(dllimport)
#endif

DLLAPI long __stdcall _Sum(const long p_Number1, const long p_Number2);

コードファイル(*.cpp)

#define DLLAPI

#include "CppDll.h"

DLLAPI long __stdcall _Sum(const long p_Number1, const long p_Number2)
{
	return p_Number1 + p_Number2;
}

モジュール定義ファイル(*.def)

LIBRARY	CppDll

EXPORTS
	_Sum

C♯のプロジェクト作成

ほとんどそのまま作るんだけど…ソリューションのコンパイル対策をしておくよ。

ビルドイベントの設定

C++のDLLはソリューションフォルダ直下の「Debug」や「Release」フォルダにDLLが格納されてしまうんだ。そうすると、デバッグするときにDLLが見つからないので、ビルドイベントを使ってコピーしてしまうよ。以下のように設定してね。(「Release」コンパイルするまでは「Release」フォルダがないのでコメントアウトしているよ)
CShapeToCppDll-004.jpg

プロジェクトの依存関係の設定

コピーするにもちゃんとリコンパイルされた資源をコピーしないといけないので、プロジェクトの依存関係を設定することで、ビルドの順番を設定するよ。
ソリューションエクスプローラーからC♯のプロジェクトを右クリックして「ビルド依存関係」→「プロジェクト依存関係」を選択してね。
「依存関係」タブの依存先にC++のプロジェクトが表示されているはずだから、チェックを入れてOKボタンをクリックしてね。

C♯のコード

MVVMモデルでサンプルを作ったからビューモデルが入っているけど…DLLを呼ぶには必要ないから無視してね。

モデル

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;

//追加
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>
        /// <param name="sender">イベント送信元</param>
        /// <param name="e">イベント情報</param>
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            //DLLの関数を呼び出す
            Int32 l_Result = _Sum(300, 500);

            //計算結果の表示
            MessageBox.Show("計算結果:" + l_Result.ToString());
        }
    }
}

ビュー

<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>

ビューモデル

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;

        /// <summary>
        /// ラベル表示文字列
        /// </summary>
        public String Label_Content
        {
            set
            {
                this.m_Label_Content = value;
                this.OnPropertyChanged("Label_Content");
            }
            get { return this.m_Label_Content; }
        }

        /// <summary>
        /// プロパティ変更イベントハンドラ
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// プロパティ変更通知
        /// </summary>
        /// <param name="p_PropertyName">プロパティ名</param>
        public void OnPropertyChanged(String p_PropertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(p_PropertyName));
            }
        }
    }
}