tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトル実行中のACCESSファイル群のパス取得方法
記事No10322
投稿日: 2010/11/26(Fri) 19:37
投稿者camputer
 今晩は,質問させていただきます.camputerと申します。
どうぞよろしくお願いいたします.
 環境:Win7+VB.NET2008+Access2010
  (Access2007以前でも結構でございます)
 歴:プログラム歴10年程度、VB歴3年程度
でございます。

 沢山のPDFファイルとアクセスファイルを開いて作業をする際、
ある時点で開いているそれらファイルを、一覧(パス)として
取っておきたい事が頻繁にございます。
 アクセスとPDFをVB.NETから触った事がございませんでしたので、
解決できると信じ数日間、検索いたしておりましたが
全然出てまいりません。。

 エクセルやパワーポイントの場合は、試しに
        Dim myApp As Object = Nothing
        Dim myPre As Object = Nothing
        Dim myPres As Object = Nothing

        myApp = GetObject(, "PowerPoint.Application")
        myPres = myApp.Presentations

        For Each myPre In myPres
            MsgBox(myPre.Path())
            MRComObject(myPre)
        Next
のような形にいたしましたらうまくパスが取れるのでございますが、
ACCESSだけ根本的にコーディングが異なるのか、
似たコードが見つかりません。

 ACCESSのVBEのオブジェクトブラウザから「Path」と
検索するなどいたしまして、出てきたCurrentProject、
Forms、CurrentDb等で試行錯誤いたしましたがうまくいかず、
VBHhanatyan内を検索させていただきましても分かりません。。

 Officeのメンバ構成はバージョンによって全然違いますが、
おそらくACCESS2007は近いと思われますし、その他の
バージョンでも結構でございますので、
もし何かヒントなどございましたら是非ともアドバイスいただきたく
どうぞよろしくお願いいたします。

[ツリー表示へ]
タイトルRe: 実行中のACCESSファイル群のパス取得方法
記事No10323
投稿日: 2010/11/26(Fri) 21:04
投稿者魔界の仮面弁士
>  エクセルやパワーポイントの場合は、試しに
Excel 等では、一つのアプリ内で複数の文書を開けます。
一方、Access では一つのファイルしか開けません。

もちろん、複数のアプリケーションを起動するという事は
どちらの製品でも可能ですけれどね。


> ACCESSだけ根本的にコーディングが異なるのか、
> 似たコードが見つかりません。
実際に試してはいませんが、イメージ的にはこんな感じになるのかな。

Try
  AccApp = GetObject(, "Access.Application")  '起動済みのインスタンスを取得
Catch
  AccApp = CreateObject("Access.Application") '起動されていないので新規に起動
  'AccApp.OpenCurrentDatabase(dbFilePath)
Finally
  AccApp.Visible = True
End Try
AccDb = AccApp.CurrentDb()
If AccDb Is Nothing Then
  MsgBox("開かれている DB はありません。")
Else
  MsgBox(AccDb.Name)
End If


なお、アプリケーション自体が複数起動されている状態においては、
GetObject で単純に取得…というわけには行きません(Excel でも Access でも)。
それぞれのインスタンスを取得したいようなケースにおいては、
ROT(Running Object Table)から辿っていく必要があります。

http://eternalwindows.jp/com/moniker/moniker04.html
http://msdn.microsoft.com/ja-jp/library/system.runtime.interopservices.comtypes.irunningobjecttable.enumrunning.aspx

[ツリー表示へ]
タイトルRe^2: 実行中のACCESSファイル群のパス取得方法
記事No10324
投稿日: 2010/11/26(Fri) 21:44
投稿者魔界の仮面弁士
>   AccApp = GetObject(, "Access.Application")  '起動済みのインスタンスを取得
(中略)
> なお、アプリケーション自体が複数起動されている状態においては、
> GetObject で単純に取得…というわけには行きません(Excel でも Access でも)。
> それぞれのインスタンスを取得したいようなケースにおいては、
> ROT(Running Object Table)から辿っていく必要があります。

ROT からオブジェクトを得るサンプル。

Imports System.Runtime.InteropServices.ComTypes
Imports System.Runtime.InteropServices
Public Class Form1
    Private Declare Function GetRunningObjectTable Lib "ole32" _
        (ByVal reserved As Integer, _
         <Out()> ByRef pprot As IRunningObjectTable) As Integer

    Private Declare Function CreateBindCtx Lib "ole32" _
        (ByVal reserved As Integer, _
         <Out()> ByRef ppbc As IBindCtx) As Integer

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
        Button1.Text = "更新"
    End Sub

    Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
        ListBox1.DataSource = GetROT()
    End Sub

    Private Function GetROT() As List(Of String)
        Dim names As New List(Of String)()
        Dim rot As IRunningObjectTable = Nothing
        Dim hr As Integer = GetRunningObjectTable(0, rot)
        If hr = 0 AndAlso rot IsNot Nothing Then
            Dim oBindCtx As IBindCtx = Nothing
            CreateBindCtx(0, oBindCtx)
            Dim oMoniker() As IMoniker = {Nothing}
            Dim oEnumMoniker As IEnumMoniker = Nothing
            rot.EnumRunning(oEnumMoniker)
            Do While oEnumMoniker.Next(1, oMoniker, IntPtr.Zero) = 0
                Dim dispName As String = ""
                oMoniker(0).GetDisplayName(oBindCtx, Nothing, dispName)
                Marshal.ReleaseComObject(oMoniker(0))
                oMoniker(0) = Nothing
                names.Add(dispName)
            Loop
            Marshal.ReleaseComObject(oBindCtx)
            oBindCtx = Nothing
            Marshal.ReleaseComObject(oEnumMoniker)
            oEnumMoniker = Nothing
            Marshal.ReleaseComObject(rot)
            rot = Nothing
        End If
        Return names
    End Function

    Private Function GetObjectFromROT(ByVal dispName As String) As Object
        GetObjectFromROT = Nothing
        Dim rot As IRunningObjectTable = Nothing
        Dim hr As Integer = GetRunningObjectTable(0, rot)
        If hr = 0 AndAlso rot IsNot Nothing Then
            Dim oBindCtx As IBindCtx = Nothing
            CreateBindCtx(0, oBindCtx)
            Dim oMoniker() As IMoniker = {Nothing}
            Dim oEnumMoniker As IEnumMoniker = Nothing
            rot.EnumRunning(oEnumMoniker)
            Do While oEnumMoniker.Next(1, oMoniker, IntPtr.Zero) = 0
                Dim s As String = ""
                oMoniker(0).GetDisplayName(oBindCtx, Nothing, s)
                If s = dispName Then
                    rot.GetObject(oMoniker(0), GetObjectFromROT)
                End If
                Marshal.ReleaseComObject(oMoniker(0))
                oMoniker(0) = Nothing
                If s = dispName Then
                    Exit Do
                End If
            Loop
            Marshal.ReleaseComObject(oBindCtx)
            oBindCtx = Nothing
            Marshal.ReleaseComObject(oEnumMoniker)
            oEnumMoniker = Nothing
            Marshal.ReleaseComObject(rot)
            rot = Nothing
        End If
    End Function

    Private Sub ListBox1_DoubleClick(ByVal sender As Object, ByVal e As EventArgs) Handles ListBox1.DoubleClick
        Dim dispName As String = CStr(ListBox1.SelectedItem)
        If MsgBox("取得します。" & vbCrLf & dispName, vbOKCancel) = vbCancel Then
            Return
        End If
        Dim o As Object = GetObjectFromROT(dispName)
        If o Is Nothing Then
            MsgBox("見つかりませんでした。")
        Else
            MsgBox("取得しました。" & vbCrLf & TypeName(o))
            '
            '
            'ここに、取得したオブジェクトに対する処理を記述。
            '
            '
            Marshal.ReleaseComObject(o)
        End If
    End Sub
End Class

[ツリー表示へ]
タイトルRe^2: 実行中のACCESSファイル群のパス取得方法
記事No10325
投稿日: 2010/11/26(Fri) 21:53
投稿者camputer
 魔界の仮面弁士博士m(_ _)m

 どうもありがとうございます!!
お忙しい中いつもご親切にアドバイスをいただき
誠にありがとうございます。
また突破口が見えて参りました^^

>一方、Access では一つのファイルしか開けません。
はい、なるほどFor Each〜にはならないはずでございますね。

>実際に試してはいませんが、イメージ的にはこんな感じになるのかな。
どうもありがとうございます!今から手元の2010で試してまいります。

>なお、アプリケーション自体が複数起動されている状態においては、
    :
>ROT(Running Object Table)から辿っていく必要があります。
 はい、仰られますとおり、もし今私の作りたいアプリケーションを
EXCEL.EXEなどでもやりたい、となった際には
複数のEXEを管理しないと漏れが出てしまいますね。
>ROT
でございますか。これも勉強してまいります。


 まずアドバイスいただいた事へのお礼だけになってしまいますが、
本当にいつもご親切にどうもありがとうございます。
お陰で今週末も楽しめそうでございます^^

 再度ご報告させていただきます。
どうもありがとうございました!!!!

[ツリー表示へ]
タイトルRe^2: 実行中のACCESSファイル群のパス取得方法
記事No10326
投稿日: 2010/11/26(Fri) 21:56
投稿者camputer
(間違えて2重投稿いたしました^^;
今、いただいた2つ目のアドバイスを読ませていただいております。。。)

[ツリー表示へ]
タイトルRe^3: 実行中のACCESSファイル群のパス取得方法
記事No10327
投稿日: 2010/11/26(Fri) 22:19
投稿者camputer
(前のコメントを投稿させていただいた後に、2つ目のアドアイスを
読ませていただきました^^)

>ROT からオブジェクトを得るサンプル。
どうもありがとうございます!!m(_ _)m
 すぐにこんなコードを出せていただけますとは。
もしかして今まで目を通された(or 作られた)コードの在処を
覚えてらっしゃるんでしょうか??

(それともこの40分の間にぱぱっとコーディングなされて。。。^^; ?)

 勉強になります。こちら(ROT)についても、1から自分で作成すると
また週末を何度か費やすところでした。どうもありがとうございます!!

 では今からいじってまいります(^ー^)ゞ
どうもありがとうございました!!

[ツリー表示へ]
タイトルRe^4: 実行中のACCESSファイル群のパス取得方法
記事No10328
投稿日: 2010/11/26(Fri) 23:48
投稿者魔界の仮面弁士
> (それともこの40分の間にぱぱっとコーディングなされて。。。^^; ?)

ですます。

というか、最初の回答の段階では、どうすれば ROT を取得できるのかすら
知りませんでした。(必要な API やインターフェイスを把握していませんでした)

ROT という仕組みがあるという事は、K.J.K.さんの受け売りで知ってはいましたし、
Visual Studio 付属の ROT VIWER で、列挙できることも知っていたのですけれどね。


で、google で調べたところ、No.10323 の末尾にも記載した
http://eternalwindows.jp/com/moniker/moniker04.html
というページがあったので、初回投稿時にその旨を紹介してはいますが、
実際に組んだのは初めてです。
(先のコードは、上記のサンプルを VB.NET に翻訳しただけです)


>  勉強になります。こちら(ROT)についても、1から自分で作成すると
> また週末を何度か費やすところでした。どうもありがとうございます!!

いえいえ、回答者側としても勉強する機会になったのでありがたいです。

# たまたま、本業のアプリのコンパイル待ちで
# 数十分ほど暇を持て余していたという事情もありますが。(^^;

[ツリー表示へ]
タイトルRe^5: 実行中のACCESSファイル群のパス取得方法
記事No10329
投稿日: 2010/11/27(Sat) 08:25
投稿者camputer
 おはようございます!
何度もコメントいただけて幸いですm(_ _)m
(今朝までプログラミングでしばらく遊んだ後、数時間寝ておりました。。^^;
お返事が遅くなり申しわけございません)

 なんとコーディングなさってくださったとの事で誠にありがとうございます!
一瞬でしたのでどこかお手持ちの宝箱から、ドンピシャのコードを
探してきてコピペなさってくださったものかと。。^^;
(確かにコードを読ませていただくと、今回の私の質問に特化した
Formになってました^^)

 (まだ完成しておりませんが)色々と試させていただきましたが、
TypeName(o) → o.name と変更いたしましたところ、「MICROSOFT ACCESS」
と、実行中のアプリケーション名も取れるようでございますので
ROTで列挙後にACCESSかどうか確認すれば、本Questionのタイトル通りの
事が完璧に行えそうでございます。どうもありがとうございます。



 あと読ませていただいて気づきましたが、ACCESSに限定せず
他のどのアプリに対してでも使えますねこのコード^^
質問時に
> 沢山のPDFファイルとアクセスファイルを開いて作業をする際、
>ある時点で開いているそれらファイルを、一覧(パス)として
>取っておきたい事が頻繁にございます。
と書かせていただいておりますが、実はPDF側もこれ(一覧取得)をやりたかった
次第でございます。(まだPDF側は納得ゆくまで検索&トライしきって
おりませんでしたので、質問はさせていただきませんでした。。)

 PDFでもROTリストに「〜.pdf」という名で入ってきてくれますので
拡張子をif文にかけてやればすべて解決できそうでございます^^
(何故かPDFはGetObjectFromROT(dispName)が取れませんが(?)、
こちらは自分で色々試してみます)


 納得いき次第、再度ご報告させていただきます。
本当にどうもありがとうございます。m(_ _)m

[ツリー表示へ]
タイトルRe^6: 実行中のACCESSファイル群のパス取得方法
記事No10330
投稿日: 2010/11/27(Sat) 10:25
投稿者魔界の仮面弁士
# これは No.10329 への返信ですが、スレッドが深くなってきたので
# 最初の投稿(No.10322)への返信として投稿します。


> TypeName(o) → o.name と変更いたしましたところ
Office 系あるいは Visual Studio 系のアプリのインスタンスであれば、
Name プロパティで簡易名を得たり、Application プロパティで
最上位オブジェクトへの参照を得られるたりするのですが、そもそも
Name プロパティを持たないオブジェクトに対して o.Name を実行すると、
MissingMemberException 例外が発生する事になります。

実際には DisplayName や TypeName だけでは、型を判断には
不十分ですね。まぁ、ある程度の推測はできそうですけれども。


> PDFでもROTリストに「〜.pdf」という名で入ってきてくれますので
試しに、デスクトップ上の pdf ファイルをダブルクリックで起動し、
ファイルが Adobe Reader 8 で開かれた状態で先のサンプルを
起動してみたところ、TypeName が "IAcroAXDoc" なオブジェクトを取得できました。

ただ、取得できる期間が限られているのか、起動後しばらくすると
ROT から削除されました。何だろう…?


> 何故かPDFはGetObjectFromROT(dispName)が取れませんが
取得しようとした時点で、ROT から削除されてしまっていたのかも。


なお、ローカルの PDF ファイルを開くのではなく、Web 上の PDF を
IE 上で開いた場合は、ROT に表示されず、先の手法では見えませんでした。

[ツリー表示へ]
タイトルRe^7: 実行中のACCESSファイル群のパス取得方法
記事No10331
投稿日: 2010/11/27(Sat) 10:30
投稿者魔界の仮面弁士
> 試しに、デスクトップ上の pdf ファイルをダブルクリックで起動し、
> ファイルが Adobe Reader 8 で開かれた状態で先のサンプルを
> 起動してみたところ、TypeName が "IAcroAXDoc" なオブジェクトを取得できました。

この "IAcroAXDoc" オブジェクトの型は、
 Adobe Acrobat 8.0 Type Library
  C:\Program Files\Adobe\Reader 8.0\Reader\AcroRd32.dll
のものが使われているようでした。

また、このオブジェクトは 以下の 3 つのメンバーを持っていました。
  Event Stop()
  Function Application() As Object
  Function Document() As Object
ただし、Application メソッドも Document メソッドも、実際に呼び出すと
未実装例外を返してしまいました。どうやらメンバーのみで、中身は無いようです。
 '未実装例外(NotImplementedException) が発生してしまう
 Dim doc As Object = o.Document


> なお、ローカルの PDF ファイルを開くのではなく、Web 上の PDF を
> IE 上で開いた場合は、ROT に表示されず、先の手法では見えませんでした。

ROT には見当たりませんでしたが、対象の InternetExplorer オブジェクトの
Document プロパティから、"AcroPDF" 型のオブジェクトを得ることができました。
こちらの型情報は
 Adobe Acrobat 7.0 Browser Control Type Library 1.0
  C:\Program Files\Common Files\Adobe\Acrobat\ActiveX\AcroPDF.dll
のようです。

この場合 src プロパティで文書ファイルの場所を得られました。
 '"http://www.nta.go.jp/tetsuzuki/shinsei/annai/gensen/pdf/h22_01.pdf" を得られた
 MsgBox(doc.src)

といっても、この場合には AcroPDF の src プロパティを使うまでもなく、
InternetExplorer オブジェクトの locationURL プロパティで
同じ内容が取得できますけれどね。


Private Sub Button2_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button2.Click
    Dim ShellWindows As Object
    ShellWindows = GetObject("new:9BA05972-F6A8-11CF-A442-00A0C90A8F39")

    ListBox2.Items.Clear()
    Dim cnt As Integer = CInt(ShellWindows.Count)
    For n As Integer = 0 To cnt - 1
        Dim ie As Object = ShellWindows.Item(CObj(n))
        ListBox2.Items.Add(String.Format("[{0:x8}] ({1},{2}) - '{3}'" _
            , ie.HWND, ie.Left, ie.Top, ie.LocationName))
        Dim doc As Object = ie.document
        ListBox2.Items.Add("=>" & TypeName(doc))
        'ListBox2.Items.Add("=>" & doc.src)

        If doc IsNot Nothing Then
            Marshal.ReleaseComObject(doc)
        End If
        Marshal.ReleaseComObject(ie)
    Next
    Marshal.ReleaseComObject(ShellWindows)
End Sub



>>> 何故かPDFはGetObjectFromROT(dispName)が取れませんが
先の No.10324 の実装だと、同じ dispName を持つエントリがあった場合に、
先頭のものしか取得できないという問題もあります。適宜修正してみてください。

[ツリー表示へ]
タイトルRe^8: 実行中のACCESSファイル群のパス取得方法
記事No10332
投稿日: 2010/11/28(Sun) 02:46
投稿者camputer
 魔界の仮面弁士様

 どうもありがとうございます!!m(_ _)m
ご回答をいただけるタイミングでは無いと思って外出しておりましたm(_ _)m
お返事が遅くなり申し訳ございません。なんとPDFの方の謎までご解明なされたとの事で。。

>Name プロパティを持たないオブジェクトに対して o.Name を実行すると、
>MissingMemberException 例外が発生する事になります。
 はい、私も午前中に何度もトライ中にエラー検出を確認いたしまして
(意味不明でございましたが^^;)、If Err.Number = 〜 などと
途中までコーディングしどうするか悩んでおりました。

>ただ、取得できる期間が限られているのか、起動後しばらくすると
>ROT から削除されました。
    :
>> 何故かPDFはGetObjectFromROT(dispName)が取れませんが
>取得しようとした時点で、ROT から削除されてしまっていたのかも。
 はい、ご推察なさられた通りのようでございます。
取得時間のご指摘をいただいてから、私の今の環境Vista + AdobeReader8 でも
確認いたしましたところ、最初だけ"IAcroAXDoc"が出てまいりました。

>この "IAcroAXDoc" オブジェクトの型は、
> Adobe Acrobat 8.0 Type Library
>  C:\Program Files\Adobe\Reader 8.0\Reader\AcroRd32.dll
    :
    :
>また、このオブジェクトは 以下の 3 つのメンバーを持っていました。
>  Event Stop()
>  Function Application() As Object
>  Function Document() As Object
    :
    :
>対象の InternetExplorer オブジェクトの
>Document プロパティから、"AcroPDF" 型のオブジェクトを得ることができました。
>こちらの型情報は
> Adobe Acrobat 7.0 Browser Control Type Library 1.0
>  C:\Program Files\Common Files\Adobe\Acrobat\ActiveX\AcroPDF.dll
>のようです。

>この場合 src プロパティで文書ファイルの場所を得られました。

 どうもありがとうございます!
私が理解するよりどんどん先に未知の問題発見、しかも解決まで。。。^^;
ご自身が触れた事のない部分を見つけた瞬間に、御検証に
取り掛かられるんですねー(・_・) 新しいバージョンのVBや
Officeが出た際にも、そのような感じなのでございましょうか。
(これじゃ一生経っても追いつける気がしないです -_-) )

>といっても、この場合には AcroPDF の src プロパティを使うまでもなく、
>InternetExplorer オブジェクトの locationURL プロパティで
>同じ内容が取得できますけれどね。
 全然存じませんでした^^; どうもありがとうございます。
私が作業する分にはIEは用いておりませんが、どうせそのうちIEを触るソフトも
作りたくなるでしょうし、今回色々触ってみます。

 では今からまたVBで遊んでまいります^^ 納得のいくところまで
完成いたしましたところで、再度ご報告させていただきます。

>適宜修正してみてください。
 (`▽´)ゞ I,Sir!!


============(追記になります)==================================

 何度も何度も追記いたしまして申し訳ございません。
この度は誠にありがとうございました。m(_ _)m
 検索に検索を重ねまして、PDFのパスを取る方法を調べ続けましたが
結局出てこず、こちらはあきらめようと思います。。
 しかしご質問させていただいたACCESSについては完璧で、他のOfficeソフトに
ついても、ほとんど取る事ができるようになりました。
(魔界の仮面弁士様からいただいたROTのコードが
メインで機能するソフトとなりました^^)

 ACCESSのみの質問にも関わらず、すごい物をいただきまして
どうもありがとうございました。
ただ、いただいたコードの素晴らしさに対して、質問タイトルが
かなり勿体無い感じになってしまった感がございますが。。。
 今回いお教えただいたROTは今後自分がコーディングする上で
かなり宝物になりそうでございます^^

 お陰で、もう少しで今回の面白いソフトが完成いたします。
この度はご親切にアドバイスいただきまして誠にありがとうございました!!!
m(_ _)m

[ツリー表示へ]