Delphiで作成したActiveXコントロールをDelphiに載せた時に設計時と実行時を判断するには?

解決


かねこ  2004-09-08 01:40:57  No: 10832

Delphi5でActiveXコントロールを作成しています。

最終的にはVCやVBに載せる予定ですが、
まずはDelphiに載せて動作確認を行っています。

ActiveXコントロール起動時にアウトプロセスのCOMオブジェクト(.exe)を
起動するプログラムになっているのですが、
DelphiにActiveXコントロールを載せた瞬間にアウトプロセスの.exeが起動してしまいます。

実際には設計時は起動せずに実行時だけ起動したいのですが、
ActiveXコントロールの場合、設計時と実行時の判断はどのようにすればよいのでしょうか?

詳しくはないのですが、以下のようにやってみてもだめでした。
ActiveXだとComponentStateが使えないのか、Controlというのがまずいのかわかりません。

  if not (csDesigning in Self.Control.ComponentState) then
  begin
    // 実行時の処理
  end;

知っているかたいましたらよろしくお願いいたします。


かねこ  2004-09-09 03:16:04  No: 10833

いろいろ調べてネットで以下のサンプルを見つけたのですが、
以下のメソッドを実装すると、GetClientSite の結果は正常なのですが、
Site が Nil で戻ってくるため、その先の UserMode まで見られません。

おそらくその UserMode で設計時かわかるようなのです。

どなたかヒントや参考になりそうなサイト情報でもかまいませんのでよろしくお願いいたします。

// This function returns a pointer to a contained control's site
function ClientSite( obj: IUnknown ): IOleClientSite;
var
  Site: IOleClientSite;
  OleObj: IOleObject;
begin
  if (obj.QueryInterface( IOleObject, OleObj ) = S_OK) and
     (OleObj.GetClientSite( Site ) = S_OK) then
    Result := Site
  else
    Result := nil;
end; 

// This function returns TRUE if the contained object's container is in 
// design mode; FALSE if the container is not in design mode or
// does not support design mode, or on any failure.
//
function IsControlInDesignMode( obj: IUnknown ): Boolean;
var
  Mode: Boolean;
begin
  try
    Mode := not ((ClientSite(obj) as IAmbientDispatch).UserMode);
  except
    Mode := False;
  end;
  Result := Mode;
end;


bob  2004-09-09 03:46:12  No: 10834

参考になるかわかりませんが、
http://forum.nifty.com/fdelphi/samples/00833.html
http://forum.nifty.com/fdelphi/samples/00988.html
http://forum.nifty.com/fdelphi/samples/00989.html
でどうでしょう。


かねこ  2004-09-09 19:31:20  No: 10835

bobさん 書き込みありがとうございます。

リンク先を見させていただきました。

私も基本的には1番目のリンクにある

   if csDesigning in ComponentState then
    ShowMessage('設計時です')
  else
    ShowMessage('実行時です');

でいけるのかと思っていたんですが、csDesigning はActiveXでは使えないみたいでした。(どこかのHPに書いてありました)
これと同じような処理をActiveXでどうやるかの情報がありましたらまたお願いします。

ありがとうございました。


bob  2004-09-09 21:57:06  No: 10836

ここですね。
http://pw2.netcom.com/~cherrman/dsgnmode.txt

そこで、簡単にボタンのActiveXを作成して
試したら上記のコードで出来ましたよ。

IsControlInDesignMode(Self)で。
#プロジェクト一式必要であればコード送りますが...


bob  2004-09-09 21:58:50  No: 10837

補足

上記コードって言ってるのは、
if csDesigning in ComponentState then
ではなく、かねこさんが調べられた
IsControlInDesignModeでOKです。


かねこ  2004-09-09 22:42:51  No: 10838

bobさん、実験までして頂いてありがとうございます。

私も同様にTButtonを継承したActiveXを使ってみましたが、
なぜかうまくいかないみたいでした。

作ってみたのは以下のソースになります。(uses に Dialogs を追加しています。)
これで設計時に貼ると、『実行時』とポップアップされました。
実行しても『実行時』とポップアップしました。

procedure TButtonX.InitializeControl;
begin
  FDelphiControl := Control as TButton;
  FDelphiControl.OnClick := ClickEvent;
  FDelphiControl.OnKeyPress := KeyPressEvent;

  if IsControlInDesignMode(Self) then
    ShowMessage('設計時')
  else
    ShowMessage('実行時');
end;

調べてみると、やはり ClientSite内の Site が Nil のままでした。
GetClientSite の前に SetClientSite 等が必要なのでしょうか?

私はサンプルの2関数の追加と上記のコードしか書いていませんが、
他にも記述するコードがあったりするのでしょうか?

何度もすみませんが、よろしければお願いいたします。


bob  2004-09-09 22:59:53  No: 10839

確かに、InitializeControl時はかねこさんの仰ってるとおりの
現象になりますね...

ボクが試したのはSet_Captionで、期待通りに
設計時/実行時は表示されます。


かねこ  2004-09-09 23:08:50  No: 10840

なるほどぉ、Set_Captionでやってみたらできました。
InitializeControl中はまだクライアントサイトがない状態だったんですね。

現在作成しているのはOCXの初期化処理である処理を行いたいので
ここかなと思っていたのですが、ここではできないとなると難しいですね。

・・・

ActiveXはまだほとんどやったことがないのでよくわかっていません。
Set_Caption のようにアクションがある前の状態で、
InitializeControlのような初期化処理を行える場所はあるのでしょうか?


bob  2004-09-10 01:20:01  No: 10841

試してませんが、以下の方法はどうでしょう?

procedure CMDesignHitTest(var Message: TCMDesignHitTest);
                                      message CM_DESIGNHITTEST;
上記宣言はTWinControlを継承していないため出来ませんが、
TActiveXControl.WndProc(var Message: TMessage);
をoverrideしてCM_DESIGNHITTESTを見るってのはどうでしょう?


かねこ  2004-09-13 20:05:44  No: 10842

体調くずして早退してお休みしていたため、返事が遅れてごめんなさい。

教えて頂いた方法をやってみましたが、設計時でも実行時でも残念ながら、
  if (Message.Msg = CM_DESIGNHITTEST) then
となるメッセージは飛んでこないようでした。
VCL独自のメッセージなのかもしれません。
(ここの常連さんのMr.XRAYさんのページ(かな?)にもVCL独自のメッセージと書いてありました)

またいろいろやってみますが、何か情報がありましたらぜひお願いします。


bob  2004-09-14 20:23:10  No: 10843

最終手段としては、設計時モードライセンスをうまく使うか、
デバッガがアタッチしているか?  ってのを調べるしか無いかな???

やっぱ難しいですね。InitializeControl内ではクライアントサイト情報が
取得出来ないですから。


かねこ  2004-09-14 21:02:35  No: 10844

設計時モードライセンスは今回は使えないというのと、
デバッガがアタッチも開発環境によって微妙ですので、ちょっと難しいかな。
もう少し時間はありますので、開始用のメソッドを用意したりとか
別の方法でも検討していきたいと思います。
解決方法がわかりましたら、また追記したいと思います。

bobさん、いろいろと本当にありがとうございました。


bob  2004-09-14 21:07:05  No: 10845

クライアントサイト情報云々の前にアンビエントプロパティが取得出来ないのね.
GetPropertyString(DISPID_AMBIENT_USERMODE, tmpStr);
もダメだし。でも
IOlecontrolのOnAmbientPropertyChange(DISPID_AMBIENT_USERMODE)が
VCLで見つからないのはなんでだろう?
ありそうな記がするんだけどなぁ〜


bob  2004-09-14 21:29:32  No: 10846

VBのActiveXでさえUserModeプロパティで取得出来るのに
Delphiだけ出来ないのは納得いかないですね。しかし。

TActiveXControlのInitializeはOverrideしても意味がないし...
#Initialize内でInitializeControl呼び出してるから


かねこ  2004-09-14 21:34:05  No: 10847

ClientSiteがいつできるか調べるために、デバッグ版DCUを使ってみたら、
TActiveXControl.SetClientSite よりも先に InitializeControl に
来てしまってました。
だから InitializeControl ではまだクライアントサイトがないみたいですね。


かねこ  2004-09-14 21:52:27  No: 10848

いいかわかりませんが、いちおうできる方法がわかりました。

FInitFlagという初期化したかフラグを用意しておいて、
メッセージ受信時に1回だけ初期化をするようにしてみました。

procedure Txxx.WndProc(var Message: TMessage);
begin
  inherited WndProc(Message);

  if (FInitFlag=False) and (ClientSite(Self)<>Nil) then
  begin
    if not IsControlInDesignMode(Self) then
      InitProc; // 実行時のみの初期化処理
    FInitFlag := True;
  end;
end;

SetClientSite 内の OnAmbientPropertyChange で CM_PARENTCOLORCHANGED は
最低でも発生しそうですので、何もこないことはなさそうです。

このコントロール向けのすべてのメッセージで毎回来てしまいますが、
これで設計時と実行時がうまく切り分けられて、初期化できました。

無理矢理な感じですが、これで問題なさそうですよね


かねこ  2004-09-14 22:28:13  No: 10849

なんとかできるようになりました。
bobさん、実験してくれたりいろいろ教えてくださいまして、
ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加