ブログ

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

2018年8月

PythonからC#で書いた.NET Frameworkのクラスライブラリを読みだす 2

Cで作ったDLLを呼び出す場合には引数の型などを調べておく必要があり,渡す際も戻しを受け取る場合にも変数型を意識する必要がある。ところが,Python for .NETのDLLの呼び出しの場合にはある程度 自動で型合わせを行ってくれる。

例えばWinPythonについてくるpythonnetで以下のDLLを呼び出してみる。

次のようなコードでTestLibraryName.dllをビルドした。

using System;
using System.Collections.Generic;
using System.Text;

namespace TestLibraryName // dllの名前と同じだと失敗する
{
    public class TestClass
    {
        public string Test(string a)
        {
            return "OUT:" + a;
        }

        public byte[] Test2(byte[] a)
        {
            for (int i = 0; i < a.Length; i++)
            {
                a[i] = (byte)(2 * a[i]);
            }
            return a;
        }

        public Int32[] Test3(ref Int32[] a)
        {
            for (int i = 0; i < a.Length; i++)
            {
                a[i] = (Int32)(2 * a[i]);
            }
            return a;
        }

        public Int32[] Test4(Int32[] a)
        {
            for (int i = 0; i < a.Length; i++)
            {
                a[i] = (Int32)(2 * a[i]);
            }
            return a;
        }
    }
}

Python側に以下のようなコードを用意してみた。

import clr

clr.AddReference('TestLibrary')
from TestLibraryName import TestClass
import System

testClass = TestClass()

# list in Python => .NET array
a = [1, 2, 3]
b = testClass.Test2(a)
for i in range(b.Length):
    print(b[i])

print()

# .NET array => Reference of .NET array
c = System.Array[int](a)
d = testClass.Test3(c)
for i in range(c.Length):
    print(c[i])

print()

# .NET array => .NET array
c = System.Array[int](a)
d = testClass.Test4(c)
for i in range(c.Length):
    print(c[i])

print()

この場合,一番最初の部分でPythonのlistが.NET側でちゃんと配列に直っている。
もう少しサンプルをあさってみたい。

今のところ,C#やVB.NETのdelegateをPythonで作るのがうまくいっていない。.NETにはDelegateクラスが用意されていたと思うので使えるのではないかと思って探している。

≫ 続きを読む

2018/08/30 コンピュータ   TakeMe

PythonからC#で書いた.NET Frameworkのクラスライブラリを読みだす

PythonからC#で書いた.NET Frameworkのクラスライブラリを読み出してみた。
C# でクラスライブラリを書いて,Pythonから使う。

まずは,C#のライブラリの記述内容である。いろいろ試してみると,namespaceの名前の付け方には注意が必要なようです。なお,ビルドは.NET Framework 4.0以上で実行環境にあるものより下のバージョンならよさそう(バージョンを混ぜるとどうなるかは気になるが試していない)。ビルドで出てきたdllをpyファイルのある同じフォルダに移動するとPython for .NETのAddReference()で探せるようになる。

using System;
using System.Collections.Generic;
using System.Text;

namespace TestLibraryName // dllの名前と同じだと失敗する
{
    public class TestClass
    {
        public string Test(string a)
        {
            return "OUT:" + a;
        }
    }
}

次にPythonの側の記述である。

import clr

clr.AddReference('TestLibrary')
from TestLibraryName import TestClass

testClass = TestClass()

print(testClass.Test('test'))

.NETのstring型はうまく文字列に変換が行われるようである。

 

≫ 続きを読む

2018/08/25 コンピュータ   TakeMe

Pythonで一定間隔で実行2

だいぶん前の記事にPythonをつかって一定間隔で処理を実行するというサンプルを書いていた(JavaScriptならsetIntervalに相当する処理をさせたいということだった)。しかし,この時は知らなかったが,Python for .NETを使えば.NET Frameworkの支援を受けて一定間隔処理を記述することも可能であると知った。

Python for .NETを使って一定間隔処理のサンプルは以下のようになる。ちなみにWinPython以外だと入ってないかもしれないのでpip install pythonnetの実行からになると思います。(WinPython 3.7.0.1にはpythonnetがまだ非対応で3.7.0.2から暫定対応している )
最初は,Environment.Versionを調べる例を作ろうとしていたので,最初に残っている。
タイマーtickもPythonで記述してしまう例になっている。

import clr
from time import sleep

clr.AddReference('System')
from System import Environment
from System.Timers import Timer
from System import DateTime
print(Environment.Version.ToString())

running = True

def timer_tick(o, e):
    if running == True:
        print(DateTime.Now.ToString())
    else:
        timer.Stop()

timer = Timer()
timer.Interval = 1000
timer.Elapsed += timer_tick
timer.Start()

try:
    while running == True:
        sleep(1)
except KeyboardInterrupt:
    running = False

ちなみにEnvironment.Versionは4.5から全部4.0.**と表示されるようになってるらしい。(あれ,.NET Framework 4.0? おかしいなと思っても気にしないで)
この例では,clr.AddReference('System')はいらないらしい(Systemは暗黙的にReferenceに含まれるみたい)。

≫ 続きを読む

2018/08/24 コンピュータ   TakeMe
タグ:Python

CefSharp.Wpfの使用例(その後)2

前の記事でCefSharp.Wpfの使用例が出ていたが,デザイナーを使っていなかった。
今回はデザイナーを使う例を掲載する(差分だけ)。

デザイナーの方は,例えば,以下のようになる。Windowタグにxmlns:cefwpf="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"追加するとコンテンツとしてコントロールを記述できる。(何も言わなくてもわかると思いますが)

<Window x:Class="WPFwebui.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFwebui"
        xmlns:cefwpf="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" Background="White">
    <Grid>
        <cefwpf:ChromiumWebBrowser x:Name="browser" Address="http://www.google.com" Loaded="BrowserLoaded"></cefwpf:ChromiumWebBrowser>
    </Grid>
</Window>

なお前の記事の時はコードビハインドにbrowserを記述していたが,差分となる部分だけをのせると以下のようになるだろう。

    // CefSharp.Wpf.ChromiumWebBrowser browser;

        public MainWindow()
        {
            InitializeComponent();

            //browser = new CefSharp.Wpf.ChromiumWebBrowser();
            // アドレスの取得
            String page = string.Format(@"{0}\html-resources\html\index.html", StartupPath);
            BrowserSettings browserSettings = new BrowserSettings();
            browserSettings.FileAccessFromFileUrls = CefState.Enabled;
            browserSettings.UniversalAccessFromFileUrls = CefState.Enabled;
            browser.BrowserSettings = browserSettings;
            // アドレス設定
            browser.Address = page;
            //Content = browser;
            //browser.Loaded += BrowserLoaded;

以上のようにするとCefSharp.Wpfでデザイナーが使用できる。browserは複数挿入することができる。
タスクマネージャーを見るとCefSharp.BrowserSubprocessが増える。

≫ 続きを読む

2018/08/23 コンピュータ   TakeMe

Pythonでmatplotlibとio.BytesIOの使用例の作成

io.BytesIOを使用すると普通はファイルに書き出す操作を省き仮想的にメモリ上に書き出すことができる。
matplotlibと合わせた使用法の例を作っていた。

まずio.BytesIOは何かというと,C#でいうMemoryStreamのような扱いができるもの。
扱う内容が文字列に限定される場合にはio.StringIOというものも用意されている。
例えば,item = io.BytesIO()で用意しておいて,plt.savefig(item, format='***')でバイト列に書き出す。
さらに,下の例ではImage.openでまるでファイルのようにitemを引数と渡せるようになる。

from matplotlib import pyplot as plt
import numpy as np
from PIL import Image, ImageDraw
import io

fig = plt.figure()

plt.plot(np.sin(np.linspace(0, 10, num=250)), 'r')
plt.xlim(0, 250)
plt.ylim(-1, 1) 
plt.title('Sinusoidal wave')
item = io.BytesIO()
plt.savefig(item, format='png') #ここで書き出し
item.seek(0)
im = Image.open(item)#さらにメモリ上のファイルのように扱う
im = im.resize((200, 200))
item.close()
im.show()

ファイルを開いてファイルに書き出すように設計されているプログラムで応用ができそう。上の例ではいったん書き出してリサイズしてから表示している。
ファイルへの書き出しは,item.read()をファイルに書き出せばよい。

from matplotlib import pyplot as plt
import numpy as np
from PIL import Image, ImageDraw
import io

fig = plt.figure()

plt.plot(np.sin(np.linspace(0, 10, num=250)), 'r')
plt.xlim(0, 250)
plt.ylim(-1, 1) 
plt.title('Sinusoidal wave')
item = io.BytesIO()
plt.savefig(item, format='svg') #ここで書き出し
item.seek(0)
with open('test.svg','wb') as out: # Open file as bytes
    out.write(item.read())  

≫ 続きを読む

2018/08/23 コンピュータ   TakeMe

Pythonでplot遊び

matplotlibとxkcd()を試して遊びをしてみた。

順番にプロットを描いていく例。

from matplotlib import pyplot as plt
import matplotlib.animation as animation
import numpy as np

fig = plt.figure()
img = []
plt.xkcd()
for a in range(50):
    item, = plt.plot(np.sin(np.linspace(0, a, num=a*5)), 'r')
    plt.title('Sinusoidal wave')
    img.append([item])
ani = animation.ArtistAnimation(fig, img)
ani.save('sani.mp4', writer="ffmpeg")
plt.show()

ffmpegが必要ならしい。ところでplt.plotを繰り返すと色が順番にかわっていく。
このほかのアニメーションを作るほうほうとしてはplotをメモリ上に書き出していきPILでアニメーションgifにまとめるというもの。

from matplotlib import pyplot as plt
import matplotlib.animation as animation
import numpy as np
from PIL import Image, ImageDraw
import io

fig = plt.figure()
img = []

plt.xkcd()
for a in range(50):
    plt.plot(np.sin(np.linspace(0, a, num=a*5)), 'r')
    plt.title('Sinusoidal wave')
    item = io.BytesIO()
    plt.savefig(item, format='png')
    item.seek(0)
    im = Image.open(item)
    im.show()
    item.close()
    img.append(im)
im = img[0]
im.save('out.gif', save_all=True, append_images=img)

xkcd()を使っていると線がずれていく(重ならない)問題が生じる。

≫ 続きを読む

2018/08/20 コンピュータ   TakeMe

WinPython でDLLの読み出し例の作成

WinPython 3.6.3でDLLの呼び出しの例を作成していた。
特に,DLLを読み込んだ後 後始末の部分が苦労した。

例1.普通に整数を引数にとり整数を戻す関数。
例2.文字列を引数にとる例。
例3.文字列を引数にとり渡した領域の一部を編集する例。

まず用意したDLLのサンプル(#ifdefの部分は本当は別のヘッダファイルに用意するものだが今回はCからは使わないのでここに書いている。すると_declspec(dllimport)はいらない)

// sampleDll0001.cpp : DLL アプリケーション用にエクスポートされる関数を定義します。
//
// sampleDll0001.hの内容
#ifdef SAMPLEDLL0001_EXPORTS
#define SAMPLEDLL0001_API extern "C" __declspec(dllexport)
#else
#define SAMPLEDLL0001_API extern "C" __declspec(dllimport)
#endif

#include "stdafx.h"
#include "sampleDll0001.h"

SAMPLEDLL0001_API int __stdcall fnsampleDll0001_01(int a, int b)
{
    return a + b;
}

SAMPLEDLL0001_API int __stdcall fnsampleDll0001_02(char* a)
{
    return (int)strlen(a);
}

SAMPLEDLL0001_API int __stdcall fnsampleDll0001_03(char* a)
{
    if (strlen(a) > 5) {
        a[0] = 'a';
        a[1] = 'b';
    }
    return (int)strlen(a);
}

次にPythonの部分。(Release/sampleDll0001.dllにファイルがあるとしている)

import ctypes
from ctypes import wintypes

lib = ctypes.windll.LoadLibrary("Release/sampleDll0001.dll");
a = lib.fnsampleDll0001_01(ctypes.c_long(1), ctypes.c_long(2));
print(a);

string1 = "my string 1"
b_string1 = string1.encode('utf-8')
a = lib.fnsampleDll0001_02(b_string1);
print(a);

string2 = "my string 2"
b_string2 = string2.encode('utf-8')
a = lib.fnsampleDll0001_03(ctypes.c_char_p(b_string2));
string2 = b_string2.decode('utf-8')
print(string2);

libHandle = lib._handle
del lib
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)    
kernel32.FreeLibrary.argtypes = [wintypes.HMODULE]
kernel32.FreeLibrary(libHandle)

ctypes.windllはstdcallの場合らしい。
cyptesでは簡単にdllの読み出しができるようになったが,読み終わった後で開放してくれない。
それに対して,kernel32.dllのFreeLibrary()を使用すればよいのだが,x86用は良いのだが,x64は宣言が間違っているらしい。
ctypes.windll.kernel32.FreeLibrary(libHandle)
ctypes.ArgumentError: argument 1: <class 'OverflowError'>: int too long to convert
と表示されてしまう。kernel32.FreeLibrary.argtypesを代入しなおして間違いを訂正する必要があるらしい。

≫ 続きを読む

2018/08/18 コンピュータ   TakeMe

C++のauto C#のvar

C++11からはC++でもC#のvarのように変数宣言時に具体的な型名を記述せずにautoキーワードで代用することが可能になっている。

autoキーワードを使用する場合にもコンパイルして実行する段階では変数型は指定される必要がある。

コードを使いまわす際にいちいち変数型を切り替えて書き直すという手間を軽減することができるようになった。

#include "stdafx.h"


int main()
{
auto a = 10;
_tprintf_s(_T("%d %d\n"), a);
    return 0;
}

でもよく,C/C++っていうのでC言語とC++言語は混ざることがあるのでC言語の仕様をよく知っている人には混乱するかもしれない。
今更このように書く人はいないが,
auto int b = 10;
などはつかえない。

ところが,cppファイルの拡張子をcppからcに変更するとVisual C++でも以下のような使い方ができるようになる。(autoがどっちの意味でも通るようになる)

int main()
{
auto a = 10;
auto int b = 10;
_tprintf_s(_T("%d %d\n"), a, b);
return 0;
}

意味を理解せずに使うのは注意。。

≫ 続きを読む

2018/08/16 コンピュータ   TakeMe

Windowsで共有メモリの使用WindowsサービスとWindowsアプリケーション間のやり取り

WindowsでWindowsサービスとアプリケーションの間の共有メモリによるデータのやり取りを確認していた。

まずは簡単に,共有メモリの使い方を学習する。
参考にするページはこのページ
とりあえず,CreateFileMappingとMapViewOfFileを使用することでメモリの共有ができる。しかし,これができるのは同じユーザの場合。
Windowsサービスとユーザアプリケーション間のやり取りに苦労する。

そこで次の例を読み込む。
CreateFileMappingの2つ目の引数をNULLではなく設定するのだ。

ここまでできるとサービス側で読み書きできて,アプリケーション側では読み取り専用で開かせたい。

アプリケーション側

#include "stdafx.h"

int main()
{
    TCHAR name[] = _T("Global\\somename");
    DWORD dwSize = sizeof(int);

    HANDLE hSharedMemory = OpenFileMapping(
        FILE_MAP_READ,//FILE_MAP_ALL_ACCESS,
        FALSE,
        name
    );
    int* pMemory = (int*)MapViewOfFile(hSharedMemory, FILE_MAP_READ/*FILE_MAP_ALL_ACCESS*/, NULL, NULL, dwSize);

    for (int i = 0; i < 40; i++)
    {
        //(*pMemory)--;
        _tprintf_s(_T("memory: %d\n"), *pMemory);
        Sleep(500);
    }

    UnmapViewOfFile(pMemory);
    CloseHandle(hSharedMemory);

    return 0;
}

 

サービス側 試行錯誤してみました。

#include "stdafx.h"


int main()
{
    TCHAR name[] = _T("Global\\somename");
    DWORD dwSize = sizeof(int);

    SECURITY_DESCRIPTOR secDesc;
    SECURITY_ATTRIBUTES secAttr;
    InitializeSecurityDescriptor(&secDesc, SECURITY_DESCRIPTOR_REVISION);

    DWORD dwAclSize = 1024;
    SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_LOCAL_SID_AUTHORITY; // SECURITY_WORLD_SID_AUTHORITY (Everyone)でもよい
    PSID pSid = NULL;
    if (!AllocateAndInitializeSid(&SIDAuth, 1,
        SECURITY_WORLD_RID,
        0, 0, 0, 0, 0, 0, 0,
        &pSid))
    {
        return 1;
    }
    PACL pDacl = (PACL)HeapAlloc(GetProcessHeap(), 0, dwAclSize);
    if (pDacl == NULL) {
        return 1;
    }

    // 随意アクセス制御リストを初期化
    InitializeAcl(pDacl, dwAclSize, ACL_REVISION);

    // ACE(アクセス制御エントリ)をDACLに追加
    AddAccessAllowedAce(pDacl, ACL_REVISION, GENERIC_READ, pSid);
    AddAccessDeniedAce(pDacl, ACL_REVISION, GENERIC_WRITE, pSid);

    SetSecurityDescriptorDacl(&secDesc, TRUE, pDacl, FALSE);
    
    secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    secAttr.bInheritHandle = TRUE;
    secAttr.lpSecurityDescriptor = &secDesc;

    HANDLE hSharedMemory = CreateFileMapping(INVALID_HANDLE_VALUE, &secAttr, PAGE_READWRITE, NULL, dwSize, name);

    LocalFree(pDacl);
    FreeSid(pSid);

    int* pMemory = (int*)MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, NULL, NULL, dwSize);

    *pMemory = 0; // Only the administrators process can handle it as fully accessible variable.
    for (int i = 0; i < 40; i++)
    {
        (*pMemory)++;
        _tprintf_s(_T("src_memory: %d\n"), *pMemory);
        Sleep(1000);
    }

    UnmapViewOfFile(pMemory);
    CloseHandle(hSharedMemory);
    return 0;
}

特に
AddAccessAllowedAce(pDacl, ACL_REVISION, GENERIC_READ, pSid);
AddAccessDeniedAce(pDacl, ACL_REVISION, GENERIC_WRITE, pSid);
の周辺を入れていました。

[参考]

  1. C++で複数プロセスから読み書き可能な共有メモリを作る
  2. 【共有メモリ】アクセスが拒否されました。

≫ 続きを読む

2018/08/16 コンピュータ   TakeMe

open62541のビルドをVisual Studio Express 2017 for Windows Desktopで行うx64版

先にopen62541をVisual Studio Express 2017 for Windows Desktopでビルドする記事を書いたが,x86用しか書いていなかったがx64用のビルドはどうするのか気になっていた。CMakeの-GオプションにWin64を追加するだけだった。

先のページに対して

mkdir build
cd build
cmake .. -G "Visual Studio 15 2017"

としていた部分をいかのように直すだけ。

mkdir build
cd build
cmake .. -G "Visual Studio 15 2017 Win64"

これ自体は巨大なライブラリではなくランタイムを除いてコンパイルするとx86用では1 MBを超えない。x64用は5割程大きく1 MBを超える。

DLLが作りたい場合には-Dオプションを付ける。(順番には注意: オプションは-Dがパスより先である必要があるらしい)

cmake -D BUILD_SHARED_LIBS=1 .. -G "Visual Studio 15 2017 Win64"

≫ 続きを読む

2018/08/12 コンピュータ   TakeMe