SendMessageでメモ帳へ書き込んでみよう

## delphiから、メモ帳を起動してテキストを書き込みます
![絵菜SendMessage0022.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273793/1f5ffdeb-86b9-02d4-9350-2b3daa30e2fb.jpeg)

小ネタです。
delphiは、windows API や メッセージを処理することもできます。
こんなことをやります。

・ShellExecute で、メモ帳を起動します。
・FindWindowW で、起動中のメモ帳のウインドウハンドルを取ります
・FindWindowEx でメモ帳の中の編集領域のウインドハンドルを取ります
・SendMessage で書き込みます




## 画面を作ります
__ファイル >> 新規作成 >> Windows VCLアプリケーション delphi__ と選択して、新規のアプリを作ります。
 コンポーネントのパレットから
 ・memo1
 ・edit1
 ・button1
 を貼りつけてください。
 以上で画面作りは完了です。
 C:（途中省略）\Embarcadero\Studio\Projects\　の下に適当なファイルフォルダーを作って保存してください。
__ファイル >> すべて保存__　を選択します。ファイル名やプロジェクトの名前はデフォルトのままで問題ありません。
 
## プログラム
メモ帳を起動するには、ShellExecute というWindowsのAPIを使用します。delphiから使用するために、uses節に　Winapi.ShellAPI　を書き足してください。


ちっちゃいプログラムなので全文を掲載します。
```pascal
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, 
  System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Winapi.ShellAPI;

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

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  lpClassName: Pchar;
  Hwnd_p, Hwnd_c: HWND;
begin
  //メモ帳のクラス名は'Notepad'。
  lpClassName:= Pchar('Notepad'); //Pchar型に変換('Notepad')

  //すでにメモ帳が起動しているか？　いないか？ 確認します
  //メモ帳のHandle:=FindWindowW(pChar型で'Notepad', ウインドウ名省略でnil)
  Hwnd_p := FindWindowW(PChar(lpClassName), nil);

  //FindWindowWの結果が0のときはメモ帳が起動していません
  if Hwnd_p = 0 then
    //ShellExecuteでメモ帳を起動します
    ShellExecuteA(Hwnd_p, '', 'notepad.exe', nil, nil, SW_SHOW);

  if Hwnd_p <> 0 then
  begin
    //親のハンドルでメモ帳のタイトルバーに書込
    SendMessage(Hwnd_p, WM_SETTEXT, 0, LPARAM(PChar(Edit1.Text)));

    //子のハンドル:=FindWindowEx(親のWindowHandle, 0, 入力部分の名は'Edit','')
    Hwnd_c :=FindWindowEx(Hwnd_p,0 ,PChar('EDIT'), '');

    //子のハンドルで編集画面に書込
    SendMessage(Hwnd_c, WM_SETTEXT, 0, LPARAM(PChar(memo1.Text)));
  end;
end;
end.
```
## 実験してみましょう
まず、メモ帳が起動していない状態で、上記のアプリを実行します。
button1をクリックします。
メモ帳が起動します。

そうしたら、edit1 と memo1 に何か適当にテキストを入力してから、
もう一度、button1 をクリックしてください。

![sendmessage01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273793/7746e871-b2c6-bfe4-0e0e-acbef37f221f.jpeg)

メモ帳に、文字列が転送されたら、成功です。
(なお、メモ帳のタイトルは、メモ帳にフォーカスを入れると、元に直されちゃいます)

## 説明
### FindWindowWは、起動中のウインドウを探します。
起動していないときは、結果は0です。見つかったときは、起動中のウインドウのウインドウハンドルを返します。
Pcharに変換しているのは、Windows APIは、NULLで終わる文字列を使うためです。

Hwnd_p := FindWindowW( PChar( lpClassName ), nil );　の意味は、「クラス名またはウインドウのタイトル名から、起動中のウインドウのハンドルを探してください」です。


FindWindowWのパラメータは次のとおり
| パラメータ | 意味 |
|:-|:-|
| 1 lpClassName | クラス名  |
| 2 lpWindowName | ウインドウの名（タイトルバー）  |

※参考　FindWindowW 関数
　https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-findwindoww

 この記事では、メモ帳の名前は、Notepadだとわかっているため、クラス名から探しています。
 ここで得られたウインドウハンドル宛てに、 後述の方法でメッセージを送ります。


## ShellExecuteAは、アプリを起動します。
ShellExecuteAはアプリを起動して、ウインドウハンドルを返します。
notopadつまりメモ帳は、パスが通っているらしくて、フルパスを指定しなくても起動します。本当はフルパス指定みたいです。

ShellExecuteA(Hwnd_p, '', 'notepad.exe', nil, nil, SW_SHOW);

ShellExecuteAのパラメータは次のとおり
| パラメータ | 意味 |
|:-|:-|
| 1 hwnd   | ウインドウハンドル。起動したウインドウのハンドルを返します  |
| 2 lpOperation | 編集や検索などの動作を指示。省略  |
| 3 lpFile | ファイル名。notepad.exeを指定  |
| 4 lpParameters | 起動時に渡すパラメーター。省略  |
| 5 lpDirectory |  作業ディレクトリー。省略 |
| 6  nShowCmd | 表示方法 SW_SHOW（ウィンドウをアクティブ化し、現在のサイズと位置で表示）を指定  |

※参考　ShellExecuteA 関数
　https://learn.microsoft.com/ja-jp/windows/win32/api/shellapi/nf-shellapi-shellexecutea
 
## FindWindowExは、子のウインドウを探します
FindWindowWが返すウインドウハンドルから直下の子のウインドウハンドルを返します。

メモ帳の編集領域のウインドウハンドル:= FindWindowEx(Hwnd_p,0 ,PChar('EDIT'), '');
です。メモ帳の編集領域を担当しているコントロールのクラス名は、ネットをさまよって調べた結果、'EDIT'という名前らしいです。

FindWindowExのパラメータは次のとおり
| パラメータ | 意味 |
|:-|:-|
| 1 hWndParent  | 親のウインドウハンドル  |
| 2 hWndChildAfter  | 子のウインドウハンドル。孫は探さないので 0を指定  |
| 3 lpszClass  | 探したい子のクラス名　'EDIT'  |
| 4 lpszWindow  | ウインドウの名前。省略  |

※参考　FindWindowExA 関数
https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-findwindowexa


## SendMessageは、他のウインドウへお手紙を送信します。
Windowsはマウスを転がしても、かな漢字変換をしても、メッセージで処理しています。起動中の他のウインドウへお手紙を出すのが、Sendmessageです。

メモ帳のタイトルバーに書き込むのが、
SendMessage(Hwnd_p, WM_SETTEXT, 0, LPARAM(PChar(Edit1.Text)));
Hwnd_pの中身は、メモ帳のウインドウハンドルです。

メモ帳の中身、編集領域に書き込むのが、
SendMessage(Hwnd_c, WM_SETTEXT, 0, LPARAM(PChar(memo1.Text)));
Hwnd_cの中身は、メモ帳の子ウインドウの編集領域担当のコントロール'EDIT'です。

| パラメータ | 意味　SendMessageでWM_SETTEXTを送信のとき |
|:-|:-|
| 1 hWnd  | 編集コントロールのウインドウハンドル  |
| 2 Msg | システムに定義されているメッセージ。WM_SETTEXTを指定  |
| 3 wParam  | WM_SETTEXTでは使用しません  |
| 4 lParam | 書込文字列。ゼロで終わる文字列。LPARAM( )で囲っているのは、NativeIntという「プラットフォームに依存する符号付き整数を定義したもの」です。Windowsには、３２ビット環境と６４ビット環境がありますから、ね。 |

※参考　SendMessage 関数 
https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-sendmessage

※参考　WM_SETTEXT メッセージ 
https://learn.microsoft.com/ja-jp/windows/win32/winmsg/wm-settext

※参考　NativeInt　
https://docwiki.embarcadero.com/Libraries/Athens/ja/System.NativeInt
delphiが、ネーティブコードを生成するコンパイラ言語なので、こんな機能があるんだと思いますけど、難しいね。