( ゚Д゚)ノ Delギコ猫のプログラミング相談室part2

このエントリーをはてなブックマークに追加
911Delギコ:01/09/20 23:14
       ∧ ∧  ・・・・。
       ( ゚Д゚)
       |∪ |
      〜|  | ナンカ ワケワカ
       U U

>>905 OnMessageはTApplicationにしかないと思う。
TWinControl全てにあっていいと思うけど
そしたら多重SubClass化なんてくだらない問題も解決かな。

> ところがすべての Windows メッセージに対して、Dynamic メソッドを
> 定義できるため、コントロールのソースがある場合、わざわざ WindowProc を
> 変更する必要はほとんどない

ここ以下がサパーリわかりません??
わからなすぎて、何がわからないのかもわからな・・・


>>907
スンマソン、またーくわからないでし。
罠、消えますの?PMethod(Self)^.自分のMethod??

現在、>>897
元に戻す時にTMethod(ナンカ.WndProc) = TMethod(Self.新しいWndProcメソド)
を確認して、Trueなら戻すし Falseなら例外でサブクラス化解除しないよにしてます。
IDE上からなら
サブクラス化、解除不可でCompo削除不可になっても
プロジェクトを閉じて開きなおせばたぶんCompo削除可能なのですが

プログラム上からTFuckCompoClassをCreateしたりFreeしたりすると
やぱりだめでしょうね(そんなことする奴はイネーと思うが)

サブクラス化は各Compo一つに1回だけを心がけるべきかしら。
>>906 事故レス >>909の方法は間違いだ。途中の順番逆だし
  これだと多重にサブクラスされていた場合に対応出来ない。

再帰的に、
WM_DetachProcが送られたら
LParamを見て送信者=受信者でなければ
 元に戻してFOldWndProcに同じメッセージを投げつける
 メッセージから帰って来たら再度コネクトする
LParamを見て送信者=受信者なら
 元に戻して完了

とすればどうだろ
検証してないけど

procedure TFormFucker.FormWndProc(var Message: TMessage);
begin
 case Message.Msg of
WM_DetachProc:
  begin
TCustomForm(AOwner).WindowProc := FOldFormProc;
   if (TFormFucker(LParam) <> Self) then begin //呼び出したのが自分でなければ
    FOldFormProc(Message);//さらに解放を続ける
  FOldFormProc:=TCustomForm(AOwner).WindowProc ;
    TCustomForm(AOwner).WindowProc := FormWndProc;//接続しなおす
  end;
  end;
 else  FOldFormProc(Message);
 end;
end;

destructor TFormFucker.Destroy;
var msg:TMessage;
begin
  msg.Msg :=WM_DetachProc;
  msg.WParam:=Longint(FormWndProc);
  msg.LParam:=Longint(self);

end;
914907:01/09/20 23:32
> ところがすべての Windows メッセージに対して、Dynamic メソッドを
> 定義できるため、コントロールのソースがある場合、わざわざ WindowProc を
> 変更する必要はほとんどない
message指令とか、WM〜メソッドをoverrideするってことかも。

907の説明)サブクラス化を行うコンポを削除すると問題なのは、
自分のメソッド(FormのWindowProcと置き換えていたやつ)
が他のコンポにOldProcみたいなかんじで保存されていて、
インスタンスが破壊された後にも呼び出される事があるってことだと思う。
だからWindowProcを置き換えるメソッドはクラスメソッドにして、
そこから処理本体のメソッドを呼び出すようにするという仕組み。
で、使い終わったら、処理本体でなくて、置き換える前のFormのメソッドを
呼び出すようにしてやる。
インチキをしているのは、クラスメソッドを呼ばせる時、TMethod.Data
に当たる部分のデータを、書き換えてしまうところ。
ここの数値が正当じゃなくても、ちゃんと呼んでくれるみたい。
説明下手でスマソ
そういや昔「ばか者」を意味するfuckerって
名前をつけるのは不適切とかのたまってたのがいたな
916デフォルトの名無しさん:01/09/20 23:54
>>915
えーあー カタカナ語はちゃんと語源を確認しようね

で、システムをフックする関数(かなにか)に
Hooker ってなずけた人がいるんです。

Hooker については
http://www.sanseido.net/sup/ItemPane.asp?P0=12&P1=MAIN,NODE,57198
を。
917Delギコ:01/09/21 00:11
                ∧ ∧   / ̄ ̄ ̄ ̄ ̄
                (゚Д゚*,) <  書いてみた
             _φ_⊂)__  \_____
           /旦/三/ /|
        | ̄ ̄ ̄ ̄ ̄|  |
        |千葉ピーナッツ|/
          ̄ ̄ ̄ ̄ ̄
こんな感じでなってるよね。

Form1.WndProc
=A.NewProc
 begin
  ・・・
  A.FOldProc=B.NewProc
 end;      begin
           ・・・
           B.FOldProc=C.NewProc
          end;       begin
                    ・・・
                    C.FOldProc=サブクラス化しないときのForm1のWndProc
                   end;

A/B/Cは一応、自分が作れるものとしましょう。
他人が作ったものでも対応できればいいけど、さすがにそりゃ無理でしょ

で、A/B/CのClassは全く別だとしましょう。
TA,TB,TCとでも考えてくださればいいっす。
918Delギコ:01/09/21 00:12
    ∧ ∧   / ̄
  ∩(,,゚Д゚) <  おもしろい。
⊂,,⌒ ,,つつ.  \_
   ̄ ̄ ソンナニ sage ンデモ エエデ(関西風)

Bをサブクラス化から開放したいとき
A.FOldProc:=B.FOldProcする必要があって
それってWM_DetachProcメッセージ伝達で出来るのかしら??
ちょっとイメージできてないです。

というか、Bを開放するときに、Aも一緒にはずしてしまうって事かしら。

でもコンポーネントは配置されているのに
理由もなくサブクラス化されていない物が混じっているのも
ちょっときついかも

そのあとAを開放しようとすると
プログラム側がワケワカな挙動しないかな。
919884:01/09/21 01:07
誰か>>895教えてくだされ
>Delギコさん
新スレ立てるとき、ここの1みたく「その他のプログラム」って書くと
またVBの質問とか出てきそうだからやめよう。
検討済みだったらスマソ。
921デフォルトの名無しさん:01/09/21 08:11
多段サブクラス取り外し問題は
取付順
 ↓ objA ─────────────── WndProc → FoldwndProc
                               ↑           │
 ↓ objB ────────── WndProc → FoldwndProc    │
                         ↑                 │
 ↓ objC ───── WndProc → FoldwndProc            │
                ↑                          │
 ↓ objD WndProc → FoldwndProc                    │
          ↑                                  │
         msg ……………> (サブクラス先の.WndProc )←──┘

こういうふうになっていると安全に取り外せるのは objDだけ
 objAを取り外したい場合は objA,B,Cの順に取り外さなければいけない
 これはメッセージの流れる順だから
  取り外せというメッセージを送ればいい。

 そのメッセージを処理するWndProc内部で取り外し処理をしては
  送ったメッセージが自分のメッセージでなければ
   FoldWndProcを通じてルートを遡らせてゆき
   そのFoldWndProcが帰って来たら再度接続しなおす。

メッセージさえ共通にしておけばいいから複数のオブジェクトを設計する場合も
それほど難しい問題は起きない。
922913:01/09/21 08:54
検証した。 問題なく取り付け取り外しできるように見えるぞ

const WM_DatachProc = WM_USER;
type
 TtestLabel = class(TLabel)
  public
  FoldWndProc:TWndMethod;
  constructor Create(AOwner: TComponent);override;
  destructor Destroy;override;
  procedure  MyWndProc(var Message: TMessage);
 end;


constructor TtestLabel.Create(AOwner: TComponent);
begin inherited;
 if (AOwner is TCustomForm) then
 begin
  FOldWndProc := TCustomForm(AOwner).WindowProc;
  TCustomForm(AOwner).WindowProc := MyWndProc;
 end else begin
  FOldWndProc := nil;
 end;
end;


destructor TtestLabel.Destroy;
var msg:TMessage;
begin
 msg.Msg:=WM_DatachProc;
 msg.WParam:=longint(self);
  (Owner as TCustomForm).WindowProc(msg);
 inherited;
end;

procedure TtestLabel.MyWndProc(var Message: TMessage);
begin
 case Message.Msg of
 WM_DatachProc:begin
  (Owner as TCustomForm) .WindowProc := FOldWndProc;
   if ( Message.WParam <> LongInt(Self)) then
     begin
     FOldWndProc(Message);//さらに解放を続ける
     FOldWndProc:=(Owner as TCustomForm) .WindowProc;
     (Owner as TCustomForm) .WindowProc:= MyWndProc;
     end else FOldWndProc:=nil;
  end;
 WM_CHAR: begin //コンポーネントの動作チェック用
   text:=Char(Message.WPARAM);
  if assigned(FoldWndProc) then FoldWndProc(Message);
  end;
 else if assigned(FoldWndProc) then FoldWndProc(Message);
 end;
end;

procedure Register;
begin RegisterComponents('Samples', [TtestLabel]);
end;

end.
923デフォルトの名無しさん:01/09/21 09:02
という事でココをご覧のコンポーネント作家さん

 WM_DatachProc = WM_USER;

をサブクラス取り外し用メッセージという事にして
WParamでSelfを送るという約束にしましょう。

LParamは空きとして
924デフォルトの名無しさん:01/09/21 09:07
さらにWindowsへのサブクラスの場合も考えて
LParam にはプロセスユニークと思われる番号(とりあえずプロセスID)が
与えられる事にしましょう。
925922:01/09/21 09:17
おっと、>>922 のコードでは
FOldWndProc がnilかどうか確認せずに使ってる個所が
何箇所かあるから修正してね
>>922-925
ふと思ったんだが、
WM_USERだとすでに使われていて別の意味を持つ場合もあると思うから、

RegisterWindowMessage('WM_SubclassFreeNotify');

みたいにして使った方が(・∀・)イイ!と思われ。
もしくはWM_USER+222みたいにするとか。222の意味は2chということで:-)
WM_USERってVCL自体で使ってたような。はまったことある。
WM_APP以降にせんと安心できなかったな。
RegisterWindowMessageがやはり無難な感じ
928デフォルトの名無しさん:01/09/21 09:50
ではサブクラス解放についてのまとめ

WM_DatachProc = RegisterWindowMessage('WM_SubclassFreeNotify');
WParam,LParam はユニークな値が入る
 この2つの値で送信者が自分かどうか判断し
  自分でなければリンクを解放して下位にメッセージを送り
  メッセージが帰って来たらリンクを再接続する
 送信者が自分ならリンクの解放だけを行う

VCL上のサブクラス取り外し
  WParam で selfが渡される WParam = LongInt(self)
  LParamは無視してよい

Win上のサブクラス取り外し
 LParamでプロセスID
 WParamはプロセス内ユニークな値(メソッドアドレス等それぞれのプロセスで管理)
929Delギコ:01/09/21 09:51

      ∧∧  / ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
 @' ― (,,゚Д゚) < 正規表現はわからんちー。
   し― し-J   \___________
 2chのどこかで正規表現すれがあったような気もする

>>921さんは>>922さん?
>こういうふうになっていると安全に取り外せるのは objDだけ
これで妥協してしまうのだったら
問題は非常に簡単なんですけどね。

> objAを取り外したい場合は objA,B,Cの順に取り外さなければいけない
??なんかわからなくなってきました。
objAを取り外したい場合はobjBのFoldWndProcを
サブクラス先の.WndProcに接続しなきゃいけないですよね。

>   そのFoldWndProcが帰って来たら再度接続しなおす。
イチドサブクラス化解除させてから再度サブクラス化させるってことかな

>>922のコードはイマからみてみます。
×Datach
○Detach
931921:01/09/21 09:56
×objAを取り外したい場合は objA,B,Cの順に取り外さなければいけない
○objAを取り外したい場合は objD,C,Bの順に取り外さなければいけない
>objAを取り外したい場合はobjBのFoldWndProcを
>サブクラス先の.WndProcに接続しなきゃいけないですよね。

 結果的にはそういう事だけど、
 それに拘るからハマルんだよ
 基本的にobjAからは objBが見えない。確実に割り出す方法はないんだよ。
 サブクラスがobjA,objBの2段だけなら サブクラス元のメソッドポインタから割り出せるけど
 さらにサブクラスされているともうダメ

 だから、メッセージを使う方法が一番安全で確実だと皆言ってるじゃないか
933Delギコ:01/09/21 10:15
    ∧ ∧  /
    (,,゚Д゚)<  こりゃまたすごいや。
     |つ つ  \
   @   |  
    ∪ ∪

動作コードみてようやくわかりました。
サンサンサンクスコクスコクスコーーーー

>>921の図でいうと
objAを取り外す場合

objAからメッセージを投げる
objDに伝わる
objDのサブクラス化を解除
objDからobjCに連絡
objCに伝わる
objCのサブクラス化を解除
objCからobjBに連絡
objBに伝わる
objBのサブクラス化解除
obfBからobfAに連絡
objAに伝わる
objAのサブクラス化解除
objAは自分自身なので何もしない
objAの処理が終わったので
objBに処理が戻り再度SubClass化
objBの処理が終わったので
objCに処理が戻り再度SubClass化
objCの処理が終わったので
objDニ処理が戻り再度SubClass化

こういう事になっているわけですね。
934Delギコ:01/09/21 10:27
 ∧ ∧    / ̄ ̄ ̄ ̄ ̄ ̄ ̄
 (,,゚Д゚) < んまー。するどい>>932
  | つつヾ  \_______
〜|   | ユルシテヨ
  ∪∪

>  結果的にはそういう事だけど、
>  それに拘るからハマルんだよ

ええ、しっかりと浜りました。
みっちりときっちりと。

>  基本的にobjAからは objBが見えない。確実に割り出す方法はないんだよ。

そうっすね。
ということは、他人の作ったものが混じった場合、例えばここのCompo
http://www.psn.ne.jp/~nagayama/program/0017.html
(ごめんねサイト作者さん)
この仕組みは動作しないから…一体どうなるんだろ。

 …テストをしている限りは変な挙動はしていないみたい。
  ちゃんとtestLabelは取り外せますね。
  正しいのかな?

>  サブクラスがobjA,objBの2段だけなら サブクラス元のメソッドポインタから割り出せるけど
>  さらにサブクラスされているともうダメ

ですねー。

>  だから、メッセージを使う方法が一番安全で確実だと皆言ってるじゃないか

やっとわかたでし。
935デフォルトの名無しさん:01/09/21 10:39
再度 サブクラス解放についてのまとめ >>コンポーネント作家の方々へ

サブクラスを利用するコンポーネントは以下の手順で解放する事

var WM_DetachProc:Cardinal; をローカル変数として定義し
 コンポーネントの初期化部で
initialization
WM_DetachProc := RegisterWindowMessage('WM_SubclassFreeNotify');
end.
とする。

サブクラス解放時にはこのメッセージIDで
 WParam,LParam はユニークな値を入れて サブクラス先に送信する
 それを受信したWindowProcでは、
  この2つの値で送信者が自分かどうか判断し
   自分でなければリンクを解放して下位にメッセージを送り
   メッセージが帰って来たらリンクを再接続する
  送信者が自分ならリンクの解放だけを行う


VCL上のサブクラス取り外し
  WParam で selfが渡される WParam = LongInt(self)
  LParamは無視してよい    サンプルコード >>922

Win上のサブクラス取り外し
 LParamでプロセスID
 WParamはプロセス内ユニークな値(メソッドアドレス等それぞれのプロセスで管理)

注意: WM_DetachProc は定数ではないので case では使えない ifで判断する事
>>934 リンク先みてみた。

これが簡単に暴走しない理由は、
 FNewWndProc := MakeObjectInstance(WndProc);
とサンクを使っていて、オブジェクト解放時にこのサンクを解放していないからじゃないかと思う
937Delギコ:01/09/21 11:02
   ∧ ∧     / ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
   ( ゚Д゚)   < ほかの自作FormフックCompoも試した
.=| |==U=U==.| |= \___________
 | | |  |  | |   普通にVCLでフック解除するヤツだよ
 | | ノU U.  | |
 | |      .| |

testLabelを削除された時に
暴走はしないのですが
自作の方のサブクラス化は解除されてしまうます。
自作の方は正しくサブクラス化解除できないときは
例外を出すようにしてるからいいけど
タブン、マトモに削除するとWndProcがどこかに逝ってしまい
暴走するだろーな。

WM_DetachProc が流れた所で
安全にサブクラス化解除されない場合は例外発生して
元に戻るようにできないかいニャ。

 考え中.....

  そこまで拘らなくてもいいって肝するけどー。
938Delギコ:01/09/21 11:04
 ∧ ∧   /なんにせよ
 ( ゚Д゚) <  教えてくれたデフォルトの名無しさんズ。
  ノつノつ   \ありがとーう。
 ( 人  
  ソ  )
  ノ ノ
 ( 人  ネジッテ ミマシタ
  ソ  )
  ノ ノ
  UU
939878:01/09/21 11:07
>>895
最初の例が間違えてたっす。スマソ。

s/(\-\-|==)+/$1/g; の場合
「-」奇数→「---」
「-」偶数→「--」
ですな。例では「-=」ともに偶数だから結果は
895が書いてる通りになります。
940デフォルトの名無しさん:01/09/21 11:09
いまさらですが、サブクラスとは具体的にどういう作業を指すのでしょうか。
オブジェクト指向の一部ですか?それとも
単にテクニックの一つでしょうか。

サブクラスとWinProcとはどういう関係がありますか。
つーか、WinProcは何者ですか。
941デフォルトの名無しさん:01/09/21 11:16
Delphiでは、メッセージハンドラとしてメッセージマップなんかより、
ずっと綺麗にメッセージを隠蔽してるから、使わなくて問題無い。
また、Delphiでコールバック関数やメッセージ使うと、
あっという間に超読めないコードになる。
だから、メッセージ操作コード出さないでね>>Delギコ
942Delギコ:01/09/21 11:23
>>940
     ∧ ∧  / ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
ピュ.ー ( ゚Д゚)<  説明リンクを探したのだけど
  =〔~∪ ̄ ̄〕\いいのがないのだ。
  = ◎――◎

ちょっと前ならTNight2000っていう面白いCompoのサイトがあったのだが
サイトがお亡くなりになってしもた。
Googleのキャッシュでちょっと見れるみたい。

他には
http://www.asahi-net.or.jp/~HA3T-NKMR/vcl3-1.htm
http://homepage2.nifty.com/boheme/delphi/tips/tec1080.htm
http://www.borland.co.jp/tips/delphi/dh015/index.html
こんなかんじかなあ。

簡単な説明すると
TLabelに流れてくるメッセージを見たい場合は
TLabelを継承してメッセージハンドラ記述して見てもいいんだけど
いろんな理由からそれをやりたくない場合
TLabelをサブクラス化するとそれが見れるという仕組みらしいよ


>>941何がいいたいんだ?
頻繁に必要になるものでもないが、必要なときには使うしかあるまい?
943Delギコ:01/09/21 11:48
             (  ヽ      ――――  ○  ――――
     , ⌒ヽ    (     )          // | \
    (    '   (       ヽ⌒ヽ 、   / / |    \
    ゝ    `ヽ(              )        | (⌒ 、
   (           、⌒         ヽ     (     ヽ
  (        (⌒              )   (
 (                          Y⌒ ヽ
  (        `)
   ゝ              ノ  ノ
_________________________________________________________________________________________
 〜〜 〜〜   〜〜〜  〜  〜〜  〜〜  〜  〜〜  〜
  〜〜     〜〜      〜〜        〜〜〜    〜
〜 〜〜     〜〜   〜〜   〜〜  〜〜    〜〜〜
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  ....   ∧ ∧       ....      .....        ....      .....
 ....    (,,゚Д゚) ..      ....   ....      .....
    .... | ∪   ....           ...   ...       .
....    〜 | ナツガ オワッタ スレ モ オワッタ       ....          
  ....   ∪∪
       .... アツイ オモイデ サヨウナラ  ....      ....   ...     


新スレ立てました。サブクラス化などの続きはこちらで、、

( ゚Д゚)ノ Delギコ猫のプログラミング相談室part3
http://piza2.2ch.net/test/read.cgi?bbs=tech&key=1001039863
944935:01/09/21 11:49
ああ、そうか、
 このメッセージが経路を辿る時に途中で解放せずにそのまま渡されて来る場合もある訳で
 経路の全てがこのメッセージをサポートしてるかどうかチェックする方法が無いといけないのか

 という事で、このメッセージを受け取った時のサブクラス先のポインタが自分が設定した時の
 状態に戻っていなければ・・・・・

 どうしようもないかな?
 せいぜいメッセージを出して別の何もしないコンポを動的に作成してそいつを割り当てておくしかないかも

という事でコンポ作家の皆さん >>935 をよろしく
945デフォルトの名無しさん:01/09/21 11:53
つまり、コントロールに送られたウィンドウメッセージを
最初に受け取るのが WinProc 関数であり、
これを別の関数に置換して、そちらにメッセージが
流れるように仕向ける操作を「サブクラス化」と呼ぶわけですか。
946884:01/09/21 11:54
>>939
ぁぁ納得したです
これで正規表現はバッチリだ(嘘
947デフォルトの名無しさん:01/09/21 12:08
>>941何がいいたいんだ?
>頻繁に必要になるものでもないが、必要なときには使うしかあるまい?

Delギコ生意気。使わずにできるぞ。
サブクラスの作成でつまずいてるんだろ。それがメッセージのせいって分からんのか。
分からんのだったら、人にアドバイスするな。
マターリいこうぜ
949Delギコ:01/09/21 12:49
 ∧ ∧      / ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
 (,,゚Д゚)    <  え〜、牛乳いかがっすかー。
./\品品昌_   \___________
( U__| 雷印 |
.U U ̄ ̄ ̄

カルシウム不足の方が1名おられるようで
他のお客様のご迷惑になりますので,,,
牛乳飲んどけ。

折れもカルシウム不足かも。ノンどくか。


でも、ウシって今・危ニャーかも??
>>949
牛乳には狂牛病の原因と考えられているプリオンは含まれません。
神経系の体液が関与しない限り安全です。
951デフォルトの名無しさん:01/09/21 21:11

終いには「Delギコ猫のプログラミング相談室」なんていう書籍が出そうだね(w
952デフォルトの名無しさん:01/09/22 06:13
なんで”サブクラス化”と言うのでしょうか?
サブクラスと言うと、子クラスを連想します。
数学の集合辺りの用語とか聞いた

>サブクラスと言うと、子クラスを連想します
合ってるやん
新スレあるのにageるとはこれいかに
>>Delギコ
スレが10までいったら書籍キボン。
デザパタとかも含めてお得なお値段\3Kあたりをキボンw

とりあえず終了
スレよりこれ以降は書き込みを自粛した方がよいかと思われ。
(モレモナー)

つうわけで再度誘導。

( ゚Д゚)ノ Delギコ猫のプログラミング相談室part3
http://piza2.2ch.net/test/read.cgi?bbs=tech&key=1001039863
こっちのスレに書かしてもらいます。
なんとなく実装の改良案。
destructor TTestLabel.Destroy;
var
 TempWindowProc: TWndMethod;
 Msg: TMessage;
begin
 TempWindowProc := TCustomForm(Owner).WindowProc;
 TCustomForm(Owner).WindowProc := FOldWindowProc;
 Msg.Msg := WM_DetachProc;
 Msg.LParam := 0;
 Msg.WParam := WPARAM(Self);
 Msg.Result := 0;
 TempWindowProc(Msg);
inherited Destroy;
 if Msg.Result = 0 then
  raise Exception.Create('サブクラス化解除がおかしいですよ〜');
end;

procedure TTestLabel.MyWindowProc(var Msg: TMessage);
begin
 if Msg.Msg = WM_DetachProc then
 begin
  if Msg.WParam = WPARAM(Self) then
  begin
   Msg.Result := 1;
  end
  else
  begin
   FOldWindowProc(Msg);
   FOldWindowProc := TCustomForm(Owner).WindowProc;
   TCustomForm(Owner).WindowProc := Self.MyWindowProc;
  end;
 end
 else
  FOldWindowProc(Msg);
end;
957Delギッコー:01/09/22 11:32
    /\       ________
  /ギッコー   /
/____\ < 新スレでやろーう
  | ゚Д゚|    \
 ノ| || || || |し      ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
         sageるよ
保守
にゃんまげ
保守