ExcelオブジェクトからプロセスIDを取得するには?

解決


あんぶー  2004-06-08 00:56:22  No: 83876

お世話になっております.
あんぶーです.

OS: Windos2000 pro SP4
    VisualStudio6.0 
    Office 2000

過去ログよりこういった内容がありましたが,理解できませんでしたので,
誠に申し訳ありませんが,再度同じような質問をさせていただきます.
(http://madia.world.coocan.jp/cgi-bin/VBBBS/wwwlng.cgi?print+200304/03040087.txt)

Excelオブジェクトより,プロセスID,またはウィンドウハンドルを取得したいのですが,どういった方法を用いればよいのかわかりません.
ご教授お願いします.


魔界の仮面弁士  2004-06-08 03:23:29  No: 83877

> Excelオブジェクトより,プロセスID,またはウィンドウハンドルを取得したいのですが
Excel 2002なら、ApplicationオブジェクトのHwndプロパティにて、ウィンドウハンドルを取得できるのですが、今回は Excel 2000 をお使いのようなので、APIを使って、

  strCaption = objApplication.Caption
  objApplication.Caption = 適当な文字列
  lngHWnd = FindWindow("XLMAIN", objApplication.Caption)
  objApplication.Caption = strCaption

という感じでしょうか。

プロセスIDに関しては、GetWindowThreadProcessId あたりでどうぞ。


あんぶー  2004-06-09 00:22:46  No: 83878

魔界の仮面弁士さま
早速の回答ありがとうございます.

> プロセスIDに関しては、GetWindowThreadProcessId あたりでどうぞ。

上記の方法でプロセスIDを取得してみたいと思います.
どうもありがとうございました.

話は変わってしまうのかもしれませんが,

Excelアプリケーションを終了する場合,
  objApplication.Quit
と書いていたのですが,

内部(プロセス)としてはまだ,終了していないこともあるのでしょうか.
(タスクバーで観察していると,Quitメソッドを抜けてもプロセスIDは残っていることがあります.)
もしご存知の方がいらっしゃいましたら,ご教授お願い致します.


魔界の仮面弁士  2004-06-09 01:49:53  No: 83879

終了命令(Quitメソッド)と、プロセスの終了と、オブジェクトの解放は独立しています。

たとえば、InternetExplorerオブジェクトに目を向けてみると、
  IE.Quit
  IE.Visible = True
のように、Quit後にオブジェクトを操作しようとすると、
実行時エラー 0x80010108(RPC_E_DISCONNECTED)の、
『起動されたオブジェクトはクライアントから切断されました。』
というオートメーションエラーとなります。

これは、Quitメソッド実行時だけでなく、ユーザー操作などにより、
IEが閉じられた場合にも、同様の結果となります。

それに対して、Excelオブジェクトの場合は、Quitメソッドや
ユーザー操作で閉じられた後でも、
  objExcel.Visible = True
という操作を受け付けるように設計されています。
プロセスまで解放させたい場合には、Quit後に
  Set objExcel = Nothing
などとして、オブジェクトも解放する必要があります。

> タスクバーで観察していると,Quitメソッドを抜けてもプロセスIDは残っていることがあります.
この現象が起きる時は、コーディングに問題がある可能性が高いです。
オブジェクトを解放していない、もしくは解放できないようなコードに
なっていないかどうかを確認してみてください。

注意すべきは、たとえば、
 OK:   Set objBook = objApp.Workbooks.Add()
 NG:   Set objBook = Workbooks.Add()
とか、
 OK:   objSheet.Range("A1") Destination:=objSheet.Range("D8")
 NG:   objSheet.Range("A1") Destination:=Range("D8")
のように、オブジェクトを修飾するのを忘れた時です。

普通は、オブジェクトを明示しないとエラーになるのですが、
Excelライブラリを参照設定している場合は、オブジェクトを省略しても、
暗黙のApplicationオブジェクトが自動生成されるため、動作してくれます。

その反面、自動生成されたオブジェクトはVBの変数として管理されて
いないため、NothingをSetして明示的に解放する事ができません。
そのため、Excelのプロセスが生き残ってしまう事になります。

http://www.interq.or.jp/www-user/komurak/progtec/030.htm
http://www.bcap.co.jp/hanafusa/VBHLP/caution.htm


あんぶー  2004-06-09 04:37:38  No: 83880

魔界の仮面弁士さま
たびたびご回答ありがとうございます.

> 『起動されたオブジェクトはクライアントから切断されました。』
> というオートメーションエラーとなります。
> これは、Quitメソッド実行時だけでなく、ユーザー操作などにより、
> IEが閉じられた場合にも、同様の結果となります。
(Excelの場合もこのようなエラーメッセージが出力されました.)
ということは,Quitしたオブジェクトに対して何らかのアクションを起こそうとした場合に発生すると考えてよいのでしょうか.

私は「何も参照していないオブジェクトに対して何らかのアクションを起こした場合に発生するエラー」と解釈していました.

----------------
なぜこの疑問が発生したか説明がうまく出来ませんので,
処理内容を説明して理解していただけたらよいのですが.

今回作成した内容は
1.Excelを起動しマクロを実行させ実行結果ファイルを作成 (1プロセス)
2.実行結果ファイルを表示(ファイルの多重起動可能) (1プロセス)
という処理を順に行います.

1.では処理概要どおりの作業を行い,(新規にExcelオブジェクトを作成)
最後にQuit,シートオブジェクトの解放,ワークブックオブジェクトの解放,アプリケーションの解放を行います.

2.の処理内容は
Excelアプリケーションが起動していたら,そのアプリケーションにワークブックを追加する.(GetObject)
Excelアプリケーションが起動していなければ,アプリケーションを作成しワークブックを追加する.(GetObjectでエラー発生→CreateObject)
ということをまず行っています.

この際,1.で終了したはずのオブジェクトが残っていたため,
2.ではGetObjectでエラーが発生せず,新しいオブジェクトが作成されないため,上記のオートメーションエラーが発生しました.(言い方が正しくないかもしれませんが)

このオートメーションエラーは時々おこり,
メモリ上に展開されるとほぼ2・3分の1のタイミングでおきました.

そして回避方法として
イ.FindWindowでウィンドウハンドルを取得
ロ.GetWindowThreadProcessIdでスレッドIDを取得
ハ.スレッドIDが"0"になるまでロを繰り返す
という処理を行い,プロセスが終了したことを確認し,
2の処理を行うようにしました.

(内部的な仕組みが理解で来ていないため)
一応(仕様として)動作はしているものができました.
が,こういった処理を行わなくてもうまく対処できるのでしょうか.

ちなみに以下の方法も考えましたが,状況は変わりませんでした.
・アプリケーションの終了コードの確認
・起動が終了するまで待つ

コーディングの問題かもしれません.(再レビューしてみます)
長々申し訳ありませんでした.
以上です.


魔界の仮面弁士  2004-06-09 07:38:26  No: 83881

> (Excelの場合もこのようなエラーメッセージが出力されました.)

Excelのバージョンにもよるのかも知れませんね。

当方で、Windows XP, Excel 2002, VB6 という環境において、
VB6にて新規プロジェクトを作成(参照設定は未設定)し、
イミディエイト ウィンドウにて、以下のコードを実行してみた時は、

'---------
Set X = CreateObject("Excel.Application")
X.Visible = True
X.Quit
X.Visible = True
Set X = Nothing
'---------

1. Quitすると、Excelは見えなくなりました。
  ただし、プロセスには残っている状態です。
2. Visible操作で、Excelが再表示されました。
  ただし、画面描画はされません。(X.ScreenUpdatingがFalseの状態です)
3. Nothingを代入した時点で、Excelのプロセスも終了しました。

……という動作になっていました。

> 1.Excelを起動しマクロを実行させ実行結果ファイルを作成 (1プロセス)
ここでいう「マクロ」とは、
 (a) Excelワークブック内に含めたマクロシートの事。(Excel4マクロ)
 (b) Excelワークブック内に含めたVBAプロジェクトの事。(Excel VBA)
 (c) Excelを制御しているVB側のコードの事。(オートメーション操作)
 (d) Excelに組み込まれたアドインプログラム。(Excelアドイン)
などのうち、(b)の事という認識でよろしいでしょうか?

> 最後にQuit,シートオブジェクトの解放,ワークブックオブジェクトの解放,アプリケーションの解放を行います.
順番的には、
  「ブックのClose」→「ブックの解放」→「本体のQuit」→「本体の解放」
のように、階層内の下位のオブジェクトから順に閉じた方が良いと
言われているようです。実際に違いがあるかどうかは確認していませんけど。(^^;)

> この際,1.で終了したはずのオブジェクトが残っていたため,
その後の「2」の件を考える前に、まず、「1」で残ってしまうという問題を
解決する必要がありそうですね。

さしあたり、「Selection」「Active〜〜」「Application」といった
プロパティを使っているところが無いかどうか、確認してみてください。

これらは、Excelの「マクロの記録」を使った時に多用されるものですが、
VBからのオートメーション操作を行う場合、オブジェクトを修飾させる事を
忘れやすく、暗黙のApplicationオブジェクトを残してしまう結果となりがちです。

たとえば、以下のコードを実行した場合、Excelのプロセスが
残ってしまう結果となります。

=====================
Option Explicit
Private Sub Command1_Click()
    Dim X As Excel.Application
    Dim B As Excel.Workbook
    Dim S As Excel.Worksheet
    Set X = CreateObject("Excel.Application")
    Set B = X.Workbooks.Open("C:\Book1.xls")
    Set S = B.Worksheets("Sheet1")
    X.Visible = True
    S.Range("A1").Value = Now()
    With Application.ActiveSheet.PageSetup
        .PrintTitleRows = "$1:$3"
        .PrintTitleColumns = ""
    End With
    Set S = Nothing
    B.Close False
    Set B = Nothing
    X.Quit
    Set X = Nothing
End Sub
==================

しかも、プロセスが残ったままの状態で、もう一度Command1を押下すると、
『オブジェクト変数または Withブロック変数が設定されていません。』
という実行時エラーになります。

この場合、どこが問題かというと、印刷設定を行っている
  『With Application.ActiveSheet.PageSetup』
の部分です。この部分を、
  『With S.PageSetup』
であれば、Excelが残ってしまうことはありません。

既存のコードをレビューする場合は、そうした点に注意してみてください。

# なお、今回はVB6なのであまり関係無いのですが、Excelの解放処理の件は、
# .NETからCOM相互運用で利用する場合、特に注意する必要があります。
http://support.microsoft.com/default.aspx?scid=kb;ja;317109


魔界の仮面弁士  2004-06-09 08:15:07  No: 83882

> 私は「何も参照していないオブジェクトに対して何らかのアクションを起こした場合に発生するエラー」と解釈していました.

その場合は、
  『オブジェクト変数または Withブロック変数が設定されていません。』
というエラーになると思います。
これは主に、Nothing状態のオブジェクト変数を使おうとした場合に発生するエラーです。

一方私が、InternetExplorerオブジェクトを例に出して書いた
  『起動されたオブジェクトはクライアントから切断されました。』
のエラーは、また別の意味を持っています。
こちらは、オブジェクト自体はNothingになっていないのです。

ただ、Excelの場合は「クライアントから切断されました」のエラーが
起きる事は稀なので、その部分の回答は、読み流して頂いても構いません。

それよりも、その後に書いた
  「暗黙のApplicationオブジェクトを残してしまうようなコード」
の方に気をつけてください。

もし、Excelのイベント等を使っていないのであれば、
  Dim X As Excel.Application
  Set X = New Excel.Application
  'Set X = CreateObject("Excel.Application")
  'Set X = GetObject(,"Excel.Application")
などを、
  Dim X As Object
  Set X = CreateObject("Excel.Application")
  'Set X = GetObject(,"Excel.Application")
の形式に書き換え、参照設定せずに完全動作する事を確認してみてください。

例えば、先の『With Application.ActiveSheet.PageSetup』なども、
参照設定を行っていなければ、コンパイルエラーとなりますので、
コーディングミスがあっても、間違いに気づくことができます。
(ただし参照設定しない場合、定数等の宣言が別途必要になります)


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

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






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