サンク(Cの関数呼び出し)について その2

このQ&Aのポイント
  • Cの関数呼び出しは、アセンブリとして見るとどんなコードになっているのでしょうか?
  • cb=(WNDPROC)VirtualAlloc(NULL,10+5 , MEM_COMMIT, PAGE_EXECUTE_READWRITE); char *b=(char*)cb; *b++= 0xC7; *b++= 0x05; *((int *)b)++=(int)(&callbackSelf); *((int *)b)++=(int)this; *b++= 0xE9; *((int *)b)= ((int)WndProcGate)-(4+(int)b); リンク先の79レス目のコードを一部抜粋致しました。 おそらくMOV命令からオブジェクトアドレス代入までが実行環境の切替か調整を行ってる部分と思うのですが、これらの値を入れる事で結果どういう作用があるのでしょうか?
  • この疑問に関する内容の書籍なりサイトがございましたら、そちらも紹介して頂けるとありがたいです。
回答を見る
  • ベストアンサー

サンク(Cの関数呼び出し)について その2

1. Cの関数呼び出しは、アセンブリとして見るとどんなコードになっているのでしょうか? 2. http://oshiete1.goo.ne.jp/qa5750174.html すみませんが、上記質問「サンク方式について」の続きとして質問させて頂きます。 http://mentai.2ch.net/prog/kako/957/957341074.html cb=(WNDPROC)VirtualAlloc(NULL,10+5 , MEM_COMMIT, PAGE_EXECUTE_READWRITE); char *b=(char*)cb;            //コールバックゲートをこしらえる *b++= 0xC7; *b++= 0x05;       //MOV [adr],xxxx *((int *)b)++=(int)(&callbackSelf); //メモリアドレス *((int *)b)++=(int)this;       //オブジェクトアドレス *b++= 0xE9;             //jump long xxxx *((int *)b)= ((int)WndProcGate)-(4+(int)b);//ジャンプ先 リンク先の79レス目のコードを一部抜粋致しました。 おそらくMOV命令からオブジェクトアドレス代入までが実行環境の切替か調整を行ってる部分と思うのですが、これらの値を入れる事で結果どういう作用があるのでしょうか? また、この疑問に関する内容の書籍なりサイトがございましたら、そちらも紹介して頂けるとありがたいです。

質問者が選んだベストアンサー

  • ベストアンサー
  • salsberry
  • ベストアンサー率69% (495/711)
回答No.1

1. 適当なCのプログラムを逆アセンブルしてみるなり、Cコンパイラからアセンブリ言語のファイルを出力させたりして、ご自分で見てみればいいのでは? Windows用のVisualC++を使っている場合は > dumpbin /disasm FILENAME.obj とか > cl /FA /FaFILENAME.asm /c FILENAME.c とか。 2. そのcbから始まるアドレスに書き込まれたコードでやっていることは、  Win構造体のcallbackSelfメンバーに、thisオブジェクトのポインタを書き込む  WndProcGate関数へジャンプする の2つです。 この状態でcbのアドレスを呼び出すと、WndProcGateを経由してWndProc関数が呼び出されます。 > これらの値を入れる事で結果どういう作用があるのでしょうか? makeClass()を呼んだときのthisによって、コールバックのときに実行されるWndProc()を切り替えられるようにしています。Winを継承してWndProc()をオーバーライドできるようにするための仕掛けのようです。 オブジェクト指向とかウィンドウプログラミングの知識がないと何のことだか分からないでしょう。

sankaku197
質問者

お礼

ありがとうございます。 dumpbinとclの事は知らなかったのですが、これ本当に便利ですね。 おかげさまで疑問のほとんどがclだけで解決しました。 以下、参考にしたサイトを載せておきます。 [起動方法] http://d.hatena.ne.jp/uox/20080302/p2 [dumpbin] http://d.hatena.ne.jp/language_and_engineering/20081008/1223384382 http://www.xlsoft.com/jp/products/intel/cvf/docs/vf-html/pg/pg26_10_02.htm [cl] http://besky-works.spaces.live.com/Blog/cns!555CF2E2F9E31C71!499.entry http://msdn.microsoft.com/ja-jp/library/367y26c6(VS.80).aspx >makeClass()を呼んだときのthisによって、コールバックのときに実行されるWndProc()を切り替えられるようにしています。Winを継承してWndProc()をオーバーライドできるようにするための仕掛けのようです。 ところで、これってサンクを応用しなくても以下のコードで十分なような気がします。 makeClass()のような実装にするのは、何か意味があるのでしょうか? class A{ public: A(){ callback = this; } virtual void CallBack(){ cout << "A" << endl; } static void CallBackGate(){ callback->CallBack(); } static A* callback; }; A* A::callback;

sankaku197
質問者

補足

改めてsalsberryさん、回答ありがとうございました。 もう新しい回答もなさそうなので、自分なりの結論を書いて締めようと思います。 マシンコードを直接打ち込む方法は面白く大変勉強になりました。 でも使う分には、やっぱり私の書いたコードで十分ですね。多分。

関連するQ&A

  • VirtualAllocについて

    このサイトでわからくなったので質問させていただきます。 http://mentai.2ch.net/prog/kako/957/957341074.html このサイトの79番目で void makeClass(char *WinClassName) {    cb=(WNDPROC)VirtualAlloc(NULL,10+5 , MEM_COMMIT, PAGE_EXECUTE_READWRITE);   char *b=(char*)cb;            //コールバックゲートをこしらえる     *b++= 0xC7; *b++= 0x05;       //MOV [adr],xxxx     *((int *)b)++=(int)(&callbackSelf); //メモリアドレス     *((int *)b)++=(int)this;       //オブジェクトアドレス     *b++= 0xE9;             //jump long xxxx     *((int *)b)= ((int)WndProcGate)-(4+(int)b);//ジャンプ先  WNDCLASS wndclass= { 0/*CS_SAVEBITS*/ ,//UINT  style;             cb,   //WNDPROC lpfnWndProc;             0,0,     //int   cbClsExtra,cbWndExtra;             0,               //HANDLE hInstance;             LoadIcon(NULL,IDI_APPLICATION),//HICON  hIcon;             LoadCursor(NULL,IDC_ARROW),  //HCURSOR hCursor;     (HBRUSH)    GetStockObject(WHITE_BRUSH),  //HBRUSH hbrBackground;             NULL,             //LPCTSTR lpszMenuName;             WinClassName, };        //LPCTSTR lpszClassName;   RegisterClass( &wndclass ); } の、    cb=(WNDPROC)VirtualAlloc(NULL,10+5 , MEM_COMMIT, PAGE_EXECUTE_READWRITE);   char *b=(char*)cb;            //コールバックゲートをこしらえる     *b++= 0xC7; *b++= 0x05;       //MOV [adr],xxxx     *((int *)b)++=(int)(&callbackSelf); //メモリアドレス     *((int *)b)++=(int)this;       //オブジェクトアドレス     *b++= 0xE9;             //jump long xxxx     *((int *)b)= ((int)WndProcGate)-(4+(int)b);//ジャンプ先 の処理がどういう処理を行っているのかわからないので1つ1つわかりやすくご教授ください。

  • C++DLLからC#へのコールバックに文字列の配列を渡したい

    教えてください! C++のアンマネージドDLLから、複数のファイル名(文字列)をC#に渡したいです。 ステップとしては、 C#から、C++のDLLの関数を最初に呼んで、コールバック関数を渡しています。 次に、DLLからコールバック関数を呼ぶ時に、複数のファイル名を渡したいです。 以下のコードを動かすと、、 C++からは2つ渡しているつもりなのに、 C#ではfilesがサイズ1のString配列として引数に入ってきます。 なぜでしょう? またどうすればC#から複数受け取れるでしょうか? 教えてください! C# public delegate int StringArrayCallback(String[] files); // C# -> C++ public static int start() { StringArrayCallback cb = new StringArrayCallback(onReceivedFiles); int ret = start(cb); return ret; } // C++ -> C# callback public static int onReceivedFiles(String[] files) { return 1; } C++コード typedef int (__stdcall *OnReceivedFilesProc )(char**); int start( OnReceivedFilesProc callback ) { std::string message1_ = "from C++1"; std::string message2_ = "from C++2"; char* messages_[2] = { (char*)message1_.c_str(), (char*)message2_.c_str() }; int ret_ = callback(messages_); std::cout << "[from c#]" << ret_; }

  • strcpy関数で文字型変数へのポインタを指定するとエラーになる

    (1)のコードは問題なく動作するのですが、(2)の方はバスエラーとなります。どちらもも引数としてアドレスを渡しているのに何が違うのでしょうか? (1) char ch[]="aaaa"; strcpy(ch, "bbbb"); printf("%s\n", ch); (2) char *chp="xxxx"; strcpy(chp, "yyyy"); printf("%s\n", chp);

  • C言語で文字列をかえす正しい書き方が知りたいです?

    C言語で次の警告が表示されます。 文字列を返したいのですが、正しい書き方はどのようにすれば良いのでしょうか? jci.h(20) : warning C4172; ローカル変数またはテンポラリのアドレスを返します。 char *test(char *a, int b) { char str[BUFSIZ]; return str; <------ }

  • javaのhashtableに関する質問です

    以前にも質問させていただいた者ですが、またどなたかよろしくお願いします。  javaのhashtableを使いたいのですが、ソースコードの中で int a[]= char b[]= Hashtable<int,char> hash=new Hashtable<int,char>();       ~ hash.put(int[a],char[b]); と、put(,)の中にint型とchar型の配列を格納することは可能でしょうか?どうしてもエラーが出てしまいます。   どうしたらいいのでしょうか?

  • 関数の戻り値なんですが...

    VC++を使用し以下のような関数additionで文字列を返したいのですが アドレスしか返しません。配列はその先頭のポインタだということは しっているのですが.... additionは、二つの文字列(32や47などの数字のやつ)を引数とし、そ れを整数型に変換し、その加算を行い、その結果を文字列型に変換し 、その文字列をかえす関数です。 関数の定義などが間違っているのですか?? #include<iostream.h> #include<string.h> #include<string> char *addition(char *,char *); void main(){ using namespace std; char a[] = "1000"; char b[] = "456"; cout << addition(a,b) << '\n'; } char *addition(char *a,char *b){ int c = 0; c = atoi(a)+atoi(b); char p[20]; _itoa(c,p,10); cout << p <<'\n'; return (*p); }

  • isalpha()関数について

    isalpha()関数について ◆開発環境 OS:Linux(Ubuntu9.10) コンパイラ:gcc4.4 IDE:eclipse 言語:C 《質問内容》 現在、char型配列A[8](最大8桁の文字列が格納されるため)に英字から始まる文字列(英字・数字)を入力し、 2文字目の文字を英字かどうか判別し、if文で分岐させようとしているのですが、 この2文字目の文字をisalpha()にかけても、英字と判別せず、思い通りの処理ができない状態で 困っています。 現状、以下のソースですと、"D0"ならelse ifに入りますが、"SM0"と入れるとifに入らずに、else ifに入ってしまいます。(2文字目は英字なのに…) 以下に、参考ソースを添付します。 どなたか、お分かりの方いらっしゃいましたら、ご教授お願いします。 できれば、isalphaの使い方等も詳しく教えていただけたら幸いです。(webで調べるとisalpha(int c)と書いてあり、文字の判別にint?とかなり疑問です。一応、ソース上ではchar型にキャストしてますが。) 《ソース》 char A[9]="";      //最大8桁の文字列(英字・数字)が入る char B[3]="";        //A,B入れ替え用バッファ char C[7]=""; //最大6桁の文字列(数字)が入る char code[2]="";      //判別用バッファ int C_val; //Cの10進値格納変数(後で使う) fscanf(stdin,"%s",&A); strncpy(code,,A+1,1);                  //code[2]に"0"をコピー if( (unsigned char)isalpha(code) != 0){          //codeが英字なら先頭2文字は英字 strncpy(B,A,2);             strncpy(C,A+2,strlen(A)-2); C_val=atoi(C); //Cの10進化 strcpy(A,"");    //Aの初期化 strcpy(A,B); strcpy(B,""); //Bの初期化 } else if( (unsigned char)isalpha(code) == 0){    //codeが英字でないなら先頭1文字が英字 strncpy(B,A,1); strncpy(C,A+1,strlen(A)-1); C_val=atoi(C);          //Cの10進化 strcpy(A,""); //Aの初期化 strcpy(A,B); strcpy(B,""); //Bの初期化 }

  • 構造体メンバがポインタであるときの代入

    typedef struct WRITE_BUF_TYPE{  byte adr_h; // ワードアドレス上位  byte adr_l; // ワードアドレス下位  byte *buf_adr; // 送信/受信先 アドレス  byte cnt; // 文字数 }; struct W_BUF_TYPE b[10]; 構造体、データ定義を上のようにしています。 b[10]の空きを探して書き込むサブルーチンを作ったのですが、*buf_adrの設定方法がわかりません。 ご存知の方、教えてください。 サブルーチン sub_func(int8 *adr){  byte i;  for(i=0;i<10;i++){    if(b[i].adr_h==0){  // b[10]の空き検索    b[i].buf_adr=*adr; // アドレスを設定する。ここでエラーとなります。  } } サブルーチンでバッファのアドレスを受けて、b[10]の空きエリアに設定するプログラムです。

  • アセンブラ、ファンクションコールの使い方

    入力した一文字を改行して表示させるものを作ってます。 以下のものを書いてみまして、 一文字読み取って改行はしますが表示されません。 アドバイス頂けますでしょうか? 環境は MASM 32 です。 name double .model small .stack 100 .data prompt db 0ah,0dh,"? $" .code start: mov ax, @data mov ds, ax lea dx, prompt mov ah, 9 ;output string int 21h mov ah, 1 ;get keyboard input and store into al int 21h mov dl, 0ah mov ah, 2 int 21h mov dl, 0dh mov ah, 2 int 21h mov dl, al mov ah, 2 ;output char from dl int 21h mov ax, 4c00h ;exit int 21h end start

  • C言語で型汎用性のある関数を作成したい

    C言語です。 下記のコードでは、期待した通りにメンバにアクセスできるのですが、 構造体のメンバが多くなったとき、構造体をネストしているときには、 挙動がおかしく(なにか別の値をとりだしてしまっている?)なります。 どのようにしたら型汎用のある関数を書くことができるでしょうか? (C++ならば、汎用(テンプレート)関数があるけれども。。。) #include <stdio.h> struct hanyo{ int i; }; struct aaa{ int i; long l; char str[100]; }; struct bbb{ int i; }; void hoge(hanyo *ph){ printf("%d\n", ph->i); return; } int main(){ aaa a; a.i = 1; bbb b; b.i = 2; hoge((hanyo *)&a); hoge((hanyo *)&b); return 0; } ----実行結果---- 1 2 続行するには何かキーを押してください . . .

専門家に質問してみよう