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;
知っているかたいましたらよろしくお願いいたします。
いろいろ調べてネットで以下のサンプルを見つけたのですが、
以下のメソッドを実装すると、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;
参考になるかわかりませんが、
http://forum.nifty.com/fdelphi/samples/00833.html
http://forum.nifty.com/fdelphi/samples/00988.html
http://forum.nifty.com/fdelphi/samples/00989.html
でどうでしょう。
bobさん 書き込みありがとうございます。
リンク先を見させていただきました。
私も基本的には1番目のリンクにある
if csDesigning in ComponentState then
ShowMessage('設計時です')
else
ShowMessage('実行時です');
でいけるのかと思っていたんですが、csDesigning はActiveXでは使えないみたいでした。(どこかのHPに書いてありました)
これと同じような処理をActiveXでどうやるかの情報がありましたらまたお願いします。
ありがとうございました。
ここですね。
http://pw2.netcom.com/~cherrman/dsgnmode.txt
そこで、簡単にボタンのActiveXを作成して
試したら上記のコードで出来ましたよ。
IsControlInDesignMode(Self)で。
#プロジェクト一式必要であればコード送りますが...
補足
上記コードって言ってるのは、
if csDesigning in ComponentState then
ではなく、かねこさんが調べられた
IsControlInDesignModeでOKです。
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関数の追加と上記のコードしか書いていませんが、
他にも記述するコードがあったりするのでしょうか?
何度もすみませんが、よろしければお願いいたします。
確かに、InitializeControl時はかねこさんの仰ってるとおりの
現象になりますね...
ボクが試したのはSet_Captionで、期待通りに
設計時/実行時は表示されます。
なるほどぉ、Set_Captionでやってみたらできました。
InitializeControl中はまだクライアントサイトがない状態だったんですね。
現在作成しているのはOCXの初期化処理である処理を行いたいので
ここかなと思っていたのですが、ここではできないとなると難しいですね。
・・・
ActiveXはまだほとんどやったことがないのでよくわかっていません。
Set_Caption のようにアクションがある前の状態で、
InitializeControlのような初期化処理を行える場所はあるのでしょうか?
試してませんが、以下の方法はどうでしょう?
procedure CMDesignHitTest(var Message: TCMDesignHitTest);
message CM_DESIGNHITTEST;
上記宣言はTWinControlを継承していないため出来ませんが、
TActiveXControl.WndProc(var Message: TMessage);
をoverrideしてCM_DESIGNHITTESTを見るってのはどうでしょう?
体調くずして早退してお休みしていたため、返事が遅れてごめんなさい。
教えて頂いた方法をやってみましたが、設計時でも実行時でも残念ながら、
if (Message.Msg = CM_DESIGNHITTEST) then
となるメッセージは飛んでこないようでした。
VCL独自のメッセージなのかもしれません。
(ここの常連さんのMr.XRAYさんのページ(かな?)にもVCL独自のメッセージと書いてありました)
またいろいろやってみますが、何か情報がありましたらぜひお願いします。
最終手段としては、設計時モードライセンスをうまく使うか、
デバッガがアタッチしているか? ってのを調べるしか無いかな???
やっぱ難しいですね。InitializeControl内ではクライアントサイト情報が
取得出来ないですから。
設計時モードライセンスは今回は使えないというのと、
デバッガがアタッチも開発環境によって微妙ですので、ちょっと難しいかな。
もう少し時間はありますので、開始用のメソッドを用意したりとか
別の方法でも検討していきたいと思います。
解決方法がわかりましたら、また追記したいと思います。
bobさん、いろいろと本当にありがとうございました。
クライアントサイト情報云々の前にアンビエントプロパティが取得出来ないのね.
GetPropertyString(DISPID_AMBIENT_USERMODE, tmpStr);
もダメだし。でも
IOlecontrolのOnAmbientPropertyChange(DISPID_AMBIENT_USERMODE)が
VCLで見つからないのはなんでだろう?
ありそうな記がするんだけどなぁ〜
VBのActiveXでさえUserModeプロパティで取得出来るのに
Delphiだけ出来ないのは納得いかないですね。しかし。
TActiveXControlのInitializeはOverrideしても意味がないし...
#Initialize内でInitializeControl呼び出してるから
ClientSiteがいつできるか調べるために、デバッグ版DCUを使ってみたら、
TActiveXControl.SetClientSite よりも先に InitializeControl に
来てしまってました。
だから InitializeControl ではまだクライアントサイトがないみたいですね。
いいかわかりませんが、いちおうできる方法がわかりました。
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 は
最低でも発生しそうですので、何もこないことはなさそうです。
このコントロール向けのすべてのメッセージで毎回来てしまいますが、
これで設計時と実行時がうまく切り分けられて、初期化できました。
無理矢理な感じですが、これで問題なさそうですよね
なんとかできるようになりました。
bobさん、実験してくれたりいろいろ教えてくださいまして、
ありがとうございました。
ツイート | ![]() |