また世話になります。
簡単なスクリプト言語を開発しています。
この質問の動機については次のリンクを。
http://madia.world.coocan.jp/cgi-bin/Vcbbs/wwwlng.cgi?print+200802/08020019.txt
[開発環境]
Windows XP Home SP2
VC++2003.NET(SDK)
[質問内容]
C/C++から機械語を実行することでAPI関数を直接呼び出したいです。
呼び出すための機械語(CPU命令)や、仕組みなどを教えて欲しいです。
なお、機械語はC/C++で使えるインライン・アセンブラを使って記述。
環境依存になると思いますがWindows専用のスクリプト言語と
割り切って作成しています。
情報をお持ちの方。
僕にも教えて下さい。
よろしくお願い致します。
えーっと、なんか解決のための方策が方向違いな気がする
泳ぎを覚えたい→プールに行って練習しろといわれた→プールの作り方教えて!
と話が進んでいる気がする。
本当にそんなことを覚えたいの?
> C/C++から機械語を実行することでAPI関数を直接呼び出したいです。
> 呼び出すための機械語(CPU命令)や、仕組みなどを教えて欲しいです。
C/C++からasm経由でWin32APIを呼び出すことはできないことはないですが、
かえって複雑になり意味がありません。なぜならWin32APIはC言語ベースで
作成されているからです。従ってWin32APIはC/C++言語から直接利用するの
が最も自然なやり方で、当然ながら、当該のスクリプト言語もC/C++言語で
作成するのが一本道となります。どうしてもアセンブラを使用したい場合
はC/C++言語から _asm キーワードを使用してアセンブラが記述できます。
ただし、C言語の関数コールの仕組み、C++の言語のvtableの仕組み等を全
て自前で実装しなければなりません。とりあえず簡単なCのソースを/faオ
プションでコンパイルして出力ファイルで勉強しましょう。ただし、これ
らに時間をかけるのはたとえ研究目だととしてもそれほど賢い行為とはい
えません。
ご検討ください。
まずコンパイラとスクリプト言語の区別が付いていないし、そもそもスクリプト言語とは何なのかも分かっていない気がする。
そうなってしまうのはたぶんスクリプト言語が必要で作っているわけじゃなく、勉強のために作っているからじゃないかな?
いわゆる手段が目的になっている典型的なパターン。
> 本当にそんなことを覚えたいの?
DLLの関数ってC/C++とかだと関数へのポインタにセットして利用しますよね。
それで最初はAPIを呼び出すラッパを通して呼び出そうと考えていました。
(こちらの方が本当は安全だけど)
でもすべての関数をサポートするなんて無理。必要ないし。
だけど一部だけスクリプトから指定のDLLの指定のAPIを呼べれば
便利になると思っています。HSPは良く知らんけどAPIを呼ぶために
DLL名とか、引数とかいろいろと定義して呼べるらしい。
同じ事を自分のスクリプトにも組み込みたいと思っての。
という理由です。
> 本当にそんなことを覚えたいの?
覚えたいというよりは他に方法を知らないだけ。
基本的には自身で作っているので組み込みたいAPIのラッパを
作成して再コンパイルすればいい話なんだけどね。
ラッパ関数(関数へのポインタ)も増えるとちょっと呼び出すときの
分岐が大変なので共通にしたいんです。
何かいい方法ありますか?
共通化とかの。
ご要望の「スクリプトから(なるべく)直接Win32APIを呼び出したい」は
LoadLibrary()
GetProcAddress()
の2つのWin32APIを使用して実装可能です。
> ご検討ください。
はい。
> まずコンパイラとスクリプト言語の区別が付いていないし、そもそもスクリプト言語とは何なのかも分かっていない気がする。
ちょっと混同しているかも。
> いわゆる手段が目的になっている典型的なパターン。
ゲーム用のスクリプト言語です。
RPGゲーム限定とかならそこそこ作れた。
それで他の種類のゲームにも使えるように汎用的?みたいな感じを
いま作り始めています。
で。今迷っているのがスクリプトを中間コードにして実行する方法ですが
この中間コードをどの程度にすべきか迷っています。
1...Java見たいの本格的な中間コードか。
2...文字列の解析を早くするだけのコードか。
シューティング・ゲームにも対応するには早い方がいいのかと思うけど。
どう思いますか?
普通ならインタプリタなので(2)で十分と思いますが。
> ご要望の「スクリプトから(なるべく)直接Win32APIを呼び出したい」は
> LoadLibrary()
> GetProcAddress()
> の2つのWin32APIを使用して実装可能です。
GetProcAddressは関数へのポインタにセットして呼び出しますよね。
でもスクリプトでDLL名、関数名、引数を渡すにはどうすればいいのですか?
可変引数の関数へのポインタに GetProcAddress のアドレスをセットして
呼び出せば出来ますか?
あっています。この方法。
間違っていたらごめんなさい。
例えばVBScriptを考えてみると・・・
結局、CScript.exeという実行ファイルが、作成したテキストベースのスクリ
プトファイルを読み込んで、それに合ったAPI等を呼び出して実行してくれて
いるのだと思います。
つまり、仲澤@失業者 さんがおっしゃっているのは
作成したスクリプトファイルの記述を解析し、必要な関数などをDLLなどから
LoadLibrary()
GetProcAddress()
をつかって呼び出すexeを作れば良いのではないか?ということなのではない
でしょうか?
ちなみに、ゲーム作成にこのようなものを使うとおっしゃっていますが、その
スクリプトコードは、作成したゲームのRelease版にも含まれるのでしょうか?
簡単に改造や解析などの標的にされそうな気がしないでもないですが・・・
私もたまにゲームを作成しますが、例えば描画・音楽再生・効果音再生などは
DLLなどで作成し、その上に乗っかる部分をそれらのDLLを活用して作っていま
す。
これが一般的だと思っているのですが、それではダメなのでしょうか?
>スクリプトでDLL名、関数名、引数を渡す
そこまで書くくらいならスクリプト書かずにプログラム書くよってなくらい本末転倒な気がしないでもない。
スクリプトってのは後々楽する為にあるようなもんだと思ってたんだが。
>ラッパ関数(関数へのポインタ)も増えるとちょっと呼び出すときの
>分岐が大変なので
そこを楽に処理する為に最初に苦労するんじゃねーの?
おつかれ様です。どらさんのおっしゃることはその通りです。
一般にはその方法しかありませんが、
しかし、当初の話は。
int a = hwnd;
int b = TRUE;
_asm{
mov eax, esp ;
push b ;
push a ;
call *ptrfunc_EnableWindow ;
mov esp, eax ;
}
とかって、やりたがっていたんぢゃなかったでしたっけ。
そのヒントを書いたつもりだったのですが・・・。
ちなみに上のコードは擬似コードで、この通りやれという意味では
ありませんが、ぜひ挑戦して結果を聞きたいです。
がんばってください。
> スクリプトコードは、作成したゲームのRelease版にも含まれるのでしょうか?
はい。
完成後はバイナリコードをリソースなどのデータとして持たせます。
作成中はテキストファイルを処理してデバッグを行い、完成後は
バイナリコードに変換して処理させる仕組みです。
> これが一般的だと思っているのですが、それではダメなのでしょうか?
確かこれだね。
いま思いましたがスクリプト言語というより、スクリプト処理のエンジンと
表現した方がいいのかもしれない。
> ラッパ関数(関数へのポインタ)も増えるとちょっと呼び出すときの
> 分岐が大変なので共通にしたいんです。
この分岐を
struct {
unsigned long (*pfunc)( unsigned long, ... );
unsigned long argn;
unsigned long args[ 32 ];
} win32api;
としてスクリプトのデータが
(1)API関数の識別コード(通し番号=0x0000〜0xFFFF)
(2)引数の個数
(3)引数のデータなどのポインタ(配列)
とした場合に win32api 構造体で API 関数の呼び出しを1つに
まとめれば分岐処理をするより早くなるかと思っていたのです。
なお、スクリプト処理エンジンの初期化で使用するAPI関数を
LoadLibrary、GetProcAddress で必要と思うものをすべて取得して
関数アドレスのテーブルとして持てたらテーブル配列に添え字で
(1)の識別コードを使ってアクセスするだけでAPI関数を分岐できると
思っています。
このような理由からC/C++の呼び出し規則などを機械語レベルで
探りたかったのです。C言語とかでは引数はスタックに積まれて
呼び出される仕組みらしいと聞いた事があります。
インライン・アセンブラもテキスト本でほんの少しだけ触れただけで
実際に使ったことはないんです。理由は機械語が分からなければ
使えないから。mov、push、pop、call の大まかな動作は分かりますが
どのレジスタに何を入れればよいかが分からなかったので質問してみた。
いろいろと考えて自分でも本末転倒だなと思い始めています。
もう一度ゲーム、スクリプト処理エンジンの全体を見直してみます。
ゲームが完成後は exe と dll とスクリプトデータを一体化して
コンパクトに作り上げたかったのです。
直接アセンブリコードが載っている訳ではありませんが、関数の呼び出し規約については
これが参考になると思います。
「Argument Passing and Naming Conventions」
http://msdn2.microsoft.com/en-us/library/984x0h58%28VS.71%29.aspx
Win32 APIは大抵 __stdcall だったはず。
いろいろと検索していたら次のリンクを見つけました。
http://www.ac.cyberhome.ne.jp/~mattn/cgi-bin/blosxom.cgi/software/lang/asm/20080107162318.htm
探していた方法はこれで見つかりましたが、
もう一度全体の構造を見直してみます。
皆様。
いろいろとありがとうございました。
また何かあれば書きこんで下さい。
ときどき覗きます。
ツイート | ![]() |