Slide 1

Slide 1 text

PHPからWin32APIを いじってみた 2016/09/24第一回PHP勉強会@仙台(仮) Taiko19xx / 木村俊彦

Slide 2

Slide 2 text

こんにちは • 木村俊彦 • Twitter / GitHub : taiko19xx • 株式会社SRIA エンジニア • 普段はもちろんPHP • たまにスマホアプリとかもやりたい • 趣味は旅行とカメラとバイク • 最近は大洗とか北海道へ

Slide 3

Slide 3 text

早速ですが アンケート

Slide 4

Slide 4 text

自宅か会社で Windowsを 使ってる方は?

Slide 5

Slide 5 text

自宅も会社も Mac / *nixの方!

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

今回の話はタイトルの通り、 「PHPからWindowsのAPIをいじる」 という話です…

Slide 8

Slide 8 text

Win32APIとは?

Slide 9

Slide 9 text

Win32APIとは Windows API(ウィンドウズ エーピーアイ)とは、 Microsoft WindowsのAPIのことである。 特に32ビットプロセッサで動作するWindows 95 以降やWindows NTで利用できるものはWin32 APIと呼ばれる。また、それらのWindowsにおけ るWin32 APIの実装をWin32と呼ぶ。 (https://ja.wikipedia.org/wiki/Windows_APIより)

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

つまり • 意味合いとしてはよくご存じの「API」と同じで、Windowsが 提供している各機能へのアクセス窓口 • 「各機能」の範囲は幅広く… • ファイルの読み書き • ウィンドウの表示 • GUIの制御 などなど • 例えば、ファイルの読み書きをする関数(ex. fopen/fwrite)は Win32APIを裏で呼び出している • PHPから呼べない機能を使いたい場合は直接呼ぶしかない

Slide 12

Slide 12 text

直接呼べるのか? • Windows APIに属する各APIは、主にDLLからの関数または COMインターフェイスとして機能を公開している。 (Wikipediaより) • そのため、言語や環境を問わず、DLLを読んだりCOMへアクセ スする事で自由に呼び出す事ができます • PHPにはCOMを使えるようにするcom_dotnet拡張があるので、 これを利用します

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

利用準備

Slide 15

Slide 15 text

php_com_dotnet.dllの有効化 • 普段からバリバリ使ってる方にはお馴染み • 標準で添付されていますが、5.3.15/5.4.5以降は手動で有効に する必要あり • php.iniに「extension=php_com_dotnet.dll」を追記するだけ • 「php –i」を実行して、com_dotnetが確認できればOK

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

DynamicWrapperの導入 • com_dotnetからWin32APIを呼び出そうとすると少し大変なの で、DynamicWrapperというWin32APIのラッパーも導入 • http://www.borncity.com/web/WSHBazaar1/WSHDynaCall.htm • dynawrapnt.zipをダウンロードして解凍し、下記コマンドでシ ステムにDLLを登録 • regsvr32.exe dynwrap.dll • PHP -> com_dotnet -> DynamicWrapper -> Win32APIという 若干遠回りな呼び出しになるが、これで扱えるように

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

ここで注意

Slide 21

Slide 21 text

PHPは 32bit版(x86)を 使用しましょう!

Slide 22

Slide 22 text

何故か • DynamicWrapperが32bitのDLLのため • 32bitのDLLは64bitのアプリケーションからは呼べない制約がある • その逆も然り • DLLを調整すれば64bitからでも呼べるようになる、らしい • これに気付くまで2~3時間ハマってました…

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

動かしてみる

Slide 25

Slide 25 text

今回表示するもの

Slide 26

Slide 26 text

やってみましょう • メッセージボックスを表示するだけ 1. com_dotnetでDynamicWrapperを呼び出し 2. 利用したいWin32APIを内包しているDLLと関数名をRegist()に渡し、 呼び出しの準備をする • 今回はMessageBox()関数を呼び出します • https://msdn.microsoft.com/ja-jp/library/cc410914.aspx 3. 実際に関数を呼び出す

Slide 27

Slide 27 text

Register() • 利用したい関数とDLLを読み込む • 引数はこんな感じ • 呼び出すDLL名(必須) • 呼び出す関数名(必須) • 関数に渡す引数の型フラグ(オプション、i=に続けて指定) • 呼出規約の種類フラグ(オプション、f=に続けて指定) • 返り値の型フラグ(オプション、 r=に続けて指定)

Slide 28

Slide 28 text

引数/返り値の型 指定するフラグ 型名 a IDispatch c unsigined char d 8byte real f 4byte real k IUnknown h handle (4bytes) l long (4bytes) p pointer s string t short (2bytes) u unsigined int w wide string

Slide 29

Slide 29 text

MessageBoxの場合 • MessageBox(HWND, LPCSTR, LPCSTR, UNIT) • 100%一致する訳ではないので、ある程度予測して当てはめる • LPCSTR(const char*)に該当するのは無いので、stringを割り当てる • HWND = h, LPCSTR = s, UNIT = u とする • つまり、「i = hssu」となる

Slide 30

Slide 30 text

呼出規約 プログラミングにおける呼出規約(よびだしきやく)ないし呼出 慣例(よびだしかんれい)はサブルーチンを呼び出す際の標準的 な手法を指す。サブルーチンにデータを渡し、戻るべきアドレス (リターンアドレス)を記録し、サブルーチンからデータを受け 取るための規則である。一つのプログラムでは、(複数の言語処 理系を用いて記述する場合も)同一の呼出規約を守る必要がある。 さまざまな呼出規約があり、引数のコールスタック(以下単にス タックと呼ぶ)への格納法、サブルーチンにデータを渡す方法、 サブルーチンからの復帰法、名前修飾が異なる。 (https://ja.wikipedia.org/wiki/%E5%91%BC%E5%87%BA%E8%A6%8F%E7%B4%84より)

Slide 31

Slide 31 text

呼出規約の種類 指定するフラグ 内容 m Microsoft (bと排他的) b Borland (mと排他的) s _stdcall (cと排他的) c _cdeel (sと排他的) 4 4 byte real value returned in ST(8と排他的) 8 8 byte real value returned in ST(4と排他的) • デフォルトは「f=ms」 • そのままでも問題なく利用できるので、特に指定のない場合は 省略してもOK

Slide 32

Slide 32 text

関数の呼出 • Register()で登録した関数は、上記のように呼び出す • なので、1回登録すればその後は複数回呼び出せる • 引数によってはnullを指定できる場合があるので、マニュアル を参照のこと • 呼出時の型は、PHP側も合わせる必要がある

Slide 33

Slide 33 text

日本語を渡す

Slide 34

Slide 34 text

日本語を渡す

Slide 35

Slide 35 text

日本語を渡す • 普通に何もせずに渡してしまうとエラーになる • mb_convert_encoding()でSJISやSJIS-winに変換すればOK

Slide 36

Slide 36 text

返り値 • 返り値のある関数の場合、PHPでその返り値を受け取れます • Registerで「r=」のフラグを利用して、引数と同じように型を 推測して登録しておく必要があります • MessageBox()の場合、押したボタンによって数値が返ってく るので、「r=u」を指定すればOK

Slide 37

Slide 37 text

返り値

Slide 38

Slide 38 text

まとめ

Slide 39

Slide 39 text

まとめ • PHPからWin32APIを扱うにはそれなりの準備が必要 • 環境依存もあるし • 使いこなすのであれば、下記が必要そう • MSDNなどのドキュメントを隅から隅まで読む時間 • Win32APIについての理解 • 準備や調査に対してのリターンが大きくないので、積極的に使 いこなす必要はあまりなさそう • PHPとは全然違う分野の理解も深まるので、「最近新しい知識 仕入れてないな」という方は試してみるといいかもしれません • 温故知新

Slide 40

Slide 40 text

雑感 • 調査前に考慮していたよりも手軽に扱えた • 個人の意見です • 本格的なGUIアプリケーションは厳しそう • やれなくはなさそうだが… • スクリプトを利用した作業を補助するくらいであれば手軽 • 処理が終わったらアラートを出すとか • エラーを出すとか • 返り値で分岐もできるので、特定の選択肢の場合は再度走らせるとか • 少し勉強したCとかC++の知識が少し役立った瞬間でした

Slide 41

Slide 41 text

参考 • PHP: COM関数 - Manual • http://php.net/manual/ja/ref.com.php • PHPでWin32APIを呼ぶ(Windows) - Qiita • http://qiita.com/joh/items/9f54c6876e356c51157 • An Automation Object for Dynamic DLL Calls | Dr Dobb's • http://www.drdobbs.com/windows/an-automation-object-for- dynamic-dll-cal/210200078 • Born's Windows Scripting Host • http://www.borncity.com/web/WSHBazaar1/WSHDynaCall.htm

Slide 42

Slide 42 text

参考 • MessageBox 関数 (MSDN) • https://msdn.microsoft.com/ja-jp/library/cc410914.aspx • No.18 msgBoxのパラメータ(早見表) • http://www.niji.or.jp/home/toru/notes/18.html