ブログ

割とコンピュータよりの情報をお届けします。

2019年2月

WCFのバッファサイズ制限

WCFを使用するアプリケーションを開発していたところ,大容量のデータを転送したところで,失敗した.

デフォルト バッファサイズが決まっている.

WCFを使用するアプリケーションを開発していたところ,大容量のデータを転送したところで,失敗した.が,これまでまともに設定ファイルなどいじったことがないため焦ってしまった.

デフォルト バッファサイズが決まっているので,maxBufferSizeとmaxReceivedMessageSizeを設定する必要がある.

参考「WCFで大きなデータを送受信するさいに注意する設定値(クオータ)のメモ

これら2つの数値は,2つあるので別々だと思っていたが,異なる値を設定すると失敗した.
WCFの導入までは何の苦労もなくできてしまうので,いったん躓くとつらい.

名前付きパイプを使っていたので今回はこれでよいのだが,HTTPなどを使用する場合には,安易にバッファサイズを大きくすることは攻撃に弱くなったり,意図しない再送・ハイレートの転送でシステムがダウンする恐れが高まるということで,バッファを超えたら分けて送付というやり方の方が本当は良いのかもしれない.

≫ 続きを読む

2019/02/28 コンピュータ   TakeMe

WindowsのMemory Mapped Fileはx86とx64のデータの橋渡しにも使える場合が多い

.NETのMemory Mapped Fileを扱うプログラムを書いていたら,32bitも64bitも混ざっていたがどうもやり取りはできるらしい.

Windowsでは共有メモリとしてMemory Mapped Fileを使うと複数のプロセスから同じメモリ領域を参照しているような動作が可能になる.
.NET Framework 4.0からはサポートするクラスが用意されているので比較的簡単に利用できる.
ただし,.NETではターゲットがAny CPUだと32bit用のつもりのものが64bit環境で動いたり,逆も起こる.
このときどうなるのか気になっていたが,概ね問題なく動作する.

概ねというのは一つ一つの変数のアドレスを指定したバイト単位の参照の場合には問題ないということ.おそらく一度に参照しようとした場合には考慮すべきことが増えるのではないかと思うので後で調べる.

例えば,以下のようなコード

// サーバ側
using System;
using System.Windows.Forms;
using System.IO.MemoryMappedFiles;

namespace MemoryMappedFile_TEST
{
    public partial class ServerForm : Form
    {
        private MemoryMappedFile mem = null;
        private MemoryMappedViewAccessor accessor = null;
        private Timer timer = new Timer();

        private Int64 counter = 0;

        public ServerForm()
        {
            InitializeComponent();

            this.FormClosed += ServerForm_FormClosed;

            mem = MemoryMappedFile.CreateNew("test_memory", 64);
            accessor = mem.CreateViewAccessor();

            this.label1.Text = counter.ToString();

            timer.Interval = 1000;
            timer.Tick += timer_Tick;
            timer.Start();
        }

        private void ServerForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            timer.Stop();
            accessor.Dispose();
        }

        private void timer_Tick(object sender, EventArgs e)
        {
            // 書き込み
            accessor.Write((Int64)0, (Int64)counter++);

            this.label1.Text = counter.ToString();
        }
    }
}
// クライアント側
using System;
using System.Windows.Forms;
using System.IO.MemoryMappedFiles;

namespace Client
{
    public partial class UserForm : Form
    {
        private MemoryMappedFile mem = null;
        private MemoryMappedViewAccessor accessor = null;
        private Timer timer = new Timer();

        private Int64 value = 0;

        public UserForm()
        {
            InitializeComponent();

            this.FormClosed += UserForm_FormClosed;

            mem = MemoryMappedFile.OpenExisting("test_memory");
            accessor = mem.CreateViewAccessor();

            this.label1.Text = value.ToString();

            timer.Interval = 1000;
            timer.Tick += timer_Tick;
            timer.Start();
        }

        private void UserForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            timer.Stop();
            accessor.Dispose();
        }

        private void timer_Tick(object sender, EventArgs e)
        {
            // 書き込み
            value = accessor.ReadInt64((Int64)0);

            this.label1.Text = value.ToString();
        }
    }
}

≫ 続きを読む

2019/02/18 コンピュータ   TakeMe

WPFのメモリリーク対策

C#ではメモリリークはほとんど心配ないと思っていたが,WPFアプリケーションを作っていると,メモリリークが頻繁に起きることが分かって恐ろしくなった.

まず,参考のページ「俺が遭遇したWPFイメージコントロールのメモリーリークと回避法(?)の1つ」を参考にすると,Imageコントロールはよくリークを起こす.参考のページのサンプルコードの最後の方にあるように,明示的にSourceにnullを入れてやらないと割と残るようだ.

リーク以外に,キャッシュという仕組みもありわかりにくい.xamlにイメージのファイル名を直接書いていると一度使ったイメージはキャッシュされ次の使用が速くなる(らしい).

このほか,UserControlもうまく設計していない場合,リークを起こす.Imageを使う場合やTimerを使う場合にはIDisposalインタフェースを実装して明示的にDisposeを呼び出すことを強く推奨する.TimerなんかはいったんStartしたままコントロールを破棄してしまうと(Windowを閉じるなど),一見コントロールが使えなくなくなっても残ってしまう.しかも,消えるタイミングが不定で恐ろしいバグになる(たいていはtickも残る).

Visual Studio 2017 ProfessionalやCommunityなら診断ツールを用いてヒープを表示させると,参照しているオブジェクト種類の一覧と参照されているオブジェクトの一覧が取れるのでデバッグの助けになる.
ポイントはImageが残っていないか?Start()を呼んだままのTimerが残っていないか?かな

≫ 続きを読む

2019/02/13 日記   TakeMe
タグ:WPF

C#からPythonの関数を呼び出し2

C#からPythonの関数を呼び出し」の最後の方で,「デバッグの時は云々」の部分をもう少し詳しく書く.

まずはコードを示してみる.

using System;
using System.Collections.Generic;
using Python.Runtime;

namespace TEST
{
    class Program
    {
        // TEST.exe C:\python-3.7.0-embed-amd64\
        // numpy入りのpython embeddableを指定してください.
        static void Main(string[] args)
        {
            var path = Environment.GetEnvironmentVariable("PATH");
            path = path + args[0] + ";";
            Environment.SetEnvironmentVariable("PATH", path);

            using (Py.GIL())
            {
                dynamic sys = Py.Import("sys");
                sys.path.append(args[0]);

                dynamic np = Py.Import("numpy");
                dynamic a = np.array(new List<double> { 1, 2, 3 });
                Console.WriteLine(a.dtype);
                dynamic b = np.array(new List<int> { 6, 5, 4 }, dtype: np.int32);
                Console.WriteLine(b.dtype);
                Console.WriteLine(a * b);
                Console.ReadKey();
            }
        }
    }
}

まずは,環境変数PathにPython Embeddableのインストールパスを設定する.
さらに,sys.path.appendでPythonモジュールのパスも指定する.
このようにすると,実行時の引数にPython embeddableのインストールパスを指定すると実行ができる.
 

≫ 続きを読む

2019/02/11 コンピュータ   TakeMe
タグ:Python

C#からPythonの関数を呼び出し

C言語からPythonの関数呼び出し4」から発展を考えていたら,C#から呼び出しの記事を書いていないことに気が付いたので備忘録も含めて記載する.
なお,参考は「Pythonから.NETを呼び出す方法とついでにその逆も(https://qiita.com/y-tsutsu/items/0c4561d6478be32e6f8e)」なのだ.

使用するのはPython for .NETである.これ自体は前にも扱っている.このときには,Pythonから.NETを呼び出すというのをやっていた.
今回はその逆である.

git clone https://github.com/pythonnet/pythonnet.git

でgithubからpythonnetをダウンロードする.
ダウンロードされたフォルダの中にpythonnet.slnファイルがあるはずなのでこれを開く(Visual Studio 2017).

その後,ビルドの構成はReleaseWinPY3でx64(実際には実行環境に合わせる)でビルドする.するとbinフォルダにPython.Runtime.dllが出来上がる.

次に,.NETのプロジェクトを作る.
この時,アッセンブリの参照に先のPython.Runtime.dllを加えてやる(これが大事).参考のページではNugetパッケージをとってきているが,Python 3.7もPython for .NETもGitHubの方の更新が速いのでこのNugetパッケージは更新が全く追い付いていない.

続いて,「Pythonから.NETを呼び出す方法とついでにその逆も」からサンプルコードを頂く.
例えば以下のようなコードになる.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Python.Runtime;

namespace TEST
{
    class Program
    {
        static void Main(string[] args)
        {
            using (Py.GIL())
            {
                dynamic np = Py.Import("numpy");
                dynamic a = np.array(new List<double> { 1.0, 2.1, 3.2 });
                Console.WriteLine(a.dtype);
                dynamic b = np.array(new List<float> { 4, 5, 6 }, dtype: np.int32);
                Console.WriteLine(b.dtype);
                Console.WriteLine(a * b);
                Console.ReadKey();
            }
        }
    }
}

実行時には,NumpyをインストールしたPython Embeddableのフォルダの場所にexeファイルとdllファイル突っ込めば実行できる.(Visual Studioのデバッグの際にはプロジェクトのプロパティで「デバッグ」の設定で,同等の構成になるように作業フォルダなどを調整する)

≫ 続きを読む

2019/02/10 コンピュータ   TakeMe
タグ:Python

Visual C++にあるMicrosoft独自の拡張機能の一例

C/C++では,コマンドライン引数が欲しい時,main関数の引数にint argc, char **argvなどを指定して使うことが多いが,実はMicrosoft独自の拡張機能が想定できる場合にはグローバル変数にもある.

Microsoftのコンパイラなら,グローバル変数として__argc, __argvが存在することが期待できる.

グローバル変数なのでスタートの関数がWinMainなどであっても同様に使用することができる.
このことは初めて知った.(忘れていただけかもしれないが...)
例えば次のようなコードで使用できる.

#include <iostream>

int main()
{
    for (int i = 0; i < __argc; i++) {
        std::wcout << __argv[i] << std::endl;
    }
    return 0;
}

MinGWでは使えないようなので注意が必要である.

≫ 続きを読む

2019/02/05 コンピュータ   TakeMe

実行ファイルのディレクトリ名をstd::wstringで返す例

C言語からPythonの関数呼び出し4
「実行ファイルと同じフォルダにあるpyモジュールも使いたいので,前回より少し手を加えた.」と書いていた部分であるが,なかなかみすぼらしいコードだったので少し考えて,代わりのコードを用意しようと思った.

変更のコードは以下に示すが,
本体は,実行ファイルのディレクトリを取得するGetModuleFileDirW()と
バックスラッシュをバックスラッシュ2つに変換するReplaceBS2BSBS()と
wstringをstringに変換するwstring2string().

#include <string>
#include <iostream>
#include <codecvt>
#include <windows.h>

std::wstring GetModuleFileDirW()
{
    wchar_t path[MAX_PATH + 1];
    ::GetModuleFileNameW(NULL, path, sizeof(path));
    wchar_t drive[MAX_PATH + 1], dir[MAX_PATH + 1], fname[MAX_PATH + 1], ext[MAX_PATH + 1];
    _wsplitpath_s(path, drive, dir, fname, ext);

    std::wstring wsDir = drive;
    wsDir += dir;

    return wsDir;
}

std::wstring ReplaceBS2BSBS(std::wstring wsDir)
{
    {
        std::wstring wstmp;
        for (int i = 0; i < (int)wsDir.size(); ++i) {
            wstmp += wsDir[i];
            if (wsDir[i] == '\\') {
                wstmp += TEXT("\\");
            }
        }
        wsDir = wstmp;
    }
    return wsDir;
}

std::string wstring2string(std::wstring wsDir)
{
    // wstring からstringへの変換
    std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> cv;
    return cv.to_bytes(wsDir);
}

int main()
{
    std::wstring wsDir = ::GetModuleFileDirW();
    wsDir = ReplaceBS2BSBS(wsDir);

    // wstring からstringへの変換
    std::string sDir = ::wstring2string(wsDir);
    std::cout << sDir.c_str() << std::endl;

    return 0;
}

c_str()で前の例と同じchar[]に変換している.

≫ 続きを読む

2019/02/03 コンピュータ   TakeMe

C言語からPythonの関数呼び出し4

久しぶりの「C言語からPythonの関数呼び出し」記事として,WinPythonとVisual Studio 2017でPython embeddable用の実行ファイルを作るということを再検討していた.

まず,Python側のコードの例は以下のようにした.
今回もlistを引数としてPythonに渡して,listを返り値としてもらうという例になっている.

# -*- coding: utf-8 -*-

def SampleFunc1(t, f):
    output = [10, 9, 7, 5, 3, 1];
    return output

C(C++)言語側のコードは以下のようにした.

#include <stdio.h>
#include <windows.h>

#define _USE_MATH_DEFINES
#include <cmath>
#ifdef _DEBUG
#undef _DEBUG
#include <Python.h>
#define _DEBUG
#else
#include <Python.h>
#endif

int main()
{
    char Path[MAX_PATH + 1];
    char Dir[MAX_PATH + 1 + 32];
    char drive[MAX_PATH + 1]
        , dir[MAX_PATH + 1]
        , fname[MAX_PATH + 1]
        , ext[MAX_PATH + 1];
    if (0 != GetModuleFileNameA(NULL, Path, MAX_PATH)) {
        _splitpath_s(Path, drive, dir, fname, ext);

        int j = 0;
        for (int i = 0; i < MAX_PATH + 1; i++) {
            Dir[j] = dir[i];
            j++;
            if (dir[i] == '\\') {
                Dir[j] = dir[i];
                j++;
            }
            else if (dir[i] == '\0') {
                break;
            }
        }
        sprintf_s(dir, "%s%s", drive, Dir);
        printf("ディレクトリ パス : %s\n", dir);
    }
    char buf1[2000];
    sprintf_s(buf1, "import sys\r\nsys.path.append('%s')\r\nprint(sys.path)\r\n", dir);
    // 実行ファイルと同じフォルダにあるpyモジュールも使いたいので,前回より少し手を加えた.

    PyObject *pName, *pModule, *pFunc;
    PyObject *pArgs, *pArg, *pArg2, *pValue, *pValue2;

    Py_Initialize();
    PyRun_SimpleString(buf1);
    pName = PyUnicode_DecodeFSDefault("SampleFunc");

    pModule = PyImport_Import(pName);
    Py_DECREF(pName);

    if (pModule != NULL) {
        pFunc = PyObject_GetAttrString(pModule, "SampleFunc1");

        if (pFunc && PyCallable_Check(pFunc)) {
            pArgs = PyTuple_New(2); // 関数fncの引数の数を引数にする

            pArg = PyList_New(1000);
            pArg2 = PyList_New(1000);
            for (int i = 0; i < 1000; i++) {
                double value = sin(2.0 * M_PI * 0.01 * 1.0 * i);
                pValue = PyFloat_FromDouble(value);
                pValue2 = PyFloat_FromDouble(2.0);
                if (!pValue) {
                    Py_DECREF(pArgs);
                    Py_DECREF(pArg);
                    Py_DECREF(pArg2);
                    Py_DECREF(pModule);
                    return 1;
                }
                PyList_SetItem(pArg, i, pValue);
                PyList_SetItem(pArg2, i, pValue2);
            }
            if (PyTuple_SetItem(pArgs, 0, pArg2) != 0)
                return 1;
            if (PyTuple_SetItem(pArgs, 1, pArg) != 0)
                return 1;

            pValue = PyObject_CallObject(pFunc, pArgs);
            Py_DECREF(pArgs);
            if (pValue != NULL) {
                //printf("%d\n", PyList_CheckExact(pValue));
                Py_ssize_t len = PyList_Size(pValue);

                for (int i = 0; i < len; i++) {
                    //printf("%I32d ", PyFloat_Check(PyList_GetItem(pValue, i)));
                    printf("%.4g\n", PyFloat_AsDouble(PyList_GetItem(pValue, i)));
                }
                //printf("return: %s\n", (char*)PyUnicode_DATA(pValue));
                Py_DECREF(pValue);
            }
            else {
                Py_DECREF(pFunc);
                Py_DECREF(pModule);
                PyErr_Print();
                fprintf(stderr, "Call failed\n");
                return 1;
            }
        }
        else {
            if (PyErr_Occurred())
                PyErr_Print();
            fprintf(stderr, "Cannot find function\n");
        }
        Py_XDECREF(pFunc);
        Py_DECREF(pModule);
    }
    else {
        PyErr_Print();
        fprintf(stderr, "Failed to load module\n");
        return 1;
    }
    if (Py_FinalizeEx() < 0) {
        return 120;
    }
    return 0;
}

プロジェクトのプロパティを次のように設定しておくとF5キーでデバッグがかかるようになる.
(Python部分はデバッグできない) 「ビルド後のイベント」のコマンドライン
copy .\*.py $(TargetDir)

「デバッグ」の「環境」にPython Embeddableのインストールパスを設定する.例えば
PATH="$(Path)C:\python-3.7.2.post1-embed-amd64\;"

なお,[C/C++]の[追加インクルードディレクトリ]にはWinPythonのインストールパスを設定する.例えば
C:\WPy-3710Zero\python-3.7.1.amd64\include
[リンカー]の[追加のライブラリディレクトリ]には 例えば
C:\WPy-3710Zero\python-3.7.1.amd64\include
[リンカー]の[追加の依存ファイル]には
python3.lib
を設定する.

 

なお途中にでている「手を加えた」としている部分は次の記事ですこし考え直した.

≫ 続きを読む

2019/02/02 コンピュータ   TakeMe
タグ:Python

letsencryptからcertbotへアップグレードは必要?

letsencryptを使っていたが,"TLS-SNI-01 validation is reaching end-of-life."という内容のメールが届くようになった.
暫く様子見をしていたが,突然使えなくなるのは困るので,一足早くhttps://certbot.eff.org/の指示に出ているクライアントに入れ替えてみた.

"TLS-SNI-01 validation is reaching end-of-life."という内容のメールの中身を確認すると,
一応指示としてはLet's Encryptの専用クライアントを使用している者はバージョンを確認する,アップグレードするなど以下のアドレスを
https://community.letsencrypt.org/t/how-to-stop-using-tls-sni-01-with-certbot/83210
を確認してほしいとメールの本文に書かれていた.

ディストリビューションの標準のリポジトリのクライアントがどのバージョンかわからないので,とりあえず,https://certbot.eff.org/の指示に従ってクライアントを切り替えておいた.

さらに,
sudo sh -c "sed -i.bak -e 's/^\(pref_challs.*\)tls-sni-01\(.*\)/\1http-01\2/g' /etc/letsencrypt/renewal/*; rm -f /etc/letsencrypt/renewal/*.bak"
sudo certbot renew --dry-run
まで実行したところ,
表示錠はhttp-01にしても問題ないようだ.

まだ2週間程度の猶予が与えられているため本当に必要だったのかまだわからない.

≫ 続きを読む

2019/02/02 コンピュータ   TakeMe

Visual C++のプロパティページの設定は構成毎?

今日は,Visual C++のプロパティページでは,構成毎・プラットフォーム毎の設定をするのが面倒だなーと言っている人がいてびっくりした.
というか私も最近知った内容だが...

Visual C++のプロパティページでは,構成毎・プラットフォーム毎の設定ができるが,「すべての構成」「すべてのプラットフォーム」を指定しておくと,構成・プラットフォームで共通の設定を入力できるようになっている.
言われなければ気付かないかもしれない.
最近はRelease/Debug, 32ビット/64ビットの4パターン設定する必要があるが,共通の項目は共通の値を入力できる.

ところで,全く話が変わるが,最近は
#ifdef _WIN32
がきかず
#ifndef _WIN64
が効くことがある...

≫ 続きを読む

2019/02/01 コンピュータ   TakeMe