末尾へ




日記1;事始め日記2; Network


20020606;22:00
私はプロの情報屋である。こう書くと、ゴロツキ探偵のような気分になるが、最近は、大学も情報工学科とか、情報科学科とかいわず、情報学研究科みたいになってしまっているので、こうなってしまう。プロのプログラマではないが、プログラミングは手段としてやっているので、レドモンド辺りでバグを大量生産しているプログラマよりは、はるかに腕がたつ。機械語が大好きである。コンピュータのハードの隅ずみまで精通していれば、小さくて高性能のプログラムが書けるからである。とはいえ、これで人工知能のプログラムをかくのはチョット辛いので、LISPやPROLOG(今や死語)も使うが、まあ、Cあたりが一番使い易い。BASICは子供のおもちゃで、昔、日曜プログラマ仕事というか、遊びでいじくったが、あの子供っぽい制御構造が好きになれず、やっぱりおもちゃだという印象が付きまとっている。

そんな中、60MBもある
Delphi6がコンパイラまで付いてただという事を不覚にも今頃気が付いて、いじくってみるきになった。本当は、相当昔、VBのCCEのCDがついたVB本を買ってきて少しいじくって見たが、CCEはコンパイラがついていない本物のおもちゃなのですぐに止めてしまった。そういえば、あのころ、知人がDelphiを賛歌していたが、気にしなかったのが失敗だった。だって、まさかコンパイラまでついた本格的開発環境がただだとは思わなかったのだから。PC-UNIXは別ね。GCCは遊んでいます。

download/installはちょっと面倒
 1.Delphi6とUpdate2をダウンロード;約60MB+40MB。
 2.上記で、ついでに、インストール番号とインストールキーの2つを取得。
   メールで送られてくるので、メールアドレスを準備のこと。
   これは大切に保存のこと。
 3.インストール;この時、インストーラ用にもフォルダーができるが、
   これがCD-ROMの内容になるので、消さないで保存;約140MB
   続いて、本体のインストールが始まる;約60MB
   再起動。
 4.続いて、Update2もインストール;これはフォルダは使わない
 5.Delphiを起動;使用許諾コードを取得するためにインターネット
   に接続する必要がある。この時、メールアドレスとパスワードを使う。
   このコードは、2.の2つとは違う。要するに3つのライセンスキーみたいなものが必要なのだ。
   この3つめのコードは、勝手に設定されるので、何かわからないし、管理の必要もない。

2.の2個のキーを保存しておけば複数のPCにインストールできる。インストールの度に、5.で使用許諾コードを取得するという構造である。

で、Delphi6である。早速5分ほどで落として、インストールUpdateも必ずいれましょう。これが、準備もいれると、200MBはHDDを食うので、注意。installに使ったファイルは消していいだろうと思ったら、修復セットアップに使うから残しておいた方がいいらしい。早速起動。

でも全然プログラミングできない。ここから、この日記が始まる。

書けない理由は簡単で、5000円や1万円もする入門書を買わないからである。780円1600!!円の新書(Delphiじゃなく、VB)と、Webだけでやろうとしているからである。いや、本当は、高い本でもダメかもしれないが。。。

さて、VB(いやDelphiだがどちらでも同じ事)というか、ウィンドウの本である、そしてobject orientedなプログラムには、3つの異なる状況がある。どの本にもWebにも、これが明確に分けて書かれていない。説明の流れの中では、この3つの概念が当然でてくるが、そこが、書いている方が意識していないので、大体がグチャグチャである。

3つとは、

 1.人間へのインタフェイス;
  つまり、ウィンドウ(ボタンや入力窓や、スクロールバーなど)
  の作り方。

 2.OSの機能を使うためのAPIやその他、関数;
  OSは一般ユーザにいろいろな機能を提供するだけでなく、
  プログラマにも色々な機能をAPIというモジュールで提供
  している。典型的にはファイルの入出力である。多数ある。

 3.コード;自分でかく命令群。

で、入門書でやるインチキが、1.だけ見せて、ほら簡単にウィンドウが使えるでしょ、というものである。ボタンやスクロールができたって、何にも嬉しくない。これは何をしているかといえば、速い話し、貴方のCDラジカセやオーディオのプリメインアンプや、もっと簡単なものでは、テレビの前面の操作パネル(電源スイッチや、チャネルやボリュームなど)を作っているだけである。そんなものが出来ても、何にもできない!!

操作パネル(コントロール パネル)だから、その上に配置するボタンなどの部品をコントロール呼ぶ。なんだ、ActiveXコントロールって、そういうことかって、思ったでしょ。

さて、操作パネルなんて出来ても、中身の電子回路がないとCDはならない。テレビだって映らない。当然だ。単なる箱にすぎないのだから。ここからが本当のプログラミングになる。そりゃ、VBが無かったら、ウィンドウも、ボタンもいちいちコードを書かなければならないので、大変だが、そんなところは共通なので、パッケージにしてある、ということである。問題は、この箱だけ作らせて、ホラ簡単でしょという教え方にある。もっとも、その後まで、完全に面倒をみてくれるのならいいのだが、ここで終わっては、何もできない。簡単もクソもないのである。

さて、プログラムの中身は自分がやりたいことだから、パッケージはない。多少の部品はあるだろう。それが、APIやら、関数やらである。で、入門編にはなにげなく、簡単な例題を出して、その中でAPIを使って、ネ簡単でしょ、という調子であるが、何気なく使ったその関数やAPIは、どうやってそれを使うことを知ったのか、あるいは知り得るのかの記述がない。

何かを自分で書こうと思っても、どんなAPIが、どこにあるのかが分からないのである。居候は、こりゃインチキだとわかるが、本物の初心者は、そういうものが必要であるなんてことさえ知る訳がないので、自分で何かをしようとした場合、何をしたら良いかもわからなくなり、五里霧中の状態になるだろう。罪なものだ。

たとえば,
メモをクリアする場合、TMemoのLinesプロパティのClearメソッドを使うので、下記のように書く、とあるが、どこを調べれば、TMemoというのがあるって分かるのかが、そして、他にどんなのがあるかってのが分からないのだね。

Memo1.Lines.Clear;

どこかに一覧があるのだろうが。。。どこにあるの!!!


20020606 23:30

少し分かった。メニューバーの下に、standard、Additional、Win32とかいうのが並んでいるが、これがコントロールじゃないか、Delphiでは、コンポーネントかな。で、画面左にあるオブジェクトインスペクタってのが、このコンポーネントのプロパティを設定するところ。

ということは、コンポーネントを配置して、適当にプロパティをオブジェクトインスペクタで変える→

このコンポーネントをダブルクリックで、コードを書くエディタが立ち上がる→

ここに他のコンポーネントのプロパティを

Memo1.Lines.Clear;

のように書くのだが、Memo1とか、Linesとかは、Memoとかいうコンポーネントとをパレットから探し出して、配置すれば、オブジェクトインスペクタの中を見て、探し出せるという寸法かな。

コードエディタには、上の3.コード;自分でかく命令群。というのを書く。この命令群は、
http://wgc.cside2.jp/lab2/labn2002.htm に詳しい。

でもその前に絶対ここを見ておくと 、上に書いたことが図で見えてわかりやすい。

これで、1.と3.は解決した。さて、2.はどこ!!!

まさか、コンポーネントパレットにあるのがすべてってことじゃないよね。

Win32APIを生でかいていいのかなあ。でもそんな処理系があるとはどこにも書いてないし。。。

もう寝よ。


20020607 23:27

http://www2s.biglobe.ne.jp/~aks-lab/index2.htmにある☆ Delphi Construction ☆

は、とりあえず、それなりのプログラムが、しかもほぼ完全にfool-proofに作れるようになっていて、非常に良い。昨日のここを 読んだあと、ここで書かれている、そのままをやってみたら、まあ、何となく分かってきた。非常にfool-proofに書かれているので、ほとんど迷わずに実行できた。

「Form1」の「OnCreate」イベントは、「Form1」をダブルクリックするか、オブジェクトインスペクタのイベントの項から作成することが出来ます。

この一節の最後の一句だけがfool-proofではない。オブジェクトインスペクタのイベントの項で、何をしたら作成することが出来るのかが分からない。イベントの項目に「OnCreate」という項目があるので、その右の空白欄をダブルクリックすると、コードエディタに「procedure TForm1.FormCreate(Sender: TObject);」 が書き込まれる。 迷ったのはここだけである。

さて、しかし、

第弐回(画像フォームとメインフォームの分離)

Form2.Image1.Picture.LoadFromFile(OpenDialog1.FileName);

というように、なぜ書けるのかが分からない。
Helpで、Delphiのヘルプを選び、VCLリファレンスを開く。オブジェクト/コンポーネントのユニット別では、たとえば、Imageがどのユニットに属するのか分からないので、慣れないと調べられない。因みにGraphicsかと思ったがなかった。Imageなのにネ。ExtCtrl(ExtendedControl?)にあったが、こんな事はわかるはずがない。

それで、アルファベット順で調べたが、クラスには頭にが付くので、Imageの場合、TIの項になるなんて馬鹿らしい事も覚えていないと、調べる事さえできない。

さて、Form2.Image1.Picture.の表記である。Imageのプロパティをこのヘルプで調べてみる。するとPictureが現れるので、Image1.Pictureは納得が行く。しかし、Tformのプロパティをどうみても、Imageという項はないのである。これは納得がいかないし、美しくもない。分からないのは、Imageって、Formの上にある部品であって、プロパティじゃないし、ましてメソッドでもない、イベントであるわけがない。なのに、なぜ、こんな書き方(Form2.Image1)ができるの? Formだけは例外?

まあ、しかし、PictureがImageのプロパティっていうのも相当おかしい。定義してあるから形式的には良いと言えば良いのだが、semanticsは変だ。で、なんと、PictureっていうObjectがあって、そこへリンクしてしまっているらしい。

まあしかし、考えたら、ここはプログラム言語の世界、別に実世界のsemanticsなんて関係ないので、定義したら、それでよしの世界だね。Mädchen、Fräuleinだって、Weibだって、中性だし。不合理な事はどこにでもあるさ。Form上の部品もFormにとってはプロパティでもおかしくないが、定義してよネ。

この汚さに慣れるのは暫くかかりそうだ。

ついでに、TPictureのメソッドにはちゃんとLoadFromFileがあり、これもまあ、ok。

疲れた。


20020608 22:10

上のAkさんの画像講座はすばらしい。画像なんかに興味はない人にとっても、このレクチャの構造はすばらしいのだ。Delphiの構造が実に良く分かる。Cしか使ったことのない人でもこれならOOが使えるようになる。大体、Object Orientedなんて、こけおどしだからね。

Image1: TImage; →インスタンス:クラス

で、インスタンス;クラスなんて無意味語は、やくざな人工知能のようなこけおどしだけでやっているような世界の言葉であって、堅実な堅気の世界であるプログラミングの世界に持ち込むべきではなかったのだが、そんな事を言っていては食べていけないので、こういうことになっている。Cな人達にとっても分かり易くいえば、クラスというのは、引数を入れる前のサブルーチンね。つまり、マニュアルに書いてある時は、ありゃクラスなんだね。で、プログラムの中では、実際に使うのだから引数を入れる。これがインスタンスだね。コメントはどうなんだってのは無し。まあ、クラスだが。

クラスと言わずにgenericといった方が本当は分かり易い。が、genericも変な英語だから分からないかな。インスタンスは文字通り、実例。実例というより、実際に使う形。このクラスとインスタンスという概念は、だから新しくもなんともないが、そんな事を言うと実も蓋もなくなって、研究者が食べていけないので、こういう分かりにくい言葉を敢えて使って、新しい概念のように見せるやり方は二流研究者(どっか別の項では誉めてたかな)の常套手段だ。

中学の英語で不定詞というのを習う、to不定詞というのだが、あれがクラスだ。インドヨーロッパ語族の言語学では、動詞に不定形と定形という概念がある。英語ではあまりピンと来ないが、ドイツ語やフランス語をやればすぐ分かる。動詞の形というのは、一定でしょ。ドイツ語なら、enで終わる。lieben,dankenなんて、フランス語は、例外やら、何やらで多少みだれているが、基本は3種の形しかない。一番多いのがerで終わるものだ、aimerってね。こんな形では文の中では使わない。これは辞書に載せる一般的な=genericな形だ。実際に文章にすると、主語が一人称単数なら何、二人称複数なら何というように活用する。

で、辞書に載せる形は、まだ主語の人称や数などで、形が決まっていない一般形だから、これを不定形という。主語が、IchやJeなどのように決まると、つまり文の中で使われると、動詞は主語に応じて、形が定まるので、これを定形という。不定形をクラス、文をプログラム、定形をインスタンスと置き換えれば、もう分かったでしょ。OOなんてアホみたいなものだって。

ところで、中学英語は、少し可笑しい。不定形の事を不定詞と呼んでいる。ありゃ「詞」ではないよ。で、英語は、生い立ちからして乱れに乱れた言語なので、遥か昔に不定形が一定の形をとらなくなった。で、どの形が、というか、全部不定形のまま使うので、本物の不定形が分からなくなってしまった。で、文のなかで不定形を使う時は頭にT0をつけて、これをto−不定詞という。で、toの付かない不定詞を原形不定詞というが、原形とは不定形の別称だから、不定形不定詞という訳の分からん事になっている。忘れていた、定形は、活用した形と思えばいい。

英語でも、三人称単数現在の動詞は、sを語尾に付けるので、辛うじて、定形らしくなる。また、beという不定形を持つ動詞は、典型的に定形をもっているね。am、are、isって。

日本語は印欧語族ではないが、動詞語尾は、食べる、走る、見る、遊ぶ、歩く。。。で、全てで終わってるという奇麗な不定形を持っている。ただし、日本語の動詞は、主語によって活用形(定形)が変わるわけではない。

で、繰り返すと、この不定形がクラス、定形がインスタンス。

未使用の説明用のサブルーチンがクラス。プログラム中で、引数を与えて、callする使用時サブルーチンがインスタンス。

で、プロパティは何だって?引数だよ。メソッド? これも引数でしょ。同じサブルーチンをモードを引数で与えることにやって、いろいろな事をさせることができる。メソッドって、モードを与える引数だね。

Omnipotence(arg1,arg2,arg3,arg4,.....argn, mode)

  mode =1 ; fat16ファイルを読む
  mode =2 ; fat16ファイルを書く
  mode =3 ; NTFSファイルを読む
  mode =4 ; NTFSファイルを書く
  mode =5 ; EXT2ファイルを読む
  mode =6 ; EXT2ファイルを書く
     ・・・
  mode =100000 ; 日本語を英語に翻訳する
  mode =100001 ; 英語を日本語に翻訳する
    ・・・
  mode =100000000 ; 囲碁を打つ
  mode =100000001 ; チェスを打つ
    ・・・

argは、propertyとすれば一目瞭然。
 subroutine(arg1)と書く代わりに、subroutine.arg1と書いているだけのことだ。

今日は、進んだのかな?何をしているのかわからなくなった。
おやすみなさい。

20020609

今日は日曜日、一日中Delphi三昧と脳は思うのだが、心は、なぜか、嫌がっている。プログラミングって、シンキ臭いからね。日曜位、情報とは離れていたいと脳は思うのだが、心はやりたがるという、アンビヴァレント、矛盾な状況。

で、10分PCの前にいては、5分、他事に逃げる。

AKさんのHP を復習していて(繰り返すが、ここは本当に素晴らしい。実に良くできていて、そのままやっていれば、天然自然にDelphiの構造が分かる。ほとんどイラツクことがない)、分からない事ができた。

ネガポジ反転をやっているのだが、
「その5:「Procedure NageImage」を記述する」の

//ユーザーからのメッセージを受け付ける
Application.ProcessMessages;
//キャンセルフラグのチェック(「True」の場合は処理の中止)
if CanselFlag then     //これはCanelFlagの間違いネ
Break;


が丸で分からない。 Application.ProcessMessages;
は、Helpでも見てみることにしよう。
しかし、なぜ、この1行が必要なの?

<Fomr1>の<Button4>の<OnClick>イベント
//「中止」ボタン
procedure TForm1.Button4Click(Sender: TObject);
begin
CanselFlag := True; //キャンセルフラグを「True」に変更。
end;


ボタン4は、キャンセルボタンで、このeventが起きることは良し、それで、CanselFlagが立つ。

その後、
Application.ProcessMessages;
if CanselFlag then Break; 

これでキャンセルなのだが、なんで、Application.ProcessMessagesが必要なのかが分からない

Helpでも見るか。

分かった;Helpによれば、

ProcessMessages メソッドは,アプリケーションがメッセージキューを処理できるよう,その実行を一時的に停止します。

procedure ProcessMessages;

説明
ProcessMessages メソッドを呼び出すと,メッセージキューに現在あるメッセージをアプリケーションが処理できるようになります。ProcessMessages メソッドは Windows メッセージループが空になるまで循環し,その後アプリケーションへ制御を戻します。

注意 メッセージ処理を無視すると,ProcessMessages メソッドを呼び出すアプリケーションだけに影響し,そうでないアプリケーションには影響しません。時間のかかる処理を行うときに ProcessMessages を定期的に呼び出すようにすれば,アプリケーションが描画や他のメッセージに応答することが可能になります。 注意 ProcessMessages メソッドは,HandleMessages メソッドとは異なり,アプリケーションをアイドル状態にすることはできません。


OSの都合で必要なのは分かったが、こんなものは純論理的には要らないな。

procedure TForm1.Button4Click(Sender: TObject);
begin
CanselFlag := True;
end;

その後、
       ・・・
if CanselFlag then Break; 

これで十分だ。こんな変な制御をするからWindowsはついプログラマが書き忘れて虫だらけになっているのだろう、という気がする。

次のコントラストの講座では、

//ユーザー関数(ラウンドバイト)
Function TForm1.RoundByte(data:Double):Byte;
begin
if Data >= 255 then //バイトの範囲(0〜255)に値を丸め込んでいます。
Data := 255;
if Data <= 0 then
Data := 0;
RoundByte := Round(Data);
end;


が少し引っかかった;

if data > 255 then Dta := 255;
if data < 0 then data := 0;
RoundByte := Round(Data);

でいいネ。

しかし、実はこれはプログラマは、良くやる手である。虫は、境界で出る。だから、こういう境界条件を書くところは、無駄に見えても安全サイドに倒して書く癖を付けておくことは重要なのだ。

100mの道路に10m置きに端から端まで、植木を植えました。植木は何本必要でしょう、という問題に100÷10=10本とやる人は89.2345678%位いるだろう。これが境界条件の怖さ。

Roundは、言及されていないが、そういう組込み関数があるのだろう。

さて、利家とまつでもみるか。

2002.0612 21:50

久しぶりに再開。今日は、APIが一体どういう構造になっているか調査。

実は、VNCがセキュリティの為と称してパスワードを保存してくれないので面倒でしょうがない。そこで、F_Macroを使って一応自動Loginはできたが、一寸動きが遅いので、パスワードをファイルにしておいて、VNCのパスワード入力ボックスに送り込めないかという練習問題を自分に課した。それに必要なAPIを探す事によって、その構造をしろうという寸法。

で、Delphiを起動してコンポーネントを眺めても、ヘルプを眺めてもさっぱり分からない。そんなAPIはなさそうに思えるが、本当の所はわからない。Vectorのユーティティ→設定関係にはその手のアプリが幾つかある。上のF_Macroもそうだが、「Windows操作の自動化、マウスの自動移動」などもそう。「キーボード delphi」をキーにして、Googleを調べると山のように当たるのでなかなか厄介である。その内、SendKyesというAPIがVisualBasicにあり、これを使えば良いらしい事がわかった。やってみないとわからないが、とにかくヒントである。ところが、DelphiにはこんなAPIはないという。さらに調べると、ここから入って、ここに コンポーネントとして用意されていることが分かった。ただし、Delphi4までしかない。

さらにGoogleで調べると、日本にもあった が、何と、Delphi1.0!!であり、使えない(かどうかしらないが、とにかくDelphi6用ではない)。

http://www.nifty.ne.jp/forum/fdelphi/faq/00021.htmには、下記のように書いてあり、Windows APIを使えるらしい;


Windows APIのSendMessageかPostMessageでWM_KEYDOWN/WM_KEYUP/WM_CHARなどを 送って代用する手があります。
#0NIFTYの SBORLAND LIB 2 に村松 真(PXK04012)さんのSendKeys.LZHがあります。
#0日本語版(1.0J)ではCD-ROMの\extras\examplesにも格納されています


WindowsAPIをDelphiからどのように使うのかまだ分からないので、これは後回し。NIFTYの SBORLAND LIB 2 は会員制だから、村松 真さんのHPを直接探したが、上の「日本にもあった」というのがそれ。これはだめである。

結局、なんのことはない、CD−ROMに入っているのだが、居候のはDownload版である。それで、インストールしたときに作ったインストール用フォルダを覗いたら、Info¥SendKeysというフォルダがあるではないか。

要するに、DelphiのAPIはコンポーネントのツールバー(だったか、名前は忘れた)にあるものしかなくて、その他は、どこかで探してくるしかないようだ。

http://home1.infonia.ne.jp/~delphian/delphi/ には沢山あるのだが、APIというより、アプリケーションっぽい。
また、SendKeysのようにかなり基本的なAPIもbuilt-inAPIではなく、外部API(って言葉があるかどうか知らんが)だということがわかった。こりゃ大変だ。

さて、sendkye32.pasをDelphiのフォルダのLibフォルダに入れて、Delphiのメニューのコンポーネントから、コンポーネントのインストールをした。すると、これはコンパイルしないと入れられないというメッセージが出て、okを押すと、コンパイルしたのは良いが、さて、どこにこのコンポーネンツは入ったのか、アイコンもコンポーネントパレットの(思い出したパレットだ、ツールバーじゃなくて。どうでも良いが)どこにも無いようだし、分からない。Readmeもないし、Helpもないし、どうやって使うのだろう。コードからただ呼べば良いのか?でも呼び方が分からない。パラメータの説明なしにどうやって使うの?VBのSendKeysのHelpでも読めって事?


20020614 01:53 Midnight

今日は、ちょっと寄り道。
http://www.nifty.ne.jp/forum/fdelphi/samples/00073.htmlで下記のような物を見つけた;


■説明
 keybd_eventというAPIを利用してキボード操作をエミュレートします。

中略

//キーを送るウィンドウをアクティブにします。
SetForegroundWindow(WindowHandle);
//[CTRL]キーを押す
keybd_event(VK_CONTROL, 0, 0, 0);
//「V」を押す
Key := VkKeyScan('V');
keybd_event(LoByte(Key), 0, 0, 0);
//「V」を離す動作(必須)
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
//[CTRL]キーを離す動作(必須)
keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);


これで、CTRL+Vのキーボードからの入力ををシミュレートしている。
ならば、"passwd"を送るには、単純に以下でIいいのかな;

//キーを送るウィンドウをアクティブにします。
SetForegroundWindow(WindowHandle);
Key := VkKeyScan('p');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);

Key := VkKeyScan('a');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);

Key := VkKeyScan('s');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);

Key := VkKeyScan('s');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);

Key := VkKeyScan('w');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);

Key := VkKeyScan('d');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);



問題は、WindowHandleの取得方が分からない事。ヤレヤレ。

まあ、それでも一応やってみるか、と思って、Formをダブルクリックして、これをBeginの後ろに書き込み、全てを保存実行。しても何も起きない。それはそれでいいのだが、.exeの作り方をHelpで幾ら調べても分からない。まあ、出来の良いon-lineソフトは別にして、メーカのHelpで、古今東西初心者の役に立つ物が有った試しはない。ありゃHelpというより、備忘録だね。

何のことはない。上で保存したフォルダーにProject1.exeとしてあるじゃない。実行したからコンパイルされて、出来たのかな。意外にこういう事が、Webにも書いていない。命令語の説明とかは懇切丁寧にあるのだが。

さて、他のアプリのWindow Handleの取得法はここにあった。
http://hp.vector.co.jp/authors/VA009712/take/delphi/kabeapp.htm

これによると、Windowsのアクセサリにある電卓を操作するには、

hCalc:=FindWindow(nil,'電卓'); {電卓のハンドルを取得}

として、Window Handleを取得できる。が、しかし、今度は電卓のWindowsでの名前(Caption)が電卓であることをどうやって知ったかである。どうも WinSightというソフトがいるらしいのだが、これが、
WinSightは Professional 以上でないと付いてきません

ということである。仕方が無いので他を探したら、
FinderSystem というものがあった。なんと、中高生グループの作である。Merci.

WinInfo というのもあるが、まだ使っていない。Library→VariousToolsにある。

これで、
 1.まず、VNCviewerのパスワード入力窓まで行って、
  このキャプションを取得する。 VNC Authentication であった。

 2.次に、そのウィンドウの中の子窓である入力窓(Null)と
  OKボタン窓(&OK)のキャプションを調べる。

注; Nullとは無ということ。実際の表現は、「''」となる。VNC Authenticationなら、 「'VNC Authentication'」

これらの情報から、結局、下記のようなプログラムで、パスワードを送り込めたが、最後の「OK」ボタンがこれではまだ押せなかった。結局、自分の手で押して、VNCサーバに接続できた。

パスワードを送りこむだけの為に、何やら大きなウィンドウが開くが、まあ良しとしよう。

実際の使い方は、
 1.IPアドレスを入力する窓では手入力。
 2.VNCviewerのパスワード入力窓が出る。
 3.このプログラムを起動
ということで、多少進んだが、まだ先は長い。

今日はこれで止め。眠い。


20020614

昨日使ったAPIは下のものだが、こういうのはあるんだ。

SetForegroundWindow(WindowHandle);
VkKeyScan('p');
keybd_event(LoByte(Key), 0, 0, 0);
FindWindow(nil,'VNC Authentication');
SendMessage(hOkBtn,WM_LBUTTONDOWN,0,0);

WindowsのAPIとDelphiの関係を知るには、Delphi壁の穴 がいい。

居候はCな人間だから、昨日というか、今未明、nullと書いたが、成る程、Pascalではnilか。Lispもnilだが、こういうのは概念と実表記の違い程度に思っているので全然気にしなかったのだが。

Delphi壁の穴を読んでいてDelphiとWindowsAPIの関係がやっと分かった。やっぱり、生ではつかえないのだ。WinAPIは、そちらのマニュアルを見る事にしよう。1万もするが。それから、例によって、Delphiをインストールする時に使ったフォルダのdocumentationフォルダに1000ページを越えるマニュアルがあるが、とてもじゃないけど読む気にはなれない。こういうのは辞書だから必要な時に必要なところだけを読もう。


余談;
4か月程、書き物をしていて、秋葉原に行けなかったが、昨夜久しぶりに行って驚いた、もう、店が結構入れ替わっていた。ジャンクっぽい店の割には高いなあと、相場が分かっていないのだなと思っていた店は無くなっている。全体に少し高めだが、特徴がある物を置いているところは、それなりに生き残っている。当たり前の事、資本の論理がそのまま短期で観察できる場所である。

PHSとPCの接続コネクタがある店では10円、他の店では500円である。1m程のJ11の電話ケーブルが付いているのだから、これだけでも100円はする店が結構あるのだが。超薄型カテゴリ5EのLANケーブルが10m700円だった。45cmの普通の物が100円。45cmという長さは自作でもしないとなかなかない。30cmのものが500円位して、以前、2本も買った。


20020614 22:00

さて、keybd_eventではファイルからパスワードを素直に読めない。やはり、SendKeysを使いたい。が、これがどうしていいか五里霧中。コンポーネントのインストールができたのかどうか?どちらにしてもコンポーネントパレットには現れない。Libフォルダを見るとsndkey32.dcuというバイナリらしきもチャント入っている。これをどうしていいのかわからない。この件は、暫くは、ダメだ。次、何をしようか?


20020615 (土)13:00

今日は以下を試みた;
 1)アプリの起動
 2)パスワードをいれた後、「OK」ボタンをクリック: SendMessage
 3)Form=ウィンドを出さないで、いきなりVNCviewerを起動
 4)VNCを起動した後、自動終了

1)は簡単であった

ソースの小物 (Delphi編)に以下のようにあった。ただ、ここのHPの記述は間違っているので、サンプルソースをDLして中身を見た(下記は訂正済み);


[408]他のアプリケーションを実行するには
 プログラム中に他のアプリケーションを実行するには、ShellExecuteというWindowsAPIを使用します。このAPIを使用するためにはShellAPIをUses節に記述しなければなりません
 また、3番目の引数は"*.BMP"等の関連付けされたファイルに設定することも可能です。
例;
ShellExecute(Application.Handle,'open','c:\windows\calc.exe',NiL,NiL,SW_SHOW);


赤字は居候、例は、正しく訂正してある。


//*********************************:
// VNCviewer起動; uses にShellAPI
// を加える事を忘れずに
//*********************************
ShellExecute(Application.Handle,
'open',
'd:\program\vnc\vncviewer.exe',
NiL,
NiL,
SW_SHOW);


ShellExecuteはWindowsのAPIなのだろうが、Application.Handleは宣言もしていないのにこれで動く、宣言していないというのは嘘で、身に覚えがないだけで、VCLが勝手に書いた部分のコードにあるのだろうが、何がそういう予約語で、何が自分で宣言すべきなのか、さっぱり分からない。

2)はなぜか全然効かない。

//VNCviewerPassWd Windowのハンドルを取得
  hVncPassP:=FindWindow(nil,'VNC Authentication');
//passwd窓のOKボタンのハンドルを取得
 hOkBtn:=FindWindowEx(hVncPassP,0,'Button','&OK');
 SetForegroundWindow(hOkBtn);

//マウスの左ボタンを押す
 SendMessage(hOkBtn,WM_LBUTTONDOWN,0,0);
//マウスの左ボタンを離す
 SendMessage(hOkBtn,WM_LBUTTONUP,0,0);



これで、OKボタンにフォーカスが来て、押下されているのが見えるのだが、効果がない。

パスワード窓は、captionが「&OK」とあるように「ALT+O」が利くので、これでやってみた。

//[ALT]キーを押す
 keybd_event(VK_MENU, 0, 0, 0);
 Key := VkKeyScan('O');
 keybd_event(LoByte(Key), 0, 0, 0);
 keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
//[ALT]キーを離す
 keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);


これで一応動いた。

Delphi壁の穴によると、

一引数に押したいキー(仮想キーコード)を指定します。
ShiftキーはVK_SHIFT、
CtrlキーはVK_CONTROL、
ALTキーはVK_MENUです。

これらのキーは「押しながら」操作するので、キーを離す操作が必要です。第三引数に「KEYEVENTF_KEYUP」を指定するとキーを離したことになります。また、アルファベットや数字は Byte型で指定します。本当は、VK_A とか VK_1 があるのですが、なぜかDelphiはわざと使えなくしています。その他の仮想キーコードは、Windows.pas に書いてあるのでソースコードを持ってる場合は覗いてみて下さい。



マウス関係のSendMessageは暫く 中村の里 でお勉強かな。

もう一つ非常に不可解な事がある。

Key := VkKeyScan('8');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
Key := VkKeyScan('.');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);


のようにsleepを字の合間に入れないと、スカを食って、抜ける文字が出る。8文字入れても1字しか入らなかったり、場合による。

こんなアホな仕様が世の中にあるはずが無い。ドライバならいざしらず、完全論理レベル(Temporal論理ってのは無し)のendプログラマーがタイミングを考えてコードを書くなんて馬鹿なことは無いので、何か居候が悪いのだろうがどこなのかが分からない。

後、IPアドレスを入力した後、パスワード窓が出るが、これが不定時間であるので、窓が出たことで判断させたい。

尚、下記のコードでは、IPアドレス;192.168.0.2が入った後、8秒まって、そのまま、IPアドレスの後ろにパスワードが入っていく;

192.168.0.2passwd

WindowHandleの切り替えもも効いていない。

3)も簡単であった。
http://www.nifty.ne.jp/forum/fdelphi/faq/00100.htmに次のようにあった;


2.0になってApplicationオブジェクトにShowMainFormというプロ パティが追加されました。
これをFalseにすると、メインフォームを表示しないで実行する 事が出来ます。
Falseにするタイミングは、Application.Runより前で有れば良い ので、メインフォームのOnCreateイベントや、プロジェクトソー スのApplication.CreateFormの後で大丈夫です。
メインフォームを表示したくなったら、Showメソッドを呼びます。



ということで、メインのフォームをダブルクリックしてできた下記のprocedureの下にこれを加えた。メインフォームにはコンポーネントは一切載せない。

procedure TForm1.FormCreate(Sender: TObject);
begin
//*********************************:
// Main Formを出さない
//*********************************
Application.ShowMainForm :=False ;



4)も簡単

http://www.nifty.ne.jp/forum/fdelphi/faq/00019.htmより引用;

[Q]
アプリケーションを終了する方法を教えてください (1)MainForm の FormCreate メソッドで条件によっては終了したい場合 (2)MainForm 以外(子フォーム)から終了させたい場合

[A]
(1)Application.Terminateを使えば終了できます。
 または、プロジェクトファイル(.DPR)を"例"のようにする方法もあります。
(2)Application.MainForm.Closeを使えば終了できます。
 (MainFormのプロシジャーなら、単にCloseとします)[例] ファイルを探して見つからなかったときに終了させる方法の例です。 以下のようにプロジェクトファイル(DPRが拡張子のもの)を書きます。 コマンドライン引数に指定したファイルが見つからなければすぐに終了します。

program Project1;
Forms,
Unit1 in 'UNIT1.PAS' {Form1};
{$R *.RES}
begin

if FileExists(ParamStr(1)) then
begin

Application.CreateForm(TForm1, Form1);
Application.Run;
end;
end.



最終行に以下を入れた。
//***********************
// 自動終了
//***********************

Application.Terminate ;



残務;
マウスでボタンのクリック(同一Project内では完全にできる。VNCができない)
SendKyesの使用法
タイミング(sleep)の問題
パスワードWindowが出た事をきちんと判断させたい
.exeのアイコンをDelphiの物から変える



これまでのコード;オッソロしく不細工だが、まあ、とにかく通しで動くことが目標。

unit sendpass1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
Forms,
Dialogs, StdCtrls, ShellAPI;

type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
hVncIP: HWND; {ハンドル}
hVncIPP: HWND; {Parentハンドル}
hVncPassP: HWND; {Parentハンドル}
hVncpass: HWND; {ハンドル}
hOkBtn: HWND; {ハンドル}
Key: Byte ;
implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
//*********************************:
// Main Formを出さない
//*********************************
Application.ShowMainForm :=False ;

//*********************************
// VNCviewer起動; uses にShellAPI
// を加える事を忘れずに
//*********************************
ShellExecute(Application.Handle,
'open',
'd:\program\vnc\vncviewer.exe',
NiL,
NiL,
SW_SHOW);

//これがないとカスって、しまい、正しく動かない
sleep(500) ;

//***********************************
//VNCviewerIP Windowのハンドルを取得
//***********************************

hVncIPP:=FindWindow(nil,'Connection details');
//IP textWindowのハンドルを取得
hVncIP:=FindWindowEx(hVncIPP,0,'Edit','');

//**************************************
//     IP アドレス指定
//**************************************
//キーを送るウィンドウをアクティブにする。
SetForegroundWindow(hVncIP);

//ここではIPアドレスを入力している;192.168.0.2
Key := VkKeyScan('1');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
Key := VkKeyScan('9');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
Key := VkKeyScan('2');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
Key := VkKeyScan('.');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
Key := VkKeyScan('1');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
Key := VkKeyScan('6');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
Key := VkKeyScan('8');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
Key := VkKeyScan('.');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
Key := VkKeyScan('0');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
Key := VkKeyScan('.');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
Key := VkKeyScan('2');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);

//[Enter]キーを押す
keybd_event(VK_RETURN, 0, 0, 0);
Sleep(50);
//[Enter]キーを離す
keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);
{
//IP窓のOKボタンのハンドルを取得
 hOkBtn:=FindWindowEx(hVncIPP,0,'Button','OK');
 SetForegroundWindow(hOkBtn);

//マウスの左ボタンを押す
SendMessage(hOkBtn,WM_LBUTTONDOWN,0,0);
//マウスの左ボタンを離す
SendMessage(hOkBtn,WM_LBUTTONUP,0,0);
}


//*******************************************
//Password window が出るのを3秒(適当)待つ
// パスワードWindowが出た事をきちんと判断させたい
//*******************************************
sleep(3000) ;

//******************************
// パスワードWindowが出た
//******************************

//VNCviewerPassWd Windowのハンドルを取得
hVncPassP:=FindWindow(nil,'VNC Authentication');
//パスワード入力窓のハンドルを取得
hVncPass:=FindWindowEx(hVncPassP,0,'Edit','');

//キーを送るウィンドウをアクティブにする。
SetForegroundWindow(hVncPass);
Sleep(50);
//ここではパスワードのpasswdを入力している; passwd
Key := VkKeyScan('p');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
Key := VkKeyScan('a');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
Key := VkKeyScan('s');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
Key := VkKeyScan('s');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
Key := VkKeyScan('w');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
Key := VkKeyScan('d');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
//[ALT]キーを押す
keybd_event(VK_MENU, 0, 0, 0);
Key := VkKeyScan('O');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
//[ALT]キーを離す動作
keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);

{  マウス押下が効かない。画面を見ていると押下
  は行われているのだが、有効にならず、次に進まない。
//passwd窓のOKボタンのハンドルを取得
//hOkBtn:=FindWindowEx(hVncPassP,0,'Button','&OK');
//SetForegroundWindow(hOkBtn);
//マウスの左ボタンを押す
//SendMessage(hOkBtn,WM_LBUTTONDOWN,0,0);
//マウスの左ボタンを離す
//SendMessage(hOkBtn,WM_LBUTTONUP,0,0);
}

//***********************
// 自動終了
//***********************

Application.Terminate ;
end;

end.






2002.0615 22:00

さて、やっと基本部分が動いたので、これから少しずつ、手を加える。

まずは、VNCサーバのIPアドレスと、パスワードを文字列から抽出。これは.iniから取ってくる為の準備。



unit sendpass1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
Forms,
Dialogs, StdCtrls, ShellAPI;

type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
hVncIPP: HWND; {Parentハンドル}
hVncIP: HWND; {ハンドル}
hVncPassP: HWND; {Parentハンドル}
hVncpass: HWND; {ハンドル}
hOkBtn: HWND; {ハンドル}
Key: Byte ;
IPaddress: string;
Passwd: string;
// VNCのパス
FullPath:Pchar;

i :integer ;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
//*********************************:
// Main Formを出さない
//*********************************
Application.ShowMainForm :=False ;
//*********************************
// VNCviewer起動; uses にShellAPI
// を加える事を忘れずに
//*********************************

Fullpath := 'd:\program\vnc\vncviewer.exe' ;

ShellExecute(Application.Handle,'open',Fullpath,NiL,NiL,SW_SHOW);

//これがないとカスって、しまい、正しく動かない
sleep(900) ;

//***********************************
// VNCviewerIP Windowのハンドルを取得
//***********************************

hVncIPP:=FindWindow(nil,'Connection details');
//IP textWindowのハンドルを取得
hVncIP:=FindWindowEx(hVncIPP,0,'Edit','');

//**************************************
//     IP アドレス指定
//**************************************
//キーを送るウィンドウをアクティブにする。
SetForegroundWindow(hVncIP);

//ここではIPアドレスを入力している;192.168.0.2
// 本来は、.iniからとる。
IPaddress := '192.168.0.2' ;

i:= 1;
while i<=Length(IPaddress) do
  begin
   Key := VkKeyScan( IPaddress[i] );
   keybd_event(LoByte(Key), 0, 0, 0);
   keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
   Sleep(50);
   i:= i+1;
end;

//[Enter]キーを押す
keybd_event(VK_RETURN, 0, 0, 0);
Sleep(50);
//[Enter]キーを離す
keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);

//*******************************************
//Password window が出るのを3秒(適当)待つ
// パスワードWindowが出た事をきちんと判断させたい
//*******************************************
sleep(3000) ;

//******************************
// パスワードWindowが出た
//******************************
//VNCviewerPassWd Windowのハンドルを取得
hVncPassP:=FindWindow(nil,'VNC Authentication');
//パスワード入力窓のハンドルを取得
hVncPass:=FindWindowEx(hVncPassP,0,'Edit','');

//キーを送るウィンドウをアクティブにする。
SetForegroundWindow(hVncPass);
Sleep(50);


//ここではパスワードのpasswdを入力している; passwd

Passwd := 'passwd' ;

i:= 1;
while i<=Length(Passwd) do
  begin
   Key := VkKeyScan( Passwd[i] );
   keybd_event(LoByte(Key), 0, 0, 0);
   keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
   Sleep(50);
    i:= i+1;
end;
// ALT+OでOKボタンクリック
//[ALT]キーを押す
keybd_event(VK_MENU, 0, 0, 0);
Key := VkKeyScan('O');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
//[ALT]キーを離す動作
keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);

//***********************
// 自動終了
//***********************

Application.Terminate ;
end;

end.





20020615 midnight

さて、上のプログラムはVNCサーバのIPアドレスやパスワードなどを変えようとしたら、コンパイルのやり直しである。なにしろ、192.168.0.2とかpasswdとかいう値が、プログラムの中に生でそのまま入っている。こういうのをハードコーディングhard codingといって、プログラマの恥じであるような書き方だ。まあ、変えられないようにわざとやることもあるが、これだって、ハッカーにかかれば、バイナリエディタで書き換えてられてしまう。元の長さより長くたって、一度どこかへ飛ばしてしまうから平気である。で、こんな書き方は普通以上のプログラマは設計しない。 それで、

IPaddress=192.168.0.2
Passwd=passwd
VNCpath=d:\program\vnc\vncviewer.exe
を .ini ファイルから読ませてみようとしてまた、バリアーにぶつかった。

http://hp.vector.co.jp/authors/VA015850/program/dtips4.htmlによれば、.iniファイルの読み書きはオブジェクトがあるので、それを使えばいいとのこと;

usesIniFilesを追加
・Varに IniFile: TIniFile; を宣言
・IniFileのオブジェクトを生成



Var
IniFile: TIniFile;
  ・
  ・
IniFile := TIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));

普通のINIファイルは、プログラムファイルの拡張子を置き換えたものなので、上のようにすれば良いでしょう。

IniFileのCreateは、メインフォームのCreateイベントで行えば良いでしょう。 当然ながら生成したものは破棄しないといけないので、メインフォームのCloseイベントに

IniFile.Free;

http://hp.vector.co.jp/authors/VA015850/program/dtips4.htmlより引用


http://www2s.biglobe.ne.jp/~hatena/delphi/5.htmlも。

次に、VNCviewerのフルパスをShellExecuteに渡すのだが、これがPcharである。ShellExecuteはWindosのAPIだからそうなのだろう。

http://user.shikoku.ne.jp/kasai/delphi1.htmlにそのように書いてあった;
Delphiの文字列(string型)は先頭に長さ情報を持った文字列、一方、APIやC言語で使われる文字列(PChar型)は最後がNULL(0)で終わる文字列である。

それで、string →Pchar変換がいる。
stringA :String ;
string → PCharの変換は
PChar(stringA)

これに従い、

FullPath:string;

ShellExecute(Application.Handle,'open',Pchar(FullPath),NiL,NiL,SW_SHOW);

とした。

が、これ以前の問題で、なにやら、.iniが読めない。Project.exeと同じフォルダに入れて下のように書いてあるのだが;

autovnc.iniの内容;

[Variables]
IPaddress=192.168.0.2
Passwd=passwd
VNCpath=d:\program\vnc\vncviewer.exe

で、VNCviewerが立ち上がらずに、DelphiのVCLやら、コードエディタの中に、.iniを呼んでいるデフォールトの文字列が挿入されたり、ファイル呼び出し窓が開き、”sswd”(”passwd”の一部)というファイルはありませんなどとめちゃくちゃな事になる。ということは、実は、ハンドルの取得もできていない??

//***********************************
// VNCviewerIP Windowのハンドルを取得
//***********************************

hVncIPP:=FindWindow(nil,'Connection details');
//IP textWindowのハンドルを取得
hVncIP:=FindWindowEx(hVncIPP,0,'Edit','');


これは何の役にも立っていないのか?

原因は、.iniが読めず、デフォールトで指定してある値を使用していることにある。 デフォールトで指定した値は、.iniの値とは異なるので、VNCが見つからないから、VNCviewerが立ち上がらず、よって、ハンドルが取得できない。で、

//キーを送るウィンドウをアクティブにする。
SetForegroundWindow(hVncIP);

は有効とならならないか、あるいは、たまたまhVncIPの値を持つウィンドウがあればそこへフォーカスが当たる。なければ、現在フォーカスが当たっているウィンドウが選択される。それらが、たまたまキーボード入力を受け付ける類の物だと、そこへkeybd_eventからの値が入ってしまう。keybd_eventは、フォーカスのある窓にキー入力を送り付けるからである。


.iniの読み方は、以下であるが、これで読めていない;
IniFile := TIniFile.Create('autovnc.ini');
IPaddress := IniFile.ReadString('Variables','IPaddress','192.168.0.2');
    ・・・


 16日になってしまった。 (TT)

やっと原因が分かった; Delphi Programing Beauty のおかげである。


アプリケーション名.ini というファイル名(PictureView.ini)にしようと思ったので、

ChangeFileExt(Application.ExeName, '.ini')

と指定しています。
Application.ExeName にはEXEファイルのフルパス(C:\Program Files\PictureView\PictureView.exe など)が入っているので、そいつの拡張子を、ファイル名の拡張子部分を置換した文字列を返す ChangeFileExt 関数でiniファイルのフルパス(C:\Program Files\PictureView\PictureView.ini など)へ変換して、TIniFile のコンストラクタへ放り込んでいます。 これで確実にEXEファイルと同じディレクトリにあるiniファイルが読み込まれます。 うん。ビューティだ♪


ということである。プログラム名(Project1.exe)と.iniファイル名(autovnc.ini)が最初違っていた。直すのが面倒だったので、

IniFile := TIniFile.Create('autovnc.ini');

とリテラルで書いたのがまずかった。とはいえ、同じフォルダにあれば普通は、見に行くものであるし、このように書いてあるHPもある。ところが、Delphi Programing Beautyさんによると、ChangeFileExt 関数でiniファイルのフルパスへ変換して ということである。まったく、これだからAPIは嫌いだ、約束事が多すぎて、M$のソフトがバグの山や海になっている理由が分かる。

残務;
マウスでボタンのクリック(同一Project内では完全にできる。VNCができない)
SendKyesの使用法
タイミング(sleep)の問題
パスワードWindowが出た事をきちんと判断させたい
.exeのアイコンをDelphiの物から変える

あれ、残務が全然減ってないぞ。回避ばかりしたからかな。


20020616 06:59

これまでのところを少しまとめると、冒頭20020606;22:00に書いた下記がこの日記の動機であった。

3つとは、

 1.人間へのインタフェイス;
  つまり、ウィンドウ(ボタンや入力窓や、スクロールバーなど)
  の作り方。

 2.OSの機能を使うためのAPIやその他、関数;
  OSは一般ユーザにいろいろな機能を提供するだけでなく、
  プログラマにも色々な機能をAPIというモジュールで提供
  している。典型的にはファイルの入出力である。多数ある。

 3.コード;自分でかく命令群。

で、入門書でやるインチキが、1.だけ見せて、ほら簡単にウィンドウが使えるでしょ、というものである。ボタンやスクロールができたって、何にも嬉しくない。これは何をしているかといえば、速い話し、貴方のCDラジカセやオーディオのプリメインアンプや、もっと簡単なものでは、テレビの前面の操作パネル(電源スイッチや、チャネルやボリュームなど)を作っているだけである。そんなものが出来ても、何にもできない!!


この内、2.が全然わからなかったが、これは実は今でも分からない。1万円もするWin95API本にAPI一覧があるが、多分これは使えるのだろう。だって、Win95が抱え込んでいるAPIなのだからたとえば、comctl.dllcomdlg.dll、kernel32.dllなどが抱え込んでいるはずだ。VBのものは、これはVBの機能なのだから、Delphiがやっているかどうかは運次第。無ければどこかの誰かがコンポーネント化してくれるのを持つか、自分で作る。こうなると、一覧表などないからGoogleだけが頼り。ということらしい。後は、読んでもわからん酷い作りのHelpと、インストールフォルダにあるpdf文書。

今未明、気が付いたことは、OOの書き方は、基本的にエラーチェックが甘くなる、ということ。 上のコードを見ると、実に奇麗に見えるのだが、それは全てうまくいっている時の話。たとえば、

Fullpath := IniFile.ReadString('Variables','VNCpath','d:\Program\VNC\vncviewer');

は、如何にもVNCのパスを取れているように書いてあるが、このAPIがカスって、とれなかった場合、実際、それはあったのだけれど、defaultパスのd:\Program\VNC\vncviewerは人によって違うのだから何の役にも立たない。この為、暴走した。だからここへはdefaultパスを書くのではなく、なにかエラー符号を書いておいて、Fullpathを直下で読んで、その符号ならエラー、としなければならない。

しかし、本来このエラーは、このコードが起こしたのではない。そもそも、

IniFile := TIniFile.Create( 'autovnc.ini'));

がautovnc.iniを見つけられず、したがって、それを読んでいないことが問題なので、この関数の返り値であるIniFileをチェックしておかなければならない。今の所、そのような返り値について書いてあるHPは見つけられていない。一番、いけないのは、驚天動地な事にHelpにエラーリターンの記載が無いか、どこか奥の方に隠れている事。Windowsが山海の幸じゃない、虫で溢れかえっているのはこのプログラミング哲学の為なのだネ。

尚、フルパスでなく、上のように書いた時(IniFile := TIniFile.Create( 'autovnc.ini'));)は、.iniは、ユーザフォルダじゃなく、%SystemRoot%Windowsじゃないと駄目なのだ;
注意 通常,INI ファイルは \WINDOWS ディレクトリに格納されています。ほかの場所で INI ファイルを扱うには,FileName パラメータにファイルのフルパス名を指定します。

http://www.runan.net/program/tips/API/shell32_ShellExecute.shtml
・VNCviewerを起動したShellExecuteの戻り値
 成功した場合、32より大きい値が返る。
ということで、少し手直ししてエラーチェックを入れた。

さて、
アイコン; これは http://www2a.biglobe.ne.jp/~gonta/prog004.htm を参考にした。


「プロジェクト(P)」−「オプション(O)」を選択し、プロジェクトオプションの「アプリケーション」タグで自作のアイコンの読み込みを行ってください。

なお、アイコンは、Delphiのインストール用フォルダのほうのBorlandDelphiInstall\install\Common\Borland Shared\Images\Iconsに少しある。

パスワードWindowが出た事をきちんと判断させたい

http://www2.biglobe.ne.jp/~sakai/gensfaq2.htm[Q]他のアプリのダイアログを終了させるには?  に応用できるものがあった。


以上、これだけ手を加えて(ほぼ最終版)コンパイルした物をここ(208KB) に置きますが、この日記を読んで分かるように危ないですから。御自分のリスクでお使いください。コードは下記(HTMLでは、SJISの時、半角スペースは無視されるようなので、indentが効いていません。 )。



2002.0616 midnight
どうも、入力窓とか、OK窓とかの子供窓にフォーカスしなくても入力はできる。親にフォーカスが当たって入れば、入力窓が複数存在するのでなければ、実際にはできた。それで子供窓のハンドル取得は止めにした。マウスでOK窓のクリックの問題もOK窓のハンドルに固執したからできなかったのかもしれない。

でやってみたが、駄目だった。(06月18日追記)

エラー処理後、プログラムを抜けるexit 命令(VBなら break)をやっと見つけたので、手直し。それから、autovnc.iniにPasswdを書かなければ、聞いてくるようにした。これで手入力可能。VNCサーバのIPアドレスもそうした。これでVNCサーバが複数ある時でも、簡単に切り替えられる。 (06月21日追記)

残務;
他のアプリの入力窓をclearする方
マウスでボタンのクリック(同一Project内では完全にできる。VNCができない)
SendKyesの使用法
タイミング(sleep)の問題



unit sendpass1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes,
Forms, StdCtrls, ShellAPI, IniFiles, Controls;

type
TForm1 = class(TForm)
Label1: TLabel;
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }

public
{ Public 宣言 }
end;
var
Form1: TForm1;
IniFile: TIniFile;
hVncIPP: HWND; {Parentハンドル}
hVncPassP: HWND; {Parentハンドル}
Key: Byte ;
IPaddress: string;
Passwd: string;
// VNCのパス
FullPath:string;
i :integer ; //counter

const
IniErr='ERROR';
patherr='VNCviewerが与えられたパスに存在しません' ;
inipatherr='.iniファイルにVNCへのパス記述が見つかりません' ;
hVncerr1='VNCviewerが与えられたパスに存在せず起動できません(親)' ;
hVncerr3='VNCviewerpasswd親窓が存在しません' ;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin //100000
//*********************************:
// Main Formを出さない
//*********************************
Application.ShowMainForm :=false ;

//*********************************
// .ini fileの読み込み
//*********************************
IniFile := TIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
IPaddress := IniFile.ReadString('Variables','IPaddress',IniErr);
Passwd := IniFile.ReadString('Variables','Passwd',IniErr);
Fullpath := IniFile.ReadString('Variables','VNCpath',IniErr);
IniFile.Free;
if Fullpath = IniErr then
begin
Application.ShowMainForm :=true ;
Label1.Caption := inipatherr ;
beep ;
exit ;
end;

//*********************************
// VNCviewer起動; uses にShellAPI
// を加える事を忘れずに
//*********************************

if ShellExecute(Application.Handle,'open',Pchar(FullPath),nil,
nil,SW_SHOW) <= 32 then
begin //error VNC not found path error
Application.ShowMainForm :=true ;
Label1.Caption := patherr ;
beep ;
exit ;
end;

//***********************************
// VNCviewerIP Windowのハンドルを取得
//***********************************
hVncIPP := 0;
i :=0;
//IP入力窓の親ウィンドウハンドル取得
repeat
sleep(300) ;
hVncIPP:=FindWindow(nil,'Connection details');
i := i+1 ;
until (hVncIPP<>0) or (i> 100); //100 は30秒分起動を待つ

if hVncIPP =0
then
begin //error VNC not found. handle not obtained
Application.ShowMainForm :=true ;
Label1.Caption := hVncerr1 ;
beep ;
exit ;
end;

//**************************************
//     IP アドレス指定
//**************************************
//キーを送るウィンドウをアクティブにする。
SetForegroundWindow(hVncIPP);

if IPaddress <> IniErr then
//ここではIPアドレスを入力している
Begin
i:= 1;
while i<=Length(IPaddress) do
begin
Key := VkKeyScan( IPaddress[i] );
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
i:= i+1;
end;
//[Enter]キーを押す
keybd_event(VK_RETURN, 0, 0, 0);
//[Enter]キーを離す
keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);
End;
//******************************
// パスワードWindow
//******************************

//VNCviewerPassWd Windowのハンドルを取得
hVncPassP := 0 ; //親ウィンドウ
i := 0 ;
repeat
sleep(100);
hVncPassP:=FindWindow(nil,'VNC Authentication');
i := i+1 ;
until (hVncPassP <> 0) or (i> 300); //PasswdWindowを30秒待つ
if hVncPassP =0
then
begin //error VNC not found. Passwd handle not obtained
Application.ShowMainForm :=true ;
Label1.Caption := hVncerr3 ;
beep ;
exit ;
end;
//キーを送るウィンドウをアクティブにする。
SetForegroundWindow(hVncPassP);
If Passwd <> IniErr then
Begin
//ここではパスワードのpasswdを入力している; passwd
i:= 1;
while i<=Length(Passwd) do
begin
Key := VkKeyScan( Passwd[i] );
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
Sleep(50);
i:= i+1;
end;

//[ALT]キーを押す
keybd_event(VK_MENU, 0, 0, 0);
Key := VkKeyScan('O');
keybd_event(LoByte(Key), 0, 0, 0);
keybd_event(LoByte(Key), 0, KEYEVENTF_KEYUP, 0);
//[ALT]キーを離す動作
keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
End;
//***********************
// 自動終了
//***********************
Application.Terminate ;
end; //100000
end.






20020618 0024

SendKeysについて
0612 21:50にsendkeyをコンポーネントにしてみようとして失敗したが、これはできないらしい。.pasだけしかないのだから。おかしいのはHelpをみると、コンポーネント化という項目があるが、それを読んでいくと、いつのまにかパッケージ化の話にすりかわっている。コンポーネントは.pasや.dcu単体ではつくれず、.bpl(Package Library)や.dpc(DelphiPackageCollection)というファイルが付属してないとできないらしい。なのになぜ、メニューにコンポーネント化と、パッケージ化の2つがあり、しかもできたようなふりをするのだろう?

さて、SendKeyの使い方; Delphi CD-ROMか、DL版ならインストール時にできたインストールフォルダの Info¥SendKeysというフォルダにsndkey32がある。

このSndkey32.pasを
  ファイル→開く
V で、プロジェクトの2つ目の.pasとして登録する。これを使うメインの.pasには、uses節にsndkey32を登録する。後は、以下のメインコードを参照;

なお、sndkey32.pasには詳しい解説が書いてあるが、引数はPcharだと書いてある。下記のコードのように、'電卓'、'12345'としてリテラルで書くと、何と、これはPcharになるらしい。Pascalって、内部ではリテラルはstringじゃないの???

で、string宣言すると、当然、Pchar変換が必要。でないと、コンパイルエラーになる。

  WindowName : String ;
  str : String;

  WindowName := '電卓' ;
  str := '1234567' ;
  AppActivate(Pchar(WindowName));
  SendKeys(Pchar(str), Wait);


ところで、このSendkeyは結局は、keybd_eventを使っているので、居候が書いたautovncのこの部分のコードより重くなるので、不採用。

残務;
他のアプリの入力窓をclearする方
マウスでボタンのクリック(同一Project内では完全にできる。VNCができない)
タイミング(sleep)の問題



unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, sndkey32, StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;

var
Form1: TForm1;
Wait: Boolean ;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
Wait := true ;      //これが何の為の引数か不明
AppActivate('電卓');  //Windows付属の電卓を起動しておく事
SendKeys('12345', Wait); //これで電卓に12345が表示される
end;
end.



私のホームページへ | SiliconValley-Cupertinoのページへ | メイン | 今すぐ登録