Re: アセンブラで使いたいのですが ( No.6 ) |
- 名前:管理人 日時:2017/09/24 23:05
お待たせしました、呼び出し方式を stdcall に変更したものをアップしましたので、
よろしければお試しください m(_ _)m
https://dxlib.xsrv.jp/temp/DxLibDotNet.zip // Windows版 .NET用
DxLib.dll の作成に使用していた def ファイルの記述の仕方が誤っていたようで、
DxLib.dll と一緒に出力される DxLib.lib に含まれる関数は stdcall 方式になっていたのに、
DxLib.dll に 含まれる関数は cdecl 方式になってしまっていました (_ _;
|
Re: アセンブラで使いたいのですが ( No.7 ) |
- 名前:X 日時:2017/09/24 23:24
どうもご親切にありがとうございます!
でも今度はリンカが関数のシンボルを読み込まなくなり、さっきより動かなくなりました。
調べた方法は古風な方法で、関数の呼び出し前と呼び出し後のスタックポインタの値をDrawStringで表示してみたら
スタックポインタがずれていたのです。
新しいものをダウンロードした際前のやつを消してしまいましたが、さっきのでも何とか対処できましたので
申し訳ありませんが前のバージョンのやつをダウンロードさせていただけないでしょうか?
かさねがさね申し訳ありません。
|
Re: アセンブラで使いたいのですが ( No.8 ) |
- 名前:X 日時:2017/09/24 23:34
す、すいません前のバージョンのものはごみ箱に残っていましたのでダウンロードしなくても大丈夫です。
DXライブラリの関数を呼び出し前にスタックポインタを保存、呼び出し後保存したものを戻すという方法で強引に動きます。
プロセスが終了せず、不安定ですがこちらは私のプログラム(やWinAPI)が間違っているのかもしれません。
|
Re: アセンブラで使いたいのですが ( No.9 ) |
- 名前:管理人 日時:2017/09/24 23:56
一応元の DEF ファイルを使用する方式に戻したものをアップしました
https://dxlib.xsrv.jp/temp/DxLibDotNet.zip // Windows版 .NET用
プログラム中では明示的に __stdcall を記述しているのですが、何故 cdecl になってしまうのか謎です…
> プロセスが終了せず、不安定ですがこちらは私のプログラム(やWinAPI)が間違っているのかもしれません。
DxLib_End を呼んでいないということは無いでしょうか?
|
Re: アセンブラで使いたいのですが ( No.10 ) |
- 名前:X 日時:2017/09/25 00:14
有難うございました。
DxLib_Endは呼んでいますが、プロセスが残り、タスクマネージャで終了しています。
WinAPIを使った自前のプログラムでもよくこうなるので終了処理に何かあるんでしょう。
新しい方のバージョンのdllはPEViewなるソフトでシンボルテーブルを見たら前置詞が_dx_なっていたので、そちらでやってみたのですが
やはりリンカが認識できませんでした。
|
Re: アセンブラで使いたいのですが ( No.11 ) |
- 名前:X 日時:2017/09/25 00:44
たびたびすみません。
新しい方のDxLib.dllはMASM32のリンカ link.exeでもリンクしてみようとやってみたのですが、
DxLib.dll : fatal error LNK1136: invalid or corrupt file
と表示されました。
|
Re: アセンブラで使いたいのですが ( No.12 ) |
- 名前:管理人 日時:2017/09/25 00:48
> 新しい方のバージョンのdllはPEViewなるソフトでシンボルテーブルを見たら前置詞が_dx_なっていたので、そちらでやってみたのですが
> やはりリンカが認識できませんでした。
すみません、念のため __declspec(dllexport) の記述を残しておいたのが原因のようです
完全に元の状態に戻せた筈ですので、何度も申し訳ありませんがよろしければお試しください m(_ _;m
https://dxlib.xsrv.jp/temp/DxLibDotNet.zip // Windows版 .NET用
|
Re: アセンブラで使いたいのですが ( No.13 ) |
- 名前:X 日時:2017/09/25 04:31
ダウンロードしたdllは良好です。
終了処理の方は、
DxLib_Initが-1を返せば
ExitProcess(-1)
のみ、
通常終了は
DxLib_End()
ExitProcess(0)
でやっています。
PostQuitMessageなどはDxLib_End内で呼び出されていると思ったのではずしています。
ちなみに説明が変になってしまいましたが、MASM32付属のリンカで試した「新しい方」とは
2番目のSTDCALLの.dllの事です。
|
Re: アセンブラで使いたいのですが ( No.14 ) |
- 名前:管理人 日時:2017/09/26 00:45
無事元の状態に戻ったようで何よりです m(_ _;m
> 終了処理の方は、
> DxLib_Initが-1を返せば
> ExitProcess(-1)
> のみ、
> 通常終了は
> DxLib_End()
> ExitProcess(0)
> でやっています。
プロセスが終了しない件と関係は無いと思いますが、WinMain を ret で抜ける方法で
終了した場合もプロセスが残ってしまいますでしょうか?
> PostQuitMessageなどはDxLib_End内で呼び出されていると思ったのではずしています。
はい、DxLib_End の中で PostQuitMessage は呼んでいます
> ちなみに説明が変になってしまいましたが、MASM32付属のリンカで試した「新しい方」とは
> 2番目のSTDCALLの.dllの事です。
2番目と言いますと、No.6 の書き込みのときのものでしょうか?
では、No.9 の書き込みのときのものはリンクが成功するということでしょうか?
( 全て同じ URL なので、現在はどれからダウンロードしても No.12 のものなのでややこしいですが… )
|
Re: アセンブラで使いたいのですが ( No.15 ) |
- 名前:X 日時:2017/09/26 07:05
> 2番目と言いますと、No.6 の書き込みのときのものでしょうか?
そうです、そうです。STDCALLのものなのでしつこく使おうとして試していたのです。
No.9のものはこちらのフォルダ内のどれがどれやらわからなくなってしまいました。すいません。
終了処理についてですが、それを書いた時は終了するプログラムをこちらで実装していなかったので、
Alt+F4キーで終了していました。
キー操作のプログラムをようやく作ったので、Escキーなどを押したときメインループを抜けて終わるようなプログラムにしたところ、正常に終了できました。
その場合も、Alt+F4キーで終了するとプロセスが終了せず水面下で残ります。
|
Re: アセンブラで使いたいのですが ( No.16 ) |
- 名前:X 日時:2017/09/26 20:48
dxlib.o.oo7.jp/temp/DxLibDotNet.zipに置いて下さったものは一通り保持していますので、
問題があれば消して下さっても大丈夫です。
同じスレッドで細かい事をぐちゃぐちゃ書いて申し訳ないのですが、
2014年の ま さんの「リフレッシュレートの同期」のスレッドで、
int count = 0;
while( ProcessMessage()==0 && ClearDrawScreen()==0 && CheckHitKey(KEY_INPUT_ESCAPE)==0 ){
if( count >= 0 || count <= 1 ) DrawGraph(Aの画像) ;
if( count >= 2 || count <= 3 ) DrawGraph(Bの画像) ;
count = ( count + 1 ) % 4;
ScreenFlip();
}
で2フレームに1回描画するという例がのっていましたが、
フレームに合わせているのはどの部分ですか。
私は今のところ、仕様がよくわからないので次のような処理にしてしまっています。
-> 17ミリ秒を秒間60フレームでの1フレーム近似値として使っています。
メインループ先頭:
1.GetNowCount で取得したミリ秒単位カウント値+17を保存
2.ループ中身の処理
3.GetNowCount で再びミリ秒単位カウント値を取得
4.「1.」のカウント値 -(引く) 「2.」のカウント値分、WaitTimerでウェイト(「1.」の方が大きい場合のみ)
5.ループ先頭へ戻る
こういう風にしてしまったので、キー入力までフレーム単位で取得する事になってしまいます。
キー入力の取得などはフレーム単位以上のスピードにする場合、上記の私のやり方だと
「ウィンドウズメッセージおよびキー入力があった時ウェイトを抜けるウェイト(ウェイト時間が指定できるもの)の関数」が
必要になってしまいます。
サンプルや掲示板の他の例をみると私みたいなことはやらずにフレーム単位ループができているみたいなのですが
この辺の仕様はどうなっていますか。
|
Re: アセンブラで使いたいのですが ( No.17 ) |
- 名前:管理人 日時:2017/09/27 00:33
> そうです、そうです。STDCALLのものなのでしつこく使おうとして試していたのです。
> No.9のものはこちらのフォルダ内のどれがどれやらわからなくなってしまいました。すいません。
なるほど、そちらでしたら DLL の解析ツールなどで関数名を見ていただくと分かりますが
dx_DxLib_Init が _dx_DxLib_Init@0
dx_DrawPixel が _dx_DrawPixel@12
のように、先頭に _ 末端に @(数字) という関数名になっています( VisualC++ が自動的に関数名に追加する文字です )
こちらの _ と @(数字) が付いた関数名を指定していただければ No.6 の dll の関数も使うことができると思います…
> 終了処理についてですが、それを書いた時は終了するプログラムをこちらで実装していなかったので、
> Alt+F4キーで終了していました。
>
> キー操作のプログラムをようやく作ったので、Escキーなどを押したときメインループを抜けて終わるようなプログラムにしたところ、正常に終了できました。
> その場合も、Alt+F4キーで終了するとプロセスが終了せず水面下で残ります。
Alt+F4キーで終了する場合はウインドウは勝手に閉じますが、プログラム的な挙動としては
ProcessMessage() がマイナスの値を返してくるようになるだけなので、例えばメインループが
while( 1 )
{
ProcessMessage();
ClearDrawScreen();
DrawBox( 0, 0, 64, 64, GetColor( 255,255,255 ), TRUE );
ScreenFlip();
}
↑のような、ProcessMessage の戻り値を無視するプログラムとなっているとプロセスが何時まで待っても
終了しなくなります、なので if( ProcessMessage() < 0 ) break; のように、ProcessMessage() の戻り値が
マイナスの値となったらメインループから抜け、DxLib_End を呼び、ExitProcess や WinMain 関数から抜けるなどの
処理でプロセスを終了する必要があるのですが、その辺りは問題ないでしょうか?
> フレームに合わせているのはどの部分ですか。
ScreenFlip の部分です、この中でVSYNCが来るまで待っています
> こういう風にしてしまったので、キー入力までフレーム単位で取得する事になってしまいます。
> キー入力の取得などはフレーム単位以上のスピードにする場合、上記の私のやり方だと
> 「ウィンドウズメッセージおよびキー入力があった時ウェイトを抜けるウェイト(ウェイト時間が指定できるもの)の関数」が
> 必要になってしまいます。
>
> サンプルや掲示板の他の例をみると私みたいなことはやらずにフレーム単位ループができているみたいなのですが
> この辺の仕様はどうなっていますか。
ScreenFlip を呼ぶと VSYNC が来るまでひたすら待たされるので、Xさんが現在されている手法と同じような感じです
加えて ScreenFlip 内の VSYNC 待ちは途中で抜けることができないので
「ウィンドウズメッセージおよびキー入力があった時ウェイトを抜けるウェイト(ウェイト時間が指定できるもの)の関数」
を実装することはできません
これをされる場合は DxLib_Init を呼び出す前に SetWaitVSyncFlag( FALSE ); を実行して ScreenFlip 内で VSYNC 待ちをしないようにして、
「ウィンドウズメッセージおよびキー入力があった時ウェイトを抜けるウェイト(ウェイト時間が指定できるもの)の関数」を実装
するしかありません( その代わり VSYNC で同期を行わないのでテアリングが発生します )
あと、ScreenFlip の待ち時間はモニタのリフレッシュレートに依存していますので、Xさんがされているように GetNowCount や
WaitTimer を使用した FPS固定化処理を行っていないと、例えば『リフレッシュレート 60Hz を前提に組んだプログラムを
リフレッシュレート 120Hz の環境で実行したらキャラの動きやスクロールスピードが2倍になってしまった』という事態が
発生してしまいます
この辺りはサンプルプログラムコーナーの『13.移動速度を一定にする(1)』や『13.移動速度を一定にする(2)』で
一応解説しているのですが、考えてみたらもう少し色々な箇所で説明するべきかもしれません…
|
Re: アセンブラで使いたいのですが ( No.18 ) |
- 名前:X 日時:2017/09/27 03:48
度々本当にすみません。
> こちらの _ と @(数字) が付いた関数名を指定していただければ No.6 の dll の関数も使うことができると思います…
おしかったです。
@+数字付きの名前でやると実行ファイル作成までちゃんと通り、実行ファイルまで作成できます。
リンカで認識できなかったのは私が@[数字]をつけなかったなかったせいですね。
しかしできあがったプログラムを実行すると、パソコン再起動が必要な止まり方をします。
対_cdecl用の処理で呼び出すとDxLib_Init とDxLib_Endだけのテストは通ったので、No.6もまだ_cdeclのままの可能性がありますがよくわかりません。
ダウンロードさせていただいたNo.6のものは、DxLib.dllだけでDxLib.libがありませんので.libの方は試していません。
ユニコード版の.libがあるので時間がかかりますが後で試してみます。
> この辺りはサンプルプログラムコーナーの『13.移動速度を一定にする(1)』や『13.移動速度を一定にする(2)』で
> 一応解説しているのですが、考えてみたらもう少し色々な箇所で説明するべきかもしれません…
見出しだけ見て拝見しておりませんでした、すみません。
ProcessMessageとScreenFlipで挟んだだけのループでやる事にしました。
背景はClearDrawScreenを使わず、自前の背景を直接描画しようと思うのですが大丈夫でしょうか。
|
Re: アセンブラで使いたいのですが ( No.19 ) |
- 名前:X 日時:2017/09/27 04:28
> ユニコード版の.libがあるので時間がかかりますが後で試してみます。
ユニコード版にするのが思ったより大変なので無理そうです。
ズーズ―しくて大変申しあげ難かったのですが、次にファイルをアップしてくださる機会があれば、
STDCALLで.libでなく一つの.objに全部入れたものをお願いできますか?
実行ファイルは大きくなりますが、GoAsmと一番相性が良いので通る可能性が高いと思います。
とはいえ、_cdecl版で既に2000行作ってしまっているのでこのままやってしまうのもアリなのですが...
|
Re: アセンブラで使いたいのですが ( No.20 ) |
- 名前:管理人 日時:2017/09/28 00:31
#include <windows.h>
HMODULE DxLibDLL ;
int ( __stdcall *dx_DxLib_Init )( void );
int ( __stdcall *dx_ChangeWindowMode )( int Flag);
int ( __stdcall *dx_ClearDrawScreen )( const RECT * ClearRect ) ;
int ( __stdcall *dx_SetDrawScreen )( int DrawScreen);
int ( __stdcall *dx_DrawBox )( int x1, int y1, int x2, int y2, unsigned int Color, int FillFlag);
int ( __stdcall *dx_ScreenFlip )( void);
int ( __stdcall *dx_DxLib_End )( void);
int ( __stdcall *dx_ProcessMessage )( void);
unsigned int ( __stdcall *dx_GetColor )( int Red, int Green, int Blue);
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
int x, add ;
DxLibDLL = LoadLibrary( "D:\\DxLib\\Release\\Library\\TestPackage\\DxLibDotNet\\DxLib.dll" ) ;
dx_DxLib_Init = ( int ( __stdcall * )( void ) )GetProcAddress( DxLibDLL, "dx_DxLib_Init" );
dx_ChangeWindowMode = ( int ( __stdcall * )( int Flag) )GetProcAddress( DxLibDLL, "dx_ChangeWindowMode" );
dx_ClearDrawScreen = ( int ( __stdcall * )( const RECT * ) )GetProcAddress( DxLibDLL, "dx_ClearDrawScreen" );
dx_SetDrawScreen = ( int ( __stdcall * )( int DrawScreen) )GetProcAddress( DxLibDLL, "dx_SetDrawScreen" );
dx_DrawBox = ( int ( __stdcall * )( int x1, int y1, int x2, int y2, unsigned int Color, int FillFlag) )GetProcAddress( DxLibDLL, "dx_DrawBox" );
dx_ScreenFlip = ( int ( __stdcall * )( void) )GetProcAddress( DxLibDLL, "dx_ScreenFlip" );
dx_DxLib_End = ( int ( __stdcall * )( void) )GetProcAddress( DxLibDLL, "dx_DxLib_End" );
dx_ProcessMessage = ( int ( __stdcall * )( void) )GetProcAddress( DxLibDLL, "dx_ProcessMessage" );
dx_GetColor = ( unsigned int ( __stdcall * )( int Red, int Green, int Blue) )GetProcAddress( DxLibDLL, "dx_GetColor" );
// ウインドウモードで起動
dx_ChangeWindowMode( TRUE ) ;
// DXライブラリの初期化
if( dx_DxLib_Init() < 0 ) return -1 ;
// 描画先を裏画面にする
dx_SetDrawScreen( 0xfffffffe/*DX_SCREEN_BACK*/ ) ;
// メインループ
x = 0 ;
add = 8 ;
while( dx_ProcessMessage() == 0 )
{
// 移動
x += add ;
if( x < 0 || x > 640 ) add = -add ;
// 画面のクリア
dx_ClearDrawScreen( NULL ) ;
// 四角形の描画
dx_DrawBox( x, 120, x + 64, 120 + 64, dx_GetColor( 255,255,255 ), TRUE ) ;
// 裏画面の内容を表画面に反映
dx_ScreenFlip() ;
}
// DXライブラリの後始末
dx_DxLib_End() ;
// ソフトの終了
return 0 ;
}
その上で
// 画面のクリア
dx_ClearDrawScreen( NULL ) ;
// 四角形の描画
dx_DrawBox( x, 120, x + 64, 120 + 64, dx_GetColor( 255,255,255 ), TRUE ) ;
// 裏画面の内容を表画面に反映
dx_ScreenFlip() ;
↑この辺りをデバッガを使用して逆アセンブル表示してみたのですが
00241F35 push 0
00241F37 call dword ptr [dx_ClearDrawScreen (2AD69Ch)]
00241F3D push 1
00241F3F push 0FFh
00241F44 push 0FFh
00241F49 push 0FFh
00241F4E call dword ptr [dx_GetColor (2AD698h)]
00241F54 push eax
00241F55 push 0B8h
00241F5A mov eax,dword ptr [x]
00241F5D add eax,40h
00241F60 push eax
00241F61 push 78h
00241F63 mov ecx,dword ptr [x]
00241F66 push ecx
00241F67 call dword ptr [dx_DrawBox (2AD690h)]
00241F6D call dword ptr [dx_ScreenFlip (2AD688h)]
↑このようにちゃんと stdcall方式の記述( 関数呼び出し側が行った引数の push が関数内で pop される )で正常に動作していました
なので、私が No.5で『改めて確認したところ cdecl になっていました』と言っていたのは誤りで
( 出力関数名に _ や @(数字) が無かったので、cdecl になっていると勘違いしてしまっていました )、
やはり一番最初の何も手を加えていない状態の DxLib.dll ( そして最新の No.12 の DxLib.dll )の時点で
stdcall方式になっていたようです
『GoAsm で DLL の関数を call する記述をすると自動的にスタックポインタを操作する処理が追加される』
という可能性は無いでしょうか?
|
Re: アセンブラで使いたいのですが ( No.21 ) |
- 名前:X(解決) 日時:2017/09/28 02:09
うーん、よくわからないですね。
とりあえず長引いたので解決にします。
こちらもシングルステップ実行などでDxLib_Initを実行してみたりしてみたのですが良く分かりませんでした。
別のプログラム全般の事ですが、とりあえずdx_何たら とそのまま呼び出していると実行時にフリーズします。
STDCALLの問題ではないとするとよくわかりません。
大変お手数をおかけしました。
|
Re: アセンブラで使いたいのですが ( No.22 ) |
- 名前:X 日時:2017/09/28 09:15
(解決)と言いつつ書き込みです。
最後に頂いたNo.12の.dllで、現在私が使ったものは大体フツーに呼び出せる事がわかりました。
それで大体絞れてきたのですが、
dx_SetGraphMode を通常の呼び出し方をするとフリーズまたはメモリアクセス違反になります。
これを対cdecl用の処理を加えて(スタックポインタ(ESP)を関数呼び出し前に保存し、関数呼び出し後戻す)呼び出すと、
エラーが出ずに上手くいくという現象です。
|
Re: アセンブラで使いたいのですが ( No.23 ) |
- 名前:X 日時:2017/09/28 09:34
32ビット環境なのですが、dx_SetGraphModeの呼び出し前と呼び出し後で、ESPが4バイトずれます(引数1個分)。
SetGraphModeの引数の数は3つですよね?
COMやC++みたいに隠れ引数が入るのか何なのか・・・
|
Re: アセンブラで使いたいのですが ( No.24 ) |
- 名前:管理人 日時:2017/09/30 01:13
SetGraphMode の引数は 4つです
// 画面モードを設定する
int SetGraphMode( int ScreenSizeX, int ScreenSizeY, int ColorBitDepth, int RefreshRate = 60 ) ;
DXライブラリでは SetGraphMode のように、C++ のデフォルト引数の機能を使用して
『リファレンスには載っていないけど、実際は存在する引数』を持つ関数が幾つもありますので、
お手数で申し訳ありませんが実際の引数の数については DxLib.h に書かれている定義を参照してください m(_ _;m
|
Re: アセンブラで使いたいのですが ( No.25 ) |
- 名前:X(解決) 日時:2017/09/30 01:39
やっとわかりました。
大変お手数をおかけしました。
マニュアルを見て引数を入れていたので間違っただけでした。
|