tagCANDY CGI VBレスキュー(花ちゃん)の Visual Basic 6.0用 掲示板 [ツリー表示へ]   [Home]
一括表示(VB6.0)
タイトルCom Surrogate
記事No16565
投稿日: 2020/05/07(Thu) 18:22
投稿者でふぁいあんと
お世話になります。検索してもなかなか見つからないので
またこちらに質問いたします。

Win10Proを常時起動していて
vb6で作成したプログラム[A]を定期起動しているのですが

Com Surrogateなるプロセスがどんどん増えてくるのでこまっています。
CPUリソースは0%メモリは0.5MB
それで、調べたところ
ほぼこのプログラムAが原因というところまで突き止めました。

プログラムA----
Dim WSH As New WshShell
WSH.Run hogehoge.exe, , True
Set WSH = Nothing
---まで
hogehoge.exeもvb6で作成したプログラムで、
xml関係の送受信をやっています。本来はdllでまとめるべき物ですが
頻繁に変更があるので、exeで作成してます

別に、vb6でプロセスをチェックするプログラムを組んで
ComSurrogeteの発生時間を調べると、このhogehoge.exeを実行する時間と
ドンピシャなのです

(1)ComSurrogateがどういうときに発生(終了)するものなのしょうか
(2)ComSurrogeteをタスクマネージャーなら終了させてもまったく問題がないので
 プログラムにComSurrogeteの終了を組み込んだのですが
    Set Locator = CreateObject("WbemScripting.SWbemLocator")
    Set Server = Locator.ConnectServer
    Set objSet = Server.ExecQuery("Select * From Win32_Process")
 キャプションが、dllhostを含んでいれば Terminate
  を入れたのですが、dllhostを捕まえることはできますが
  terminateを全く無視してエラーもでません
 
本来は、ComSurrogateが出ない(出ても終了する)のがベストなので
その辺のご指導を頂ければと思います


 

[ツリー表示へ]
タイトルRe: Com Surrogate
記事No16566
投稿日: 2020/05/09(Sat) 21:03
投稿者魔界の仮面弁士
> (1)ComSurrogateがどういうときに発生(終了)するものなのしょうか

"C:\Windows\System32\dllhost.exe"
"C:\Windows\SysWOW64\dllhost.exe"
のプロパティを開いてみると、詳細タブに "COM Surrogate" の文字が見えますね。

dllhost は、ActiveX (COM) コンポーネントを専用プロセスで実行させるための実行ホストとして動作し、
親プロセスから呼び出された後、呼び出し元のかわり(surrogate)に COM ライブラリをロードします。

……という認識なのですが、詳しく書かれた情報があまり見当たらない。orz
https://docs.microsoft.com/en-us/windows/win32/cossdk/debugging-compiled-visual-basic-components
https://j.mp/2YPSiP0


> CPUリソースは0%メモリは0.5MB
> それで、調べたところ
> ほぼこのプログラムAが原因というところまで突き止めました。

dllhost 自体は中継役なので、それによって何が呼び出されているのかが重要ですね。

タスクマネージャーのプロセス タブで、COM Surrogate を右クリックして、
[詳細の表示]を選択すると、[詳細]タブ内の該当プロセスが選択されます。
その中の "コマンドライン" 列を確認してみてください。
(コマンドライン列が無い場合は、ListView の列ヘッダーを右クリックして「列の選択」を選びます)

もしもコマンドラインの内容が
DllHost.exe /Processid:{guid値}
で示されているのなら、その guid から追加の情報が得られるかもしれません。例えば…

{02D4B3F1-FD88-11D1-960D-00805FC79235} なら COM+ System Application サービス (COMSysApp)
{133EAC4F-5891-4D04-BADA-D84870380A80} なら Shell Create Object Task Server
{3EB3C877-1F16-487C-9050-104DBCD66683} なら WinInetCacheServer
{973D20D7-562D-44B9-B70B-5A0F49CCDF3F} なら WebPlatformStorageServer
{AB8902B4-09CA-4BB6-B78D-A8F59079A8D5} なら Thumbnail Cache Class Factory for Out of Proc Server


※手元に VB6 環境が無いため、WOW64 で同じようなコマンドラインになるのかどうかは未確認。


> プログラムA----
> Dim WSH As New WshShell
> WSH.Run hogehoge.exe, , True
> Set WSH = Nothing
> ---まで
> hogehoge.exeもvb6で作成したプログラムで、
> xml関係の送受信をやっています。本来はdllでまとめるべき物ですが
> 頻繁に変更があるので、exeで作成してます

プログラムA から起動した場合のみに発生するのか、
それとも hogehoge.exe を単体実行した場合にも発生するのか、
問題の切り分けは済んでいますか?

もし hogehoge.exe 側だとしたら、そのアプリのどの時点で再現するのかを調べてみてください。
スタートアップ(Sub Main)の一行目などで既に発生するなら、依存ファイル(参照設定など)を
減らした実験用 exe を用意して、再現性をテストしてみるとか。

また、親プロセスをタスク スケジューラーから自動起動しているような場合は、
デスクトップからのダブルクリック等からの通常起動の場合にも同じ結果になるのかを
比較してみてください。管理者モードで起動しているかどうか、互換モードの有無で変わるのかも確認。

[ツリー表示へ]
タイトルRe^2: Com Surrogate
記事No16567
投稿日: 2020/05/12(Tue) 17:13
投稿者でふぁいあんと
いつもお返事ありがとうございます。

> {3EB3C877-1F16-487C-9050-104DBCD66683} なら WinInetCacheServer
 こちらでした。

プログラムを精査したところ
ComSurrogateの発生は、hogehoge.exe内の
objXML.open "GET", URL, False  で発生しています
一応 dim objXML as  MSXML2.XMLHTTP60
また、hogehoge.exeの処理そのものは正常に動作しています。

hogehoge.exeを単体実行したときはSurrogateは発生しません
親プログラムAから実行しても発生しません
どうやら、タスクスケジューラからプログラムAを実行すると発生するようです。


> また、親プロセスをタスク スケジューラーから自動起動しているような場合は、
> デスクトップからのダブルクリック等からの通常起動の場合にも同じ結果になるのかを
> 比較してみてください。管理者モードで起動しているかどうか、互換モードの有無で変わるのかも確認。

プログラム自身は、Aもhogehogeも、管理者モードです
タスクスケジューラは、[最上位の特権]をチェックしてもはずしても
ComSurrogateは発生します。
プログラムの管理者と「最上位の特権」の関係てどういうことなのでしょうか

ComSurroagteが発生しないようにするでもいいですし
A内でComSurroageteを強制終了される方法はないでしょうか
別プログラムから
Set objSet = Server.ExecQuery("Select * From Win32_Process")
'
For Each objEach In objSet
---ComSurroage=Dllhost取得
objEach.terminateを実行しても
ComSurrogateは終了しませんでした。

[ツリー表示へ]
タイトルRe^3: Com Surrogate
記事No16568
投稿日: 2020/05/12(Tue) 19:45
投稿者魔界の仮面弁士
> ComSurrogateの発生は、hogehoge.exe内の
> objXML.open "GET", URL, False  で発生しています
「COM インスタンスを生成した時」や「send メソッドを呼び出した時」ではなく、
「open メソッドを呼び出した時」に発生しているのですか?

また、exe を終了させずに上記を複数回呼び出した場合も、
COM Surrogate なプロセスが増えていくのでしょうか?



> 一応 dim objXML as  MSXML2.XMLHTTP60
インスタンスはどうやって生成していますか?
New なのか CreateObject なのか。


手元に VB6 がないので、32bit 版 Office 2016 VBA から試しましたが、
{3EB3C877-1F16-487C-9050-104DBCD66683} な dllhost は一つだけで、
実行するたびに増えていくような事象は確認できませんでした。


Option Explicit
Sub Test()
    Dim objXML As MSXML2.XMLHTTP60
    Set objXML = New MSXML2.XMLHTTP60
    objXML.Open "GET", "http://hanatyan.sakura.ne.jp/index.html", False
    objXML.send
    MsgBox objXML.Status
End Sub



> hogehoge.exeを単体実行したときはSurrogateは発生しません
> 親プログラムAから実行しても発生しません
なるほど。当方の VBA で再現できなかったのはそれが原因かな…。


> どうやら、タスクスケジューラからプログラムAを実行すると発生するようです。

私も把握できているわけでは無いのですが、思い当たるのは DCOM の権限がらみです。


「管理者:コマンド プロンプト」 →「dcomcnfg.exe /32」
→ [コンソール ルート] → [コンポーネント サービス]
→ [コンピューター] → [マイコンピューター] → [DCOMの構成]
→ [WinInetCacheServer] → コンテキストメニュー[プロパティ]
→ [セキュリティ]タブ → [起動とアクティブ化のアクセス許可]

あたりでどうでしょう。


> > {3EB3C877-1F16-487C-9050-104DBCD66683} なら WinInetCacheServer
>  こちらでした。

改善に繋がる物があるかどうかは分かりませんが、
上記で検索すると、幾つか情報が見つかるかもしれません。

https://freepc.jp/error10016

[ツリー表示へ]
タイトルRe^4: Com Surrogate
記事No16569
投稿日: 2020/05/13(Wed) 19:19
投稿者でふぁいあんと
重ね重ねありがとうございます。

> > ComSurrogateの発生は、hogehoge.exe内の
> > objXML.open "GET", URL, False  で発生しています
> 「COM インスタンスを生成した時」や「send メソッドを呼び出した時」ではなく、
> 「open メソッドを呼び出した時」に発生しているのですか?

> インスタンスはどうやって生成していますか?
> New なのか CreateObject なのか。
を、まとめてご説明いたしますと

実はNewの扱いが苦手でおかしければご指摘いただきたいのですが、
hogehoge内のmainで。

dim objXML as new msxml2.xmlhttp60
から
ret = F1(objxml,,)
--
private function F1(objxml as object,,,
log(ミリ秒)
Set objXML = New MSXML2.XMLHTTP60
log(ミリ秒)
objXML.open "GET"
logミリ秒---A
objXML.send
logミリ秒
--と実行しておりまして、new の所にもlogをとれるようにしました。

で別のプログラムでComSurrogateを探して
CreationDateを記録すると、Aのタイミングです。
ちなみに以前は
private function F1(objxml as msxml2.xmlhttp60,,,
objXML.open "GET"
と直接操作していましたが、状況は同じです。


> また、exe を終了させずに上記を複数回呼び出した場合も、
> COM Surrogate なプロセスが増えていくのでしょうか?
はい、hogehogeのxml.open直後に msgbox(" ")で止めるようにして
親から呼ぶと、デスクトップから呼んだときは発生内
タスクスケジューラーから実行すると、実行した数だけComSurrogate発生します


> > どうやら、タスクスケジューラからプログラムAを実行すると発生するようです。
>
> 私も把握できているわけでは無いのですが、思い当たるのは DCOM の権限がらみです。
>
>
> 「管理者:コマンド プロンプト」 →「dcomcnfg.exe /32」
> → [コンソール ルート] → [コンポーネント サービス]
> → [コンピューター] → [マイコンピューター] → [DCOMの構成]
> → [WinInetCacheServer] → コンテキストメニュー[プロパティ]
> → [セキュリティ]タブ → [起動とアクティブ化のアクセス許可]
>
> あたりでどうでしょう。
>
これ、なんとなく当たりかなと思ったのですが、
「起動とアクティブ化のアクセス許可」の「編集」が選択できない状態です
ログオンユーザーは管理者、
コマンドプロンプトは間違いなく管理者モード
「dcomの構成」の上位の「マイコンピューター」-「プロパティ」-「com セキュリティ」に
あえてログオンユーザーを追加してもだめでした。

[ツリー表示へ]
タイトルRe^5: Com Surrogate
記事No16571
投稿日: 2020/05/13(Wed) 20:47
投稿者魔界の仮面弁士
> 「起動とアクティブ化のアクセス許可」の「編集」が選択できない状態です

普通はその状態なので、前回紹介した URL のような対処が必要なはずですが、
それでも駄目となると、レジストリのアクセス許可設定を変更した後に
OS を再起動していなかった…とか?


> dim objXML as new msxml2.xmlhttp60

ご存知とは思いますが、上記の形の宣言は、
オブジェクトを解放しにくくなるので避けた方が無難です。
https://msdn.microsoft.com/ja-jp/library/dd297716.aspx


面倒なようでも、
 Dim objXML As MSXML2.XMLHTTP60
 Set objXML = New MSXML2.XMLHTTP60
としておくことをお奨めします。


また、今回は無人実行を前提としているのだと思いますが、
そのような用途では ServerXMLHTTP60 を使うべきです。
https://support.microsoft.com/ja-jp/help/290761/frequently-asked-questions-about-serverxmlhttp

タスクからの実行なので、VB6 のプロジェクトのプロパティでは
「対話型インターフェイスの抑制」オプションも有効にしておきましょう。


ただし通信先のサーバーによっては、MSXML2 での通信が期待動作しないこともあります。
関連情報として下記の過去ログをご覧ください。
http://www.hanatyan.sakura.ne.jp/vb60bbs/wforum.cgi?mode=allread&no=16149&page=0

[ツリー表示へ]
タイトルRe^6: Com Surrogate
記事No16573
投稿日: 2020/05/15(Fri) 17:24
投稿者でふぁいあんと
> 普通はその状態なので、前回紹介した URL のような対処が必要なはずですが、
> それでも駄目となると、レジストリのアクセス許可設定を変更した後に
> OS を再起動していなかった…とか?
(1)
申し訳ございません。軽く覗いたのですがcomexpとあったので
ちょっと違う説明かなと思ってしまいました。
よく見ると同じご説明でしたね
それでこちらの記事を参考に権限を与えたのですが、
やはりComSurrogateは発生してしまいます。
と、一瞬落ち込みましたが

(2)
>タスクからの実行なので、VB6 のプロジェクトのプロパティでは
「対話型インターフェイスの抑制」オプションも有効にしておきましょう

このオプション付けたら発生しなくなりました\(^o^)/
1+2の合わせ技で効果が出たのか、2単独で効果が出たのかは不明ですが
一定の目的は達成できました。
感謝いたします。ありがとうございました。m(_ _)m



話はそれますが、もしお付き合いいただけれるのであれば

>
> > dim objXML as new msxml2.xmlhttp60
>

> 面倒なようでも、
>  Dim objXML As MSXML2.XMLHTTP60
>  Set objXML = New MSXML2.XMLHTTP60
> としておくことをお奨めします。
拝見しました。
関数渡しの場合はどうすればよいのでしょうか
例えば,Access(SQL)のデータを操作するなんてことはかなりやっているのですが
dim adoRS as new adodb.recordset

dim adors as adodb.recordset
set adors = new adodb.recordset とした場合に

adors を開いて外部関数で使う場合
ret = Fucn(adors)

この関数側では
(1)private function Func(adors as object)
set adors = new adodb.recordset

(1)private function Func(adors as adodb.recordset)
set adors = new adodb.recordset
のどちらがよいのでしょうか



> ただし通信先のサーバーによっては、MSXML2 での通信が期待動作しないこともあります。
> 関連情報として下記の過去ログをご覧ください。
> http://www.hanatyan.sakura.ne.jp/vb60bbs/wforum.cgi?mode=allread&no=16149&page=0
その説も大変お世話になりました。
これ、保存してあるのに、回答はここにあったのですよね。
大反省です。

[ツリー表示へ]
タイトルRe^7: Com Surrogate
記事No16574
投稿日: 2020/05/15(Fri) 21:41
投稿者魔界の仮面弁士
> >タスクからの実行なので、VB6 のプロジェクトのプロパティでは
> 「対話型インターフェイスの抑制」オプションも有効にしておきましょう
> このオプション付けたら発生しなくなりました\(^o^)/

良かったですね!

「対話型インターフェイスの抑制」を有効にしておいた場合、
実行時エラーが発生した場合や、うっかり MsgBox を出力した場合も、
それらの出力が抑制され、メッセージの内容がイベントログに出力されます。
サーバーでバッチ処理するような場合に便利ですよ。

ただしこのオプションでは、一切のユーザーインターフェイスが禁止されるので、
Form や ActiveX コントロールが利用できなくなるという制限がつきます。



> 関数渡しの場合はどうすればよいのでしょうか
実引数のことでしょうか、それとも仮引数のことでしょうか。
また、ByVal と ByRef の違いは把握されていますか?



> (1)private function Func(adors as object)
> (1)private function Func(adors as adodb.recordset)

どちらも(1)なのが気になりますが、それはさておき。
参照渡しの場合、実引数のデータ型と仮引数のデータ型を完全に一致させるようにします)
(ここで比較するのは、引数の変数に実際に入っている値ではなく、変数の型そのものです)


たとえば「ByRef x As Long」な仮引数に、「Dim y As Integer」な変数を実引数として渡すと、
「ByRef 引数の型が一致しません」というエラーが発生することになるでしょう。
オブジェクトを Set した Variant 型を ByRef x As Object に渡す場合も同様です。


Private Sub Main()
 Dim y AS Integer
 'Test y  'エラー「ByRef 引数の型が一致しません」

 Dim a As Variant
 Set a = CreateObject("ADODB.Recordset")
 'Example1 a  'エラー「ByRef 引数の型が一致しません」
 'Example2 a  'エラー「ByRef 引数の型が一致しません」
 Example3 a
 Example4 a
 Example5 a
End Sub

Private Sub Test(x As Long)
 x = -1&
End Sub

Private Sub Example1(x As Object)
End Sub
Private Sub Example2(ByRef x As Object)
End Sub
Private Sub Example3(ByRef x As Variant)
End Sub
Private Sub Example4(ByVal x As Object)
End Sub
Private Sub Example5(ByRef x As Variant)
End Sub


しかし As Recordset と As Object などのように、インターフェイスに互換性がある場合は、
異なった型であっても、ByRef で受け渡しを行うことができます。


なお、ADODB には複数のバージョンがあるため、そのプロシージャーが別プロジェクトからも
再利用可能なメソッドとして公開されるような場合は、それぞれのバイナリ互換性を維持するために、
意図的に古いインターフェイス名で宣言することが求められるケースがあります。
(たとえば As Recordset15 / As Recordset21 など)
Private スコープの場合は気にする必要は無いですけれどね。
https://j.mp/2LwMLVW
https://j.mp/2Wx27je



> のどちらがよいのでしょうか

結論から言えばどちらも駄目かな…。


そもそも、戻り値を使わないのであれば、Function ではなく Sub にすべきです。
そして Function を使うなら、必ず戻り値の「As 型」を明記することが求められます。

ご存知の通り、戻り値の型を省略した場合には『既定のデータ型』とみなされますが、
それには頼らず、必ず As を記述するべきです。
(初期設定では、Variant 型が『既定のデータ型』に割り当てられています)



また、今回の質問文からは、その引数が「入力用」なのか「出力用」なのかが曖昧です。

呼び出し側で Recordset インスタンスを生成しておき、
それを Func に渡して処理させたいのでしょうか?(入力用)

それとも、Func 側で Recordset インスタンスを生成して、
それを呼び出し側に結果として返したいのでしょうか? (出力用)


前者(入力用)だとすれば、引数は ByVal であるべきです。
この場合、インスタンスの生成は、呼び出す側で面倒をみることになります。
Close も同様に、呼び出す側で面倒を見ることが多いですが、
終了処理を依頼するようなプロシージャーでは、呼ばれた側で Close することもあります。
Open 処理を呼び出す側と呼ばれる側のどちらで行うかは、ケースバイケースです。

 Private Sub Main()
  Dim rs As ADODB.Recordset
  Set rs = New ADODB.Recordset '呼び出し元でインスタンスを作って
   :
  Func rs 'それを ByVal な引数で渡す
   :
  MsgBox rs.GetString(NullExpr:="(null)"), vbInformation
  rs.Close
 End Sub

 Private Sub Func(ByVal rs As ADODB.Recordset)
   'rs のインタンスは生成済みなので、ここでは New しない
 End Sub


一方、後者(出力用)だとすれば、ByRef 引数ではなく戻り値を使うことが推奨されます。
この場合、呼び出された側でインスタンスが生成され、それを呼び出し側で受け取ることになります。
Close 処理は呼び出す側で受け持つことになるでしょう。
Open メソッドについては、こちらもケースバイケースです。

たとえば、インスタンス生成のみを受け持つプロシージャーとする場合は、
開かずに返却して、呼び出し側で Open することがあります。
たとえば、インタンス生成後の初期化処理までも受け持つようなプロシージャーの場合は
開いた状態で返却するために、呼び出された側で Open しておくことがあります。


 Private Sub Main()
  Dim rs As ADODB.Recordset
  Set rs = Func()
  MsgBox rs.GetString(NullExpr:="(null)"), vbInformation
  rs.Close
  With Func()
   MsgBox .GetString(NullExpr:="(null)"), vbInformation
   .Close
  End With
 End Sub

 Private Function Func() As ADODB.Recordset
  Dim rs As ADODB.Recordset
  Set rs = New ADODB.Recordset
   :
  Set Func = rs
 End Sub



しかし提示されたコードでは、引数の前に ByVal や ByRef といったキーワードが省略されていました。
(VB6 でこれらを省略した場合、ByRef 渡しであることを意味します)

自作したプロシージャーの引数で、オブジェクトを ByRef で渡す必要があるのは、それが
入出力両方のパターンで扱われる場合だけですので、本当に入出力両方が必要なケースなのか確認しておきましょう。

[ツリー表示へ]
タイトルRe^8: Com Surrogate
記事No16576
投稿日: 2020/05/18(Mon) 15:52
投稿者でふぁいあんと

byRefとbyValの区別は一応、理解しております。
また、例示した関数も ret = Func(adoRS as object) as string
のとして、関数内で発生したエラーを文字列としてretに返しております
本当はマジックナンバーを使うべきなのでしょうか。天下御免の一人開発なので、、、


この辺にもご質問したいのですが
(ディクショナリー型の渡しなんてもうちんぷんかんぷんです。)
今回は、表題とずれてしまうので、控えます。
頂いた回答は、ゆっくり熟読させていただきます。

重ね重ね重ねありがとうございました

[ツリー表示へ]