SDKでの文字列検索で躓いています

このQ&Aのポイント
  • 最近”猫でもわかる”でSDKの勉強を始めました。現在第76章を勉強中なのですが、この章で躓いております。
  • この章では文字列検索の方法を学んでいますが、特に最後の文字列検索部分が理解できません。
  • また、DWORD型やポインタの扱いについても疑問があります。分かる方からのご指導をお願いします。
回答を見る
  • ベストアンサー

SDKにて

最近”猫でもわかる”でSDKの勉強を始めました。 現在第76章を勉強中なのですが、この章で躓いております。 分からない部分は最後の文字列検索部分。 dwCurPos = SendMessage(hEdit, EM_GETSEL, 0, 0); この章の始めにこの関数の返り値として、選択されている(黒く塗られている)部分の先頭文字のアドレスが下位ワードとして格納されていると説明があります。 よって自然に、返り値を DWORD型 で宣言すれば良いと分かりました。 しかしその後こんなことを行っています。 dwPos = LOWORD(dwCurPos); dwPosは DWORD型で宣言されているのですが、これはdwCurPosの下位ワードのみdwPosに代入していると考えてよいのでしょうか? そしてその後 lpFindPoint = strstr(&buf[LOWORD(dwPos)], lpfr->lpstrFindWhat); のように記述されています。 この関数の意味は第一引数の文字列内に、第二引数の文字列が発見された場合、最初に出現した場所のポインタを返す関数と説明があります。 それは分かるのですが、気になるのが第一引数です。 &buf[LOWORD(dwPos)] 先ほどのプログラムで下位ワードのみ代入された?dwPosの下位ワードを配列の変数として扱っています。 たしかにDWORDは32bitであり下位ワードならば16bitとなるので、int型とサイズは合うのですが、このLOWORD(dwPos)をint型として扱ってよいのでしょうか? いや、そのように扱っても問題はないのでしょうか? そして最後に dwPos = lpFindPoint - buf; のように書かれています。 lpFindPointおよびbufはアドレスであり、dwPosは先ほど同様DWORD型で宣言されています。 この計算式の結果dwPosにはどんな値が格納されるのか考えられないのですが、予想としてはこの二つのアドレスの差をunsigned long型にキャストして格納していると考えているんですが、こんな考えでよいのでしょうか? なにぶん一人で勉強しているので毎回毎回問題解決に苦労しています。 分かる方はご教授よろしくお願いします。

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

  • ベストアンサー
  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.2

★文字数ではなく『選択範囲されている終了オフセット値』です。 ・『オフセット値』とはテキストの文字列の先頭からの文字位置を表したもので、  ちょうど配列の指定と同じになります。つまり、テキストの文字列の最初から  0、1、2、…と数値が増えていきます。もし、テキストの最初に『mail』という  文字列ならば、  『m』→『0』  『a』→『1』  『i』→『2』  『l』→『3』  ←次の文字が『4』 ・となりますが範囲選択の指定では、  開始位置→『0』…mailの『m』の場所  終了位置→『4』…mailの『l』の次の場所  を指定するのです。 ・確かに表現がちょっと正しくないですね。私も前回の回答で  『選択範囲されている終了オフセット値』と表現してしまいましたが、正しくは  『選択範囲されている終了オフセットの次の値』となりますね。 ・よって  WPARAM…『範囲選択の開始位置=始点』  LPARAM…『範囲選択の終了位置=終点』の『終点』は範囲選択された『終点』の  次の位置のことを指した言葉です。決して文字数ではありません。しいて言えば  『終点』=『始点』+『文字数』のことになります。 ・つまり、始点が『12345』で文字数が『1000』ならば、  『始点』…12345  『終点』…13345=12345+1000 となります。 ・以上。おわり。→表現の意味の解釈が説明不足という事ですね。

参考URL:
http://www.winapi-database.com/Message/EM/EM_SETSEL.html
noconan
質問者

お礼

的確な説明ありがとうございました! 確かに、文字列の長さを指定したとすると strlen(lpfr->lpstrFindWhat)+dwPos このように、「文字列長さ+選択開始位置」はおかしいですよね。 なるほど。終了オフセットの次の値になるんですね。 色々とご教授ありがとうございました。

その他の回答 (1)

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.1

★おはようございます。 ・まず『dwCurPos = SendMessage( hEdit, EM_GETSEL, 0, 0 );』で取得した『dwCurPos』には  下位16ビット…選択範囲されている開始オフセット値(アドレスとはちょっと違う)  上位16ビット…選択範囲されている終了オフセット値(アドレスとはちょっと違う)  が 32 ビットの DWORD 型の整数値でセットされます。  『dwPos = LOWORD(dwCurPos);』は下位ワードのみを dwPos に代入する処理と考えて良いです。  『dwPos = HIWORD(dwCurPos);』は上位ワードのみを dwPos に代入する処理と考えます。 ・『&buf[LOWORD(dwPos)]』の『LOWORD(dwPos)』は int 型で扱っても良いです。  理由は、dwCurPos を使って 16 ビットで範囲位置を扱っていますので int でいいのです。  もしも、EM_GETSEL メッセージの戻り値を使わずに wParam、lParam に long 型の変数のポインタを  引数にセットする使い方ならば、int 型ではなくて long 型(DWORD型)にしないといけません。  つまり、『SendMessage( hEdit, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd );』という使い方  ならば long 型(DWORD型)にします。 ・『dwPos = lpFindPoint - buf;』は検索文字列の見つかったオフセット値を計算しています。予想通りです。  でもキャストした方が良いでしょう。→『dwPos = (DWORD)(lpFindPoint - buf);』とね。 ・上記の解説で疑問は解けましたか? 最後に: ・選択範囲を 16 ビットして処理していますのでテキストが 65535 バイト以上だと正常に検索できなくなると  思います。注意して下さい。改良するには『SendMessage( hEdit, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd );』  として開始オフセット値、終了オフセット値を 32 ビットとして取得して処理すれば対応できます。 ・以上。おわり。→ちゃんと理解していると思いますよ。

参考URL:
http://www.winapi-database.com/Message/EM/EM_GETSEL.html
noconan
質問者

お礼

的確な回答ありがとうございました。 一応予想はあっているらしいですね(笑) 自分としては配列変数を無理やり &buf[(int)(LOWORD(dwPos))]としてint型としてキャストを行っていたのですが、意味がないですね^^ ついでにすまないんですがもう1つ質問しても良いでしょうか? カーソルをつける作業に SendMessage(hEdit, EM_SETSEL, dwPos, strlen(lpfr->lpstrFindWhat)+dwPos); を行っています。 このEM_SETSELメッセージを送信する場合、WPARAMの値はstart位置、LPARAMはend位置と説明にありますが、これは間違いではないでしょうか? 例えば mail という文字を検索する事を考え、buf[0]から検索したとして最初から見つかったとします。 そうすると上記のプログラムだと、 SendMessage(hEdit, EM_SETSEL, 0, 4); ということになります。 プログラムの説明からbuf[0]からbuf[4]までをカーソルすることになりますが、これでは5つにカーソルをつけてしまうため1つ余計です。 しかし、このプログラムを実行させた場合きちんと文字列部分にカーソルがつくため、プログラム自体は間違えていないと思います。 結論から言うとプログラムの説明が間違えていたのかなと考えました。 つまりあのプログラムの説明は、WPARAMにカーソルの始点を、LPARAMに始点からのカーソルをつける文字数。 ということでよいのでしょうか?

関連するQ&A

  • 配列を返す

    ファイルから読み込んだ一行の文字列を読み込みカンマごとに区切って 返すというプログラムを関数化することで効率を図りたいと思います。 int main() { char buf[1000]; char *str; char *bufG; //ファイルを読み込む  while(fgets(buf,1000,fp) != NULL){//一行ずつ読む str = buf;//先頭アドレスを指す     bufG = //文字列を返す関数  ・  ・  ・ } } //文字列を返す関数 {    for(i = 0; *str != ',' && *str != '\0'; i++){ if(*str == '\n'){ bufG[i] = '\0'; } else{ bufG[i] = *str; } str++; } bufG[i] = '\0'; return bufG; } 前の質問で自動変数でこの関数を抜けたら廃棄になるというのは わかったんですが(そういう警告がでました) ここからどのようにすれば求めるプログラムになりますか? 引数とかちょとわからないので関数定義を書きませんでした。 (1)ファイルをよみこむ (2)一行ずつ読み込み文字列をbufにいれる (3)ポインタstrをbufの先頭アドレスにする (4)get_word関数にてポインタをずらしていき カンマがあればそこまでの文字列を返す (5)main関数に戻り変数に代入する (6)終端文字があるまで(4)ー(5)を繰り返す。 (7)さらに行数分繰り返す これらの一連の流れをやりたいのですが わかりません。

  • コールバック関数

    お世話になります。VB.NETにてソフト制作をしています。 今回、DLLの関数を利用する事になりましたが、うまく宣言できず困っています。御教授お願いします。 DLLはC++で作られており変更できません。 DLLは通信を補助するための物で、初期設定の関数と通信開始の物があります。 初期設定用を呼んだ後、通信開始を行なうようです。 説明には下記のような内容がありました。 【初期設定用】  initial(DWORD ip, LPNOTIFICATIONFUNC notificationFunc); ip=相手先のipアドレス  notificationFunc=コールバック関数へのポインタ コールバック関数    WINAPI *PNOTIFICATIONFUNC( DWORD id, BYTE bySet1, BYTE bySet2, BYTE bySet3, BYTE *data, DWORD datasize ); 【通信開始用】  start(DWORD ip BYTE *code ); ip=通信元のipアドレス code=相手のコードの格納アドレス 以上 全体的にどう宣言したら良いのか解りません。 (特にコールバック関数の宣言と、ポインタへの引数の渡し方) すみませんが、御教授お願いします。

  • 【C言語教えて下さい!】文字検索

    引数として、複数の文字列を格納した配列、その文字列数、あるいは文字列aを与え、aが複数の文字列を格納した配列中に見つかれば1を返し、見つからなければ0を返す関数find_string()をつくりたいのですがよくわかりません。 どなたかご教授お願いします。 ヒントや考え方など教えて下さい。

  • Redimについて

    redimで宣言した変数に文字列を格納してshll関数でほかのexeに変数を渡してその文字列を使いたいのですが中身が見れません。どうしたら見れるでしょうか?

  • C言語

    以下のC言語のプログラムを教えてください。 お願いします。 (1)標準入力から文字列(2 文字以上)を入力し,文字数を計上すると共に,入力された文字列の逆順に入れ替える処理を実現してください.なお,以下の要件を満たしたプログラムを作成してください. ・ 入力された文字列は,char 型の配列(要素数50)で受け取ること ・ 文字数を計上するcount 関数(引数:配列のアドレス,戻り値:文字数)を定義 し,main 関数より呼び出すこと ・ 文字列を逆順に入れ替えるreverse 関数(引数:配列のアドレス,戻り値:無し) を定義し,main 関数より呼び出すこと ・ 標準出力の処理は,main 関数で記述すること 【プロトタイプ宣言】 int count(char *str); void reverse(char *str); 【実行結果】 文字列を入力してください(2 文字以上) apple 文字数 = 5 入れ換え前 apple 入れ換え後 elppa (2)char 型の配列(要素数50)を2 つ宣言し,標準入力から2 つの文字列を入力してください.そして,格納した字列を入れ替える関数(swapstr 関数)を作成し,入れ替え前と入れ替え後の配列内の値(文字列)を配列名とともに標準出力するプログラムを作成してください. 【プロトタイプ宣言】 void swapstr(char *str1, char *str2); 【実行結果】 2 つの文字列を入力してください apple strawberry 入れ換え前 配列str1 = apple 配列str2 = strawberry 入れ換え後 配列str1 = strawberry 配列str2 = apple

  • VBA&c++のデータ受け渡り

    excel VBAを使用し、セルに入っているipアドレスを文字列をc++で作成したDLLファイル内にある関数に引数として渡したいのですが、関数内で確認すると旨く渡っていません。(正しく表示されない) 何か特別な方法やアドバイスが有れば宜しくお願いいたします。 現在の状況は文字列をstringに入れそのアドレスを引数としています。DLL内関数の引数の型はcharのアドレスです。

  • ポインタを引数で使用する場合

    初心者です。 ローカル関数でポインタを引数で使用する場合の定義で int A (int *x,int *y) とする場合の*はポインタの宣言としての*なのでしょうか? これまでの例題ではメイン関数のなかでポインタを宣言しアドレスを代入し・・・という使い方だったのですがローカル関数で引数を使用するさいはメイン関数内ではポインタの宣言はないので関数の定義と同時にint *x とint *yを宣言するという事なのでしょうか? それ以降の*は間接参照演算子ですね。

  • VC6.0で作ったライブラリをVBで呼び出したい

    Vc++6.0で作ったライブラリをVBで呼び出そうとしています。 void*型の引数を呼び込みたいのですが、宣言部でどのように書けばよいのでしょうか? 呼び出し時引数をどのような形で渡せば良いのでしょうか? また返り値をPVOID型とするとどのように宣言部で書いて、呼び出したらよいのでしょうか? 例 void DeleteDllClass( PVOID oBj );という関数を   VBから呼び出すと?   PVOID CreateDllClass();をVBから呼び出すと? 調べてみましたがイマイチ分からなくて・・・・

  • 配列の参照渡し

    以下のC言語のプログラムを教えてください。 お願いします。 (1)標準入力から文字列(2 文字以上)を入力し,文字数を計上すると共に,入力された文 字列の逆順に入れ替える処理を実現せよ.なお,以下の要件を満たしたプログラムを作 成すること. ・ 入力された文字列は,char 型の配列(要素数50)で受け取ること ・ 文字数を計上するcount 関数(引数:配列のアドレス,戻り値:文字数)を定義 し,main 関数より呼び出すこと ・ 文字列を逆順に入れ替えるreverse 関数(引数:配列のアドレス,戻り値:無し) を定義し,main 関数より呼び出すこと ・ 標準出力の処理は,main 関数で記述すること 【プロトタイプ宣言】 int count(char *str); void reverse(char *str); 【実行結果】 文字列を入力してください(2 文字以上) apple 文字数 = 5 入れ換え前 apple 入れ換え後 elppa $ ---------------------------------------------------------------------------- (2)char 型の配列(要素数50)を2 つ宣言し,標準入力から2 つの文字列を入力せよ. そして,格納した字列を入れ替える関数(swapstr 関数)を作成し,入れ替え前と入れ替 え後の配列内の値(文字列)を配列名とともに標準出力するプログラムを作成せよ. 【プロトタイプ宣言】 void swapstr(char *str1, char *str2); 【実行結果】 2 つの文字列を入力してください apple strawberry 入れ換え前 配列str1 = apple 配列str2 = strawberry 入れ換え後 配列str1 = strawberry 配列str2 = apple

  • PHPの参照わたしについて

    PHPの参照わたしについて質問です。 例えば、ユーザー定義関数の引数を参照渡しとする場合 function test(&$string){ $string .="参照渡し"; } $aaa = "文字列"; test($aaa); print $aaa; 等とすると、一切値のコピーがおこなわれませんよね? 次に function test($string){ $string . ="参照渡し"; return $string; } $bbb ="文字列"; $ccc = test($bbb); print $ccc; と上記のようにした場合、値のコピーが行われるのは 関数の引数に渡すときと 返り値を返すときの2回行われてるんですかね? もし、値のコピーが二回行われているとするなら 次のようにしたら値のコピーは一度だけ・・・少なくとも上の例よりPHPの動作より 軽い?ものになるのでしょうか・ function &test($string){ $string .= "参照わたし"; return $string; } $ddd = "文字列"; $eee =& test($ccc); //ここで関数の返り値を参照渡しする この場合、値のコピーが行われるのは関数に引数を渡すときの1回だけでしょうか? copy on write による動きは無視しておいて、 オブジェクト指向なプログラムでなくユーザー定義関数であれば このように関数の返り値を参照わたしにした方が、理論的?には早いのでしょうか? この場合、引数も参照渡しにすると破壊的関数になるのでそれは避けたいが、 なるべく値のコピーは防ぎたいという状況だと考えてください。 実際、こんな風にすべてのユーザー定義関数の返り値を参照にして定義するなんてこと おそらくないとおもうのですが、単純にコピーの回数がきになったのです。 よろしく御願いします。

    • ベストアンサー
    • PHP

専門家に質問してみよう