<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>外部アプリケーションの操作 | VBA・GAS・Pythonで仕事を楽しく効率化</title>
	<atom:link href="https://officevba.info/category/vba%e5%85%b1%e9%80%9a/other-application-operation/feed/" rel="self" type="application/rss+xml" />
	<link>https://officevba.info</link>
	<description>仕事の役に立つVBA・GAS・Pythonのコードを紹介していきます。</description>
	<lastBuildDate>Sat, 31 Jan 2026 12:23:01 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.1</generator>

<image>
	<url>https://officevba.info/wp-content/uploads/2017/04/cropped-Excel_1-32x32.jpg</url>
	<title>外部アプリケーションの操作 | VBA・GAS・Pythonで仕事を楽しく効率化</title>
	<link>https://officevba.info</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>WindowsAPIを用いてウインドウの一覧表を作成するVBAコード（再帰処理）</title>
		<link>https://officevba.info/recursion-getwindow/</link>
					<comments>https://officevba.info/recursion-getwindow/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 03 Dec 2023 02:20:41 +0000</pubDate>
				<category><![CDATA[ウインドウの取得]]></category>
		<category><![CDATA[recursion]]></category>
		<category><![CDATA[WindowsAPI]]></category>
		<category><![CDATA[再帰処理]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2479</guid>

					<description><![CDATA[目次 ウインドウ操作に必要になるウインドウハンドルを子ウインドウも含めて全て取得したいウインドウハンドルとは？再帰処理とは？再帰処理を用いたウインドウハンドルを取得するVBAコードについて ウインドウ操作に必要になるウイ [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-2" checked><label class="toc-title" for="toc-checkbox-2">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">ウインドウ操作に必要になるウインドウハンドルを子ウインドウも含めて全て取得したい</a></li><li><a href="#toc2" tabindex="0">ウインドウハンドルとは？</a></li><li><a href="#toc3" tabindex="0">再帰処理とは？</a></li><li><a href="#toc4" tabindex="0">再帰処理を用いたウインドウハンドルを取得するVBAコードについて</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">ウインドウ操作に必要になるウインドウハンドルを子ウインドウも含めて全て取得したい</span></h2>
<p>以前こちらの記事で（https://officevba.info/window-list/）ウインドウの一覧を取得するコードをご紹介しました。</p>
<p>このコードは階層構造になっている最上段のウインドウのみを取得するものだったのですが、ウインドウの内部をクリックしたり操作したりする際は子ウインドウや孫ウインドウなど下層にあるウインドウを順に取得しないといけないことも多く、一回で必要なすべてのウインドウの情報を取得することができませんでした。</p>
<p>子ハンドルや孫ハンドルを含めてウインドウハンドルを取得できない理由はウインドウの階層が一律でなく一般的なループ処理では対応できないことなのですが、以前フォルダとファイルを全て取得する処理で学んだ再帰処理を使うことで様々な階層のウインドウハンドルを全て取得できるようになりました。</p>
<p>今回は再帰処理を用いて開いているウインドウのすべてのハンドルを取得するコードをご紹介します。</p>
<h2><span id="toc2">ウインドウハンドルとは？</span></h2>
<p>Windowsは開いたウインドウや、ウインドウに配置されているパーツなどそれぞれに一つずつにハンドルという数値型の値を割り当てて制御しているらしいです。</p>
<p>「ウインドウ」ハンドルと言ってもVBAでいうところのオブジェクトのような感じで、エクスプローラーの開いた画面のような見た目がウインドウのものから、テキストボックスやアイコン、ステータスバーなど色々なものに割り当てられていて、理論上はウインドウハンドルさえ取得できればWindowsAPIを用いて対象を自由に操作できるらしいです。（私自身が何でもできるわけではありませんのでらしいと記載しましたがかなり機能は豊富そうです。）</p>
<p>上記の通りWindowsAPIを用いて操作対象のウインドウを指定する際、このウインドウハンドルを指定することが大事なのですが、ウインドウハンドルはウインドウを開くたびにランダムに新しい数値が割り当てられるため、プログラムを実行する過程でウインドウハンドルを取得する手段がないとWindowsAPIでは操作ができないということになります。</p>
<h2><span id="toc3">再帰処理とは？</span></h2>
<p>再帰処理はプロシージャの中に自分自身を呼び出すコードを記載することで、ループ処理時の階層が一定でなくても各階層に同じ処理を行うことができることです。</p>
<p>よく使われるのはあるフォルダの中に様々な階層のフォルダが格納されていてそれぞれにファイルが格納されている際に、各階層を順に探ってファイルとフォルダの一覧を書き出したり、ファイルをコピーしたりするのに使われます。</p>
<p>ちなみに上記のような処理をしたい場合、Pythonの標準モジュールのglobを使えば引数を一つ指定するだけで再帰処理を実行することが可能です。<br />
VBAではコードを書いて実装しないといけないですが書き方に慣れが必要なので、この辺りはPythonの方が新しい言語でより洗練されている気がします。</p>
<h2><span id="toc4">再帰処理を用いたウインドウハンドルを取得するVBAコードについて</span></h2>
<p>ウインドウが階層構造になっていて、かつウインドウごとに一定の階層ではないというのが再帰で解決できるぴったりの環境で、以前ウインドウハンドルの一覧表を作成するコードを勉強していた時には再帰処理の書き方を知らなかったので実装できず、作成したコードはウインドウの最上位だけを取得するのみでした。</p>
<p>必要なウインドウハンドルを取得するため、必要な場合は一層下ったものを探る処理を何度も繰り返して取得していましたが、再帰処理を使えば一回で全部解決させることができますし、今回の以下のようなコードの書き方では階層の情報も把握できるのでかなり便利になります。</p>
<div class="VBACode">
<pre>
'ウインドウの中の次または前のウインドウハンドルを取得する
Declare Function GetNextWindow Lib "user32" _
    Alias "GetWindow" _
    (ByVal hWnd As Long, _
    ByVal wFlag As Long) As Long

'ウインドウのキャプションタイトルを取得する
Declare Function GetWindowText Lib "user32" _
    Alias "GetWindowTextA" _
    (ByVal hWnd As Long, ByVal lpString As String, _
    ByVal cch As Long) As Long

'ウインドウが可視かどうかを取得する
Declare Function IsWindowVisible Lib "user32" _
    (ByVal hWnd As Long) As Long

'ウインドウのクラス名を取得する
Declare Function GetClassName Lib "user32" _
    Alias "GetClassNameA" _
    (ByVal hWnd As Long, _
    ByVal lpClassName As String, _
    ByVal nMaxCount As Long) As Long
    
'ウインドウハンドルの子を取得する
Declare Function FindWindowEx Lib "User32.dll" _
    Alias "FindWindowExA" ( _
    ByVal hWndParent As Long, _
    ByVal hwndChildAfter As Long, _
    ByVal lpszClass As String, _
    ByVal lpszWindow As String) As Long
 
'ウインドウハンドルを取得する
Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
        (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Public Const GW_HWNDLAST = 1
Public Const GW_HWNDNEXT = 2

Sub 再帰でWindow一覧のキャプションとクラスを取得する()
    
    With ActiveSheet
        .Cells(1, 1) = "ハンドル"
        .Cells(1, 2) = "クラス名"
        .Cells(1, 3) = "キャプション名"
    End With
    
    Dim k As Long: k = 2
    Dim hWnd As Long: hWnd = FindWindow(vbNullString, vbNullString) '引数をvbNullStringにすると1つめのウインドウを取得する

    Call ウインドウの取得recursion(hWnd, k)

End Sub

Sub ウインドウの取得recursion(ByRef hWnd As Long, ByRef k As Long)
    
    Dim strClassNameCh As String * 100
    Dim strCaptionCh As String * 80
    
    Do
        If IsWindowVisible(hWnd) Then
            
            GetWindowText hWnd, strCaptionCh, Len(strCaptionCh)
            GetClassName hWnd, strClassNameCh, Len(strClassNameCh)
            
            Dim i As Long: i = ActiveSheet.UsedRange.Rows.Count + 1
            
            With ActiveSheet
                .Cells(i, 1).Value = hWnd
                .Cells(i, k).Value = Left(strClassNameCh, InStr(strClassNameCh, vbNullChar) - 1)
                .Cells(i, k + 1).Value = Left(strCaptionCh, InStr(strCaptionCh, vbNullChar) - 1)
            End With
            
            DoEvents
            
            Dim hWndCh As Long: hWndCh = FindWindowEx(hWnd, 0, vbNullString, vbNullString)
            Call ウインドウの取得recursion(hWndCh, k + 2)
        
        End If
        
        hWnd = GetNextWindow(hWnd, GW_HWNDNEXT)
        
    Loop Until hWnd = GetNextWindow(hWnd, GW_HWNDLAST)
            
End Sub


</pre>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/recursion-getwindow/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>複数のパスワード付きzipに一括で色々なファイルを格納するVBAコード</title>
		<link>https://officevba.info/vbazipfilewithpassword/</link>
					<comments>https://officevba.info/vbazipfilewithpassword/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 19 Dec 2021 13:54:46 +0000</pubDate>
				<category><![CDATA[DOSコマンドの実行]]></category>
		<category><![CDATA[LhaPlus]]></category>
		<category><![CDATA[VBA]]></category>
		<category><![CDATA[zip]]></category>
		<category><![CDATA[コマンドプロンプト]]></category>
		<category><![CDATA[自動化]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2353</guid>

					<description><![CDATA[目次 同僚からの依頼でzipファイル格納のVBA検討zip格納に「LhaPlus」を使用する今回考えた自動zip格納VBAツールの仕組み事前準備ツールファイルの画面ツール操作実行手順今回作成したVBAコード①フォルダ取得 [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-4" checked><label class="toc-title" for="toc-checkbox-4">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">同僚からの依頼でzipファイル格納のVBA検討</a></li><li><a href="#toc2" tabindex="0">zip格納に「LhaPlus」を使用する</a></li><li><a href="#toc3" tabindex="0">今回考えた自動zip格納VBAツールの仕組み</a><ol><li><a href="#toc4" tabindex="0">事前準備</a></li><li><a href="#toc5" tabindex="0">ツールファイルの画面</a></li><li><a href="#toc6" tabindex="0">ツール操作実行手順</a></li></ol></li><li><a href="#toc7" tabindex="0">今回作成したVBAコード</a><ol><li><a href="#toc8" tabindex="0">①フォルダ取得</a></li><li><a href="#toc9" tabindex="0">②zip格納</a></li></ol></li><li><a href="#toc10" tabindex="0">動作確認の動画とあとがき</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">同僚からの依頼でzipファイル格納のVBA検討</span></h2>
<p>最近社内でメールのシステムが変更になり、これまで外部への添付ファイルは自動で「①zipに格納→②パスワード生成→③別メールでパスワードを送付」ができていたのですが、手作業でzipを作成することになりました。</p>
<p>元々パスワード付きzipファイルをメールで送るフロー自体が色々と問題を引き起こす可能性も指摘されているので、その運用自体止めてしまえばよい気もするのですが、セキュリティ対策の主観部署がこれまで行っていたzip格納を止める判断をしなかったらしく上記のように手作業の負担が増えていました。</p>
<p>今回はパスワード付きzipファイルに必要なファイルを連続で圧縮するExcelVBAコードを考えてみました。</p>
<h2><span id="toc2">zip格納に「LhaPlus」を使用する</span></h2>
<p>以前<a href="https://officevba.info/vbazipfile/" target="_blank">こちらの記事</a>で紹介していますが、zip格納を安定して実行するためにLhaPlusというフリーソフトを使用します。</p>
<p>Windows標準の機能を呼び出してzip格納をする方法もあるのですが、公式なサポート外らしくエラーが起こるし動作が不安定でした。<br />
LhaPlusはコマンドプロンプトから操作ができるようになっていて、それを利用することでVBAからも自動操作が可能になります。</p>
<h2><span id="toc3">今回考えた自動zip格納VBAツールの仕組み</span></h2>
<p>今回私が考えたツールの動作と業務のフローは以下の通りです。</p>
<h3><span id="toc4">事前準備</span></h3>
<p>①zipファイルに格納したいファイルをフォルダごとにまとめてあるフォルダに格納</p>
<p>②zip変換用フォルダをまとめて格納しているフォルダにツールファイルも一緒に格納（vba162_zip一括返還211219.xlsm）</p>
<p>詳細は以下の画像のような状態です。</p>
<p><a href="https://officevba.info/wp-content/uploads/2021/12/vba162-1.jpg"><img fetchpriority="high" decoding="async" src="https://officevba.info/wp-content/uploads/2021/12/vba162-1-700x248.jpg" alt="" width="700" height="248" class="alignnone size-large wp-image-2349" srcset="https://officevba.info/wp-content/uploads/2021/12/vba162-1-700x248.jpg 700w, https://officevba.info/wp-content/uploads/2021/12/vba162-1-300x106.jpg 300w, https://officevba.info/wp-content/uploads/2021/12/vba162-1-768x272.jpg 768w, https://officevba.info/wp-content/uploads/2021/12/vba162-1-1536x545.jpg 1536w, https://officevba.info/wp-content/uploads/2021/12/vba162-1.jpg 2019w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<h3><span id="toc5">ツールファイルの画面</span></h3>
<p>ツールファイルは以下のような画面で作成しています。<br />
まずzipを作成するためのフォルダ一覧を取得して、パスワードを手入力したあと、zipを作成するフローを想定しています。</p>
<p><a href="https://officevba.info/wp-content/uploads/2021/12/vba162-2.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/2021/12/vba162-2-700x314.jpg" alt="" width="700" height="314" class="alignnone size-large wp-image-2350" srcset="https://officevba.info/wp-content/uploads/2021/12/vba162-2-700x314.jpg 700w, https://officevba.info/wp-content/uploads/2021/12/vba162-2-300x135.jpg 300w, https://officevba.info/wp-content/uploads/2021/12/vba162-2-768x345.jpg 768w, https://officevba.info/wp-content/uploads/2021/12/vba162-2.jpg 1226w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<h3><span id="toc6">ツール操作実行手順</span></h3>
<p>①フォルダ取得ツールでzipに格納したいフォルダ一覧を取得<br />
「フォルダ取得」ボタンをクリックするとツールフォルダが格納されているフォルダをB1セルに表示し、その中のサブフォルダを一覧にしてA列に取得して入力</p>
<p>②作成するzipファイルのパスワードを入力（B列）<br />
パスワードが規則性のあるものにするならVBAで自動化することもできますが、今回は手入力するフローにしています。</p>
<p>③zip格納ボタンをクリック<br />
サブフォルダの中のファイルが同じ名前のzipに格納され、作成されたサブフォルダ「zip格納」の中にzipファイルが作成される</p>
<h2><span id="toc7">今回作成したVBAコード</span></h2>
<h3><span id="toc8">①フォルダ取得</span></h3>
<div class="GASCode">
<pre>Sub フォルダ取得()
    
    Dim FSO As Object, WSH As Object
    
    Dim FoldPath As String
    Dim FolderName As String
    Dim ArrFolderName() As String
    
    Dim i As Long, k As Long
    
    Set FSO = CreateObject("Scripting.FileSystemObject")
    FoldPath = ThisWorkbook.Path

    Cells(1, 2) = FoldPath
    i = 0
    FolderName = Dir(FoldPath &#038; "\*", vbDirectory)
    Do While FolderName <> ""
        If InStr(FolderName, ".") = 0 And FolderName <> "zip格納" Then
            ReDim Preserve ArrFolderName(i)
            ArrFolderName(i) = FolderName
            Cells(i + 4, 1) = FolderName
            'Cells(i + 4, 2) = Left(Cells(i + 4, 1), 4)
            i = i + 1
        End If
    FolderName = Dir()
    Loop
    
End Sub</pre>
</div>
<h3><span id="toc9">②zip格納</span></h3>
<div class="GASCode">
<pre>Sub サブフォルダzip格納()

    Dim Cmd As String
    Dim ArrFileName, FileName
    Dim FSO, WSH
    Dim 前FoldPath, 後FoldPath

    Set FSO = CreateObject("Scripting.FileSystemObject")
    Set WSH = CreateObject("Wscript.Shell")

    WSH.CurrentDirectory = "C:\Program Files (x86)\Lhaplus"

    '前FoldPathに格納されているサブフォルダをzip格納して後FoldPathに格納
    前FoldPath = Cells(1, 2)
    後FoldPath = Cells(1, 2) &#038; "\zip格納"

    'zip格納用のフォルダ作成
    If FSO.FolderExists(後FoldPath) = False Then
        FSO.CreateFolder 後FoldPath 'フォルダ作成
    End If

    '後で使用する対象ファイル一覧表が残っていたら削除
    If FSO.FileExists(前FoldPath &#038; "\圧縮リスト.txt") = True Then
        FSO.DeleteFile 前FoldPath &#038; "\圧縮リスト.txt"
    End If

    Dim i
    i = 4
    Do Until Cells(i, 1) = ""
        ArrFileName = ""
        FileName = Dir(前FoldPath &#038; "\" &#038; Cells(i, 1) &#038; "\*.*", vbNormal)
        Do While FileName <> ""
            If ArrFileName <> "" Then
                ArrFileName = ArrFileName &#038; vbCrLf &#038; 前FoldPath &#038; "\" &#038; Cells(i, 1) &#038; "\" &#038; FileName
            Else
                ArrFileName = 前FoldPath &#038; "\" &#038; Cells(i, 1) &#038; "\" &#038; FileName
            End If
        FileName = Dir()
        Loop

        'コマンドプロンプトは文字数の上限があるので、対象ファイル一覧を「圧縮リスト.txt」に入力
        With FSO.CreateTextFile(Cells(1, 2) &#038; "\圧縮リスト.txt")
            .WriteLine ArrFileName
            .Close
        End With

        'cells(i,2)がPassword 空欄ならパスワード設定なしでzip格納
        Cmd = "Lhaplus.exe /c:zip /n:" &#038; 後FoldPath &#038; "\" &#038; Cells(i, 1) &#038; ".zip" &#038; " /p:" &#038; Cells(i, 2).Text &#038; " /l:" &#038; Cells(1, 2) &#038; "\圧縮リスト.txt"

        WSH.Run "%ComSpec% /c " &#038; Cmd, 7, True

        FSO.DeleteFile Cells(1, 2) &#038; "\圧縮リスト.txt"

    i = i + 1
    Loop

    Set FSO = Nothing
    Set WSH = Nothing

End Sub</pre>
</div>
<h2><span id="toc10">動作確認の動画とあとがき</span></h2>
<p>今回作成したコードを実行した動画は以下の通りです。</p>
<div style="width: 1280px;" class="wp-video"><video class="wp-video-shortcode" id="video-2353-1" width="1280" height="720" preload="metadata" controls="controls"><source type="video/mp4" src="https://officevba.info/wp-content/uploads/2021/12/vba162-3.mp4?_=1" /><a href="https://officevba.info/wp-content/uploads/2021/12/vba162-3.mp4">https://officevba.info/wp-content/uploads/2021/12/vba162-3.mp4</a></video></div>
<p>パスワード付きzipファイルをメールで送る文化には賛否両論ありますが、私の会社含めまだまだ根強く残ると思います。<br />
自動サービスがなくなっていってしまう中手作業でこの処理を続けるのは結構つらいと思うので自動処理を考えてみました。</p>
<p>今回ご紹介したコードはコマンドプロンプトを起動している時間PCを使えない（キャンセルしてしまう可能性がある）ので、大量のファイルをzipに格納する場合、別のPCを使うか休憩時間前などに実行だけして休憩時間中に作成することをおすすめします。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/vbazipfilewithpassword/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://officevba.info/wp-content/uploads/2021/12/vba162-3.mp4" length="0" type="video/mp4" />

			</item>
		<item>
		<title>ウェブ上の画像をダウンロードするWindowsAPI関数URLDownloadToFileを使いこなす</title>
		<link>https://officevba.info/urldownloadtofile/</link>
					<comments>https://officevba.info/urldownloadtofile/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 14 Jun 2020 07:37:54 +0000</pubDate>
				<category><![CDATA[WindowsAPI]]></category>
		<category><![CDATA[IE操作]]></category>
		<category><![CDATA[ダウンロード]]></category>
		<guid isPermaLink="false">http://officevba.info/?p=2135</guid>

					<description><![CDATA[目次 ExcelVBAで画像ダウンロードを自動化する手順WindowsAPIのURLDownloadToFile関数についてVBAでURLDownloadToFile関数を使う事前準備についてURLDownloadToF [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-6" checked><label class="toc-title" for="toc-checkbox-6">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">ExcelVBAで画像ダウンロードを自動化する手順</a></li><li><a href="#toc2" tabindex="0">WindowsAPIのURLDownloadToFile関数について</a></li><li><a href="#toc3" tabindex="0">VBAでURLDownloadToFile関数を使う事前準備について</a></li><li><a href="#toc4" tabindex="0">URLDownloadToFileの使い方について</a></li><li><a href="#toc5" tabindex="0">URLDownloadToFileの実行サンプル</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">ExcelVBAで画像ダウンロードを自動化する手順</span></h2>
<p>仕事で色々なウェブサイトにアップされている複数の画像をダウンロードする必要があり、その手順を自動化するのにどうすればよいかネットで調べていたところ、WindowsAPIのURLDownloadToFileを使う方法を見つけました。</p>
<p>今回はURLDownloadToFileを用いてウエブページの画像やファイルをダウンロードする方法をご紹介します。</p>
<h2><span id="toc2">WindowsAPIのURLDownloadToFile関数について</span></h2>
<p>WindowsAPIはWindowsで行う処理をプログラム上で実行するために用意されている機能です。<br />
私もほんの数種類しか把握できていないのですが、WindowsAPIには色々な分野にわたり便利な機能がたくさんあります。</p>
<p>導入にはコツが必要で、慣れないと使うハードルが高いのですが、このWindowsAPIはExcelをはじめとするVBAでも使用することができ、使いこなすことでVBA単独では難しい様々な処理をすごく簡単に実行できるようになります。</p>
<p>今回ご紹介するのはWindowsAPIのURLDownloadToFileという関数で、これはウェブサイトからファイル・画像などをダウンロードするための専用の関数です。</p>
<p>（他にもWindowsAPIを用いて表示されているウインドウを取得したり、ボタンをクリックしたり、テキストボックスに文字を入力したりする方法、待ち時間を設定する方法などをご紹介していますので興味あれば<a href="https://officevba.info/category/vba%e5%85%b1%e9%80%9a/other-application-operation/windowsapi-vbscript/" target="_blank">こちら</a>のカテゴリページから見てみてください。）</p>
<h2><span id="toc3">VBAでURLDownloadToFile関数を使う事前準備について</span></h2>
<p>VBAでURLDownloadToFile関数を使うためには、実行させるコードにURLDownloadToFile（〇〇）という内容を記載するだけでなく、URLDownloadToFileとはAPIのこの関数ですよというのを事前に登録しておく必要があります。</p>
<p>具体的には宣言セクションに下記の内容を記載します。</p>
<div class="VBACode">Private Declare PtrSafe Function URLDownloadToFile Lib &#8220;urlmon&#8221; Alias &#8220;URLDownloadToFileA&#8221; _<br />
<span class="VBA_Tab1">(ByVal pCaller As Long, ByVal szURL As String, ByVal szFileName As String, _</span><br />
<span class="VBA_Tab1">ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long</span>
</div>
<p>一応ざっくりと説明すると「urlmon」というライブラリファイルに用意されている「URLDownloadToFileA」という関数を「URLDownloadToFile」という名前で使用しますという意味らしいですが、おまじないのようなものなので詳しく意味を把握していなくても問題ありません。</p>
<p>画像のダウンロードに関しては現実的な方法として、対象のページを開くことが多いと思いますので、宣言セクションに下記も併せて記述してSleep関数も使用できるようにしておく方が良いと思います。</p>
<div class="VBACode">#If VBA7 Then<br />
Private Declare PtrSafe Sub Sleep Lib &#8220;kernel32&#8221; (ByVal ms As LongPtr)<br />
#Else<br />
Private Declare Sub Sleep Lib &#8220;kernel32&#8221; (ByVal ms As Long)<br />
#End If<br />
End Sub</div>
<p>その他に準備としてIEを用いてウェブページを開いて操作するので、「ツール」→「参照設定」から下記2つのチェックを付けて利用できるようにしましょう。</p>
<li>Microsoft HTML Object Library</li>
<li>Microsoft Internet Controls</li>
<h2><span id="toc4">URLDownloadToFileの使い方について</span></h2>
<p>上記のURLDownloadToFileの事前準備をしておけばVBAで使用する自体は簡単です。</p>
<p>ダウンロードを実行したいプロシージャ内に「URLDownloadToFile(0, ウェブ上のURL, 保存先フォルダ, 0, 0)」と書けばOKです。<br />
引数は対象ファイルのウェブ上のURL、ダウンロード先のフォルダ名になります。</p>
<p>あとダウンロードがうまくいった場合、URLDownloadToFile関数は「0」という数字を返すので戻り値を取得できるように下記のように記述するのが一般的だと思います。</p>
<li>戻り値=URLDownloadToFile(0, 対象ファイルウェブ上のURL, 保存先フォルダ, 0, 0)</li>
<p>VBA界の権威であるOfficeTANAKAの田中先生もご自身のサイトで記載されていましたが、URLDownloadToFile関数自体の使い方は簡単ですが、ウェブの対象ファイルのURLをどうやって取得してくるかの方がはるかに難しいと思います。</p>
<h2><span id="toc5">URLDownloadToFileの実行サンプル</span></h2>
<p>URLDownloadToFile関数の使い方を各だけではこの関数の使い方や便利さを実感しにくいと思ったので、下記のようにサンプルを考えてみました。</p>
<p>これはワークシートのA1列に表示されているURLのページに埋め込まれている画像ファイルをすべてデスクトップの「画像」というフォルダに格納するVBAコードになります。</p>
<p>この方法だと多少強引で無駄も多いのですが、対象サイトの構造をあまり詳しく調べなくても全部の画像データを取得できるので汎用性は高いと思います。</p>
<p>また特定のファイルだけダウンロードしたい場合にもファイル名の規則などを調べるのにも役立ちます。</p>
<div class="VBACode">Option Explicit<br />
<br />
#If VBA7 Then<br />
Private Declare PtrSafe Sub Sleep Lib &#8220;kernel32&#8221; (ByVal ms As LongPtr)<br />
#Else<br />
Private Declare Sub Sleep Lib &#8220;kernel32&#8221; (ByVal ms As Long)<br />
#End If<br />
<br />
Private Declare PtrSafe Function URLDownloadToFile Lib &#8220;urlmon&#8221; Alias &#8220;URLDownloadToFileA&#8221; _<br />
<span class="VBA_Tab1"> (ByVal pCaller As Long, ByVal szURL As String, ByVal szFileName As String, _</span><br />
<span class="VBA_Tab1">ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long</span><br />
<br />
Sub IE表示()<br />
<br />
<span class="VBA_Tab1">Dim IE As Object</span><br />
<span class="VBA_Tab1">Set IE = CreateObject(&#8220;InternetExplorer.application&#8221;)</span><br />
<span class="VBA_Tab1">IE.Visible = True</span><br />
<span class="VBA_Tab2">Sleep 1000</span><br />
<span class="VBA_Tab1">IE.navigate (Cells(1, 1).Text)</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’ページが表示されるまで待つ</span></span><br />
<span class="VBA_Tab1">Dim x</span><br />
<span class="VBA_Tab1">x = 0</span><br />
<span class="VBA_Tab1">Do While (IE.Busy Or IE.readyState < READYSTATE_COMPLETE) And x <= 10</span><br />
<span class="VBA_Tab2">DoEvents</span><br />
<span class="VBA_Tab2">Sleep 1000</span><br />
<span class="VBA_Tab1">Loop</span><br />
<span class="VBA_Tab1">Sleep 1000</span><br />
<br />
<span class="VBA_Tab1">Dim WSH As Object</span><br />
<span class="VBA_Tab1">Set WSH = CreateObject(&#8220;Wscript.Shell&#8221;)</span><br />
<br />
<span class="VBA_Tab1">Dim FSO As Object</span><br />
<span class="VBA_Tab1">Set FSO = CreateObject(&#8220;Scripting.FileSystemObject&#8221;)</span><br />
<br />
<span class="VBA_Tab1">Dim DesktopPath As String</span><br />
<span class="VBA_Tab2">DesktopPath = WSH.SpecialFolders(&#8220;Desktop&#8221;)</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’ダウンロード先のフォルダがあるか存在確認</span></span><br />
<span class="VBA_Tab1">If FSO.FolderExists(DesktopPath &#038; &#8220;画像&#8221;) = False Then</span><br />
<span class="VBA_Tab2"><span class="VBA_Comment">’存在しなければ作成する</span></span><br />
<span class="VBA_Tab2">FSO.CreateFolder DesktopPath &#038; &#8220;画像&#8221;</span><br />
<span class="VBA_Tab1">End If</span><br />
<br />
<span class="VBA_Tab1">Dim imgSrc As String</span><br />
<span class="VBA_Tab1">Dim imgName As String</span><br />
<span class="VBA_Tab1">Dim strPath As String</span><br />
<span class="VBA_Tab1">Dim lngRes As Long</span><br />
<br />
<span class="VBA_Tab1">Dim txtInput</span><br />
<br />
<span class="VBA_Tab1">Set txtInput = IE.document.getElementsByTagName(&#8220;img&#8221;)</span><br />
<br />
<span class="VBA_Tab1">For x = 0 To txtInput.Length － 1</span><br />
<br />
<span class="VBA_Tab2">imgSrc = txtInput(x).src</span><br />
<span class="VBA_Tab2">imgName = Mid(imgSrc, InStrRev(imgSrc, &#8220;/&#8221;) + 1)</span><br />
<br />
<span class="VBA_Tab2"><span class="VBA_Comment">’ファイル名に使えない文字列を除外する</span></span><br />
<span class="VBA_Tab2">imgName = Replace(Replace(imgName, &#8220;:&#8221;, &#8220;&#8221;), &#8220;?&#8221;, &#8220;&#8221;)</span><br />
<br />
<span class="VBA_Tab2"><span class="VBA_Comment">’ダウンロード先のパスを指定する</span></span><br />
<span class="VBA_Tab2">strPath = DesktopPath &#038; &#8220;画像&#8221; &#038; x &#038; &#8220;_&#8221; &#038; imgName</span><br />
<br />
<span class="VBA_Tab2"><span class="VBA_Comment">’ウェブ上に格納されているファイル名が拡張子の後に文字列が追加されている場合の処理</span></span><br />
<span class="VBA_Tab2">If Left(Right(strPath, 4), 1) <> &#8220;.&#8221; Then</span><br />
<span class="VBA_Tab3"><span class="VBA_Comment">’画像ファイル名に.jpgが含まれているなら</span></span><br />
<span class="VBA_Tab3">If InStr(strPath, &#8220;.jpg&#8221;) <> 0 Then</span><br />
<span class="VBA_Tab4">strPath = Left(strPath, InStr(strPath, &#8220;.jpg&#8221;) + 3)</span><br />
<span class="VBA_Tab3"><span class="VBA_Comment">’gifに対しての処理</span></span><br />
<span class="VBA_Tab3">ElseIf InStr(strPath, &#8220;.gif&#8221;) <> 0 Then</span><br />
<span class="VBA_Tab4">strPath = Left(strPath, InStr(strPath, &#8220;.gif&#8221;) + 3)</span><br />
<span class="VBA_Tab3"><span class="VBA_Comment">’pngに対しての処理</span></span><br />
<span class="VBA_Tab3">ElseIf InStr(strPath, &#8220;.png&#8221;) <> 0 Then</span><br />
<span class="VBA_Tab4">strPath = Left(strPath, InStr(strPath, &#8220;.png&#8221;) + 3)</span><br />
<span class="VBA_Tab3">End If</span><br />
<br />
<span class="VBA_Tab2">End If</span><br />
<br />
<span class="VBA_Tab2"><span class="VBA_Comment">’ファイルのダウンロード実行</span></span><br />
<span class="VBA_Tab2">lngRes = URLDownloadToFile(0, imgSrc, strPath, 0, 0)</span><br />
<br />
<span class="VBA_Tab2"><span class="VBA_Comment">’ワークシートに情報残す</span></span><br />
<span class="VBA_Tab2">Cells(x + 1, 2) = x</span><br />
<span class="VBA_Tab2">Cells(x + 1, 3) = imgSrc</span><br />
<span class="VBA_Tab2">Cells(x + 1, 4) = imgName</span><br />
<span class="VBA_Tab2">Cells(x + 1, 5) = lngRes</span><br />
<br />
<span class="VBA_Tab2"><span class="VBA_Comment">’ダウンロードがうまくいかなかったら色付けする</span></span><br />
<span class="VBA_Tab2">If lngRes <> 0 Then</span><br />
<span class="VBA_Tab3">Range(Cells(x + 1, 2), Cells(x + 1, 5)).Interior.Color = RGB(255, 200, 200)</span><br />
<span class="VBA_Tab2">End If</span><br />
<br />
<span class="VBA_Tab2">imgSrc = &#8220;&#8221;</span><br />
<span class="VBA_Tab2">imgName = &#8220;&#8221;</span><br />
<br />
<span class="VBA_Tab1">Next x</span><br />
<br />
<span class="VBA_Tab1">IE.Quit</span><br />
<br />
<span class="VBA_Tab1">Set FSO = Nothing</span><br />
<span class="VBA_Tab1">Set WSH = Nothing</span><br />
<span class="VBA_Tab1">Set txtInput = Nothing</span><br />
<span class="VBA_Tab1">Set IE = Nothing</span><br />
<br />
End Sub</div>
<p>私は楽天のサイトで<a rel="noopener" href="https://search.rakuten.co.jp/search/mall/%E3%83%AD%E3%83%AC%E3%83%83%E3%82%AF%E3%82%B9/301981/" target="_blank">商品検索結果が表示されているページ</a>で画像を取得するイメージでコードを作りました。</p>
<p>楽天のサイトの場合、SVG拡張子のロゴなどが大量に含まれていたり、対象ファイル名にWindows上で使用できない文字が含まれているためにダウンロードが進まなかったりしました。<br />
そのあたりは少しページごとに調整が必要になります。</p>
<p>あとはダウンロードをする際にJPGのみにするとかPNGのみにするとか特定の文字を含むものにするかなど検討するとムダなダウンロードをせずに済みますので、工夫次第でより便利なコードにできると思います。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/urldownloadtofile/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>HTMLのオブジェクトの位置を取得してカーソルを合わせるExcelVBAコード</title>
		<link>https://officevba.info/iunknown_queryservice/</link>
					<comments>https://officevba.info/iunknown_queryservice/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 05 Jan 2020 14:09:27 +0000</pubDate>
				<category><![CDATA[InternetExplorer(IE)操作]]></category>
		<category><![CDATA[WindowsAPI]]></category>
		<guid isPermaLink="false">http://officevba.info/?p=1894</guid>

					<description><![CDATA[目次 HTMLオブジェクトを取得できても操作できないケースの対応方法を調べた結果をご紹介HTMLオブジェクトを取得できても操作できないケースと対処法IEのオブジェクトの画面での位置を取得するAPI関数 HTMLオブジェク [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-8" checked><label class="toc-title" for="toc-checkbox-8">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">HTMLオブジェクトを取得できても操作できないケースの対応方法を調べた結果をご紹介</a></li><li><a href="#toc2" tabindex="0">HTMLオブジェクトを取得できても操作できないケースと対処法</a></li><li><a href="#toc3" tabindex="0">IEのオブジェクトの画面での位置を取得するAPI関数</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">HTMLオブジェクトを取得できても操作できないケースの対応方法を調べた結果をご紹介</span></h2>
<p>直近で私が作業の自動化を行ったウェブサイトでは、HTMLの構造を取得して文字を入力しても受付されないものがありました。</p>
<p>ログイン画面にIDとパスワードを入力して個別ページに移動する際、不正ログインを防止するためなのか、単純に無効なIDとパスワードを検出するためなのか不明ですが、テキストボックスに入力したIDとパスワードをうまく読み込んでいない現象がありました。</p>
<p>今回はウェブサイト側に認識される形でテキストボックスにIDとパスワードを入力する方法について私が調べて作ったExcelコードを紹介します。</p>
<h2><span id="toc2">HTMLオブジェクトを取得できても操作できないケースと対処法</span></h2>
<p>HTML情報から対象となるオブジェクトを取得できても操作できないケースでこれまで私が遭遇したものとしては、JavaScriptなどのプログラムが含まれている場合があります。</p>
<p>一例として、取得したオブジェクトをクリックした際にある場所に入力されている内容をサーバーに送信したりするプログラムが実行されるものがあったりすると操作がうまく進まないケースがあります。</p>
<p>簡単なプログラムであれば内容を見てそのプログラム自体を一つずつ手作業で入力した時と同じ順序で実行していく方法が考えられます。<br />
プログラムの実行方法は<a href="https://officevba.info/vbaexecscript/" target="_blank">こちら</a>のページで紹介しています。</p>
<p>今回の操作対象では実行されるプログラム処理内容の詳細は見えなかったので上記のプログラムを実行する方法が使えませんでした。</p>
<p>ただ、今回のケースではIDとパスワードを入力してログインするフローの中で、IDとパスワードを入力するにあたって、カーソルを移動させるタイミングで入力された値をチェックするプログラムが働いていることが挙動を見ていてわかりました。</p>
<p>そのため、オブジェクトの位置を認識してマウスを移動させてカーソルを合わせてクリックしてやれば問題が解決するのではないかと推測し、対応方法を調べました。</p>
<h2><span id="toc3">IEのオブジェクトの画面での位置を取得するAPI関数</span></h2>
<p>IEのオブジェクト位置を取得するAPI関数は「IUnknown_QueryService」というものです。</p>
<p>あまり私自身詳しく把握していないのですが、4つの引数のうち最初の引数にオブジェクトを指定すると4つ目の引数に位置情報を含むデータが出力されるという使い方ができるようです。</p>
<p>（本来は使い方が違うのかなと思いますので詳細わかりましたら追記いたします。）</p>
<p>宣言部分の記載方法は下記の通りです。</p>
<div class="VBACode">Public Declare PtrSafe Function IUnknown_QueryService Lib &#8220;shlwapi.dll&#8221; _<br />
<span class="VBA_Tab1">(ByVal punk As IUnknown, guidService As GUID, riid As GUID, ppvOut As IAccessible) As Long</span><br />
<br />
Public Type GUID<br />
<span class="VBA_Tab1">Data1 As Long</span><br />
<span class="VBA_Tab1">Data2 As Integer</span><br />
<span class="VBA_Tab1">Data3 As Integer</span><br />
<span class="VBA_Tab1">Data4(0 To 7) As Byte</span><br />
End Type</div>
<p>第二・第三引数で使用するGUIDの構造体の宣言も必要となります。<br />
ややこしいので意味わからなくてもおまじないと思って宣言部分に記載すれば大丈夫です。</p>
<p>例えばGoogleのページを表示した後、検索文字を入力するテキストボックスの位置を取得し、左クリックを押してカーソルを合わせるVBAコードは下記の通りです。</p>
<p>位置を取得したあと、カーソルを合わせる、クリックするなど後の処理があるのでSetCursorPosやmouse_eventなどのAPIも同時に使用します。<br />
これらの使い方については<a href="https://officevba.info/mouse_event/" target="_blank">こちら</a>の記事をご参照ください。</p>
<div class="VBACode"><span class="VBA_Comment0">’オブジェクトの位置取得用API関数</span><br />
Public Declare PtrSafe Function IUnknown_QueryService Lib &#8220;shlwapi.dll&#8221; _<br />
<span class="VBA_Tab1">(ByVal punk As IUnknown, guidService As GUID, riid As GUID, ppvOut As IAccessible) As Long</span><br />
<br />
<span class="VBA_Comment0">’オブジェクトの位置取得用構造体の宣言</span><br />
Public Type GUID<br />
<span class="VBA_Tab1">Data1 As Long</span><br />
<span class="VBA_Tab1">Data2 As Integer</span><br />
<span class="VBA_Tab1">Data3 As Integer</span><br />
<span class="VBA_Tab1">Data4(0 To 7) As Byte</span><br />
End Type<br />
<br />
<span class="VBA_Comment0">’Sleep（待機用）</span><br />
Public Declare PtrSafe Sub Sleep Lib &#8220;kernel32&#8221; (ByVal dwMilliseconds As Long)<br />
<br />
<span class="VBA_Comment0">’マウスカーソル位置を変更する関数</span><br />
Public Declare Function SetCursorPos Lib &#8220;user32&#8221; (ByVal x As Long, ByVal y As Long) As Long<br />
<br />
<span class="VBA_Comment0">’マウスをクリックするAPI</span><br />
Public Declare Sub mouse_event Lib &#8220;user32&#8221; ( _<br />
<span class="VBA_Tab1">ByVal dwFlags As Long, _</span><br />
<span class="VBA_Tab1">Optional ByVal dx As Long, _</span><br />
<span class="VBA_Tab1">Optional ByVal dy As Long, _</span><br />
<span class="VBA_Tab1">Optional ByVal dwDate As Long, _</span><br />
<span class="VBA_Tab1">Optional ByVal dwExtraInfo As Long)</span><br />
<br />
<span class="VBA_Comment0">’マウスクリック用定数</span><br />
Public Const MOUSE_LEFTDOWN = &#038;H2<br />
Public Const MOUSE_LEFTUP = &#038;H4<br />
<br />
Public Const MOUSE_RIGHTDOWN = &#038;H8<br />
Public Const MOUSE_RIGHTUP = &#038;H10<br />
<br />
Sub Googleの検索文字入力部分にカーソルを合わせて左クリック()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’第二・第三引数を使用するための部分</span></span><br />
<span class="VBA_Tab1">Dim IID_IAccessible As GUID</span><br />
<span class="VBA_Tab1">With IID_IAccessible</span><br />
<span class="VBA_Tab2">.Data1 = &#038;H618736E0</span><br />
<span class="VBA_Tab2">.Data2 = &#038;H3C3D</span><br />
<span class="VBA_Tab2">.Data3 = &#038;H11CF</span><br />
<span class="VBA_Tab2">.Data4(0) = &#038;H81</span><br />
<span class="VBA_Tab2">.Data4(1) = &#038;HC</span><br />
<span class="VBA_Tab2">.Data4(2) = &#038;H0</span><br />
<span class="VBA_Tab2">.Data4(3) = &#038;HAA</span><br />
<span class="VBA_Tab2">.Data4(4) = &#038;H0</span><br />
<span class="VBA_Tab2">.Data4(5) = &#038;H38</span><br />
<span class="VBA_Tab2">.Data4(6) = &#038;H9B</span><br />
<span class="VBA_Tab2">.Data4(7) = &#038;H71</span><br />
<span class="VBA_Tab1">End With</span><br />
<br />
<span class="VBA_Tab1">Dim IE As Object</span><br />
<span class="VBA_Tab1">Set IE = CreateObject(&#8220;InternetExplorer.Application&#8221;)</span><br />
<span class="VBA_Tab2">IE.Visible = True</span><br />
<br />
<span class="VBA_Tab1">IE.navigate (&#8220;https://www.google.co.jp/&#8221;)</span><br />
<br />
<span class="VBA_Tab1">Do While IE.Busy Or IE.readyState < READYSTATE_COMPLETE</span><br />
<span class="VBA_Tab2">DoEvents</span><br />
<span class="VBA_Tab2">Sleep 1</span><br />
<span class="VBA_Tab1">Loop</span><br />
<br />
<span class="VBA_Tab1">Dim e As Object</span><br />
<span class="VBA_Tab2">Set e = IE.document.getElementsByName(&#8220;q&#8221;)(0)</span><br />
<br />
<span class="VBA_Tab1">Dim hr As Long</span><br />
<span class="VBA_Tab2">Dim acc As IAccessible</span><br />
<span class="VBA_Tab2">hr = IUnknown_QueryService(e, IID_IAccessible, IID_IAccessible, acc)</span><br />
<br />
<span class="VBA_Tab1">Dim a As Long, b As Long, c As Long, d As Long</span><br />
<span class="VBA_Tab2">acc.accLocation a, b, c, d <span class="VBA_Comment">’a:左位置 b:上位置 c:横幅 d:高さ</span></span><br />
<br />
<span class="VBA_Tab1">Sleep 1000</span><br />
<br />
<span class="VBA_Tab1">SetCursorPos a + c / 2, b + d / 2 <span class="VBA_Comment">’テキストボックスの真ん中をクリック</span></span><br />
<span class="VBA_Tab2">mouse_event MOUSE_LEFTDOWN, 0, 0, 0, 0 <span class="VBA_Comment">’左クリックを押す</span></span><br />
<span class="VBA_Tab2">mouse_event MOUSE_LEFTUP, 0, 0, 0, 0 <span class="VBA_Comment">’左クリックを離す</span></span><br />
<br />
End Sub</div>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/iunknown_queryservice/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VBScriptでWindowsAPIを使用する方法</title>
		<link>https://officevba.info/vbscript-windowsapi/</link>
					<comments>https://officevba.info/vbscript-windowsapi/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Mon, 24 Dec 2018 15:23:56 +0000</pubDate>
				<category><![CDATA[WindowsAPI]]></category>
		<guid isPermaLink="false">http://officevba.info/?p=1696</guid>

					<description><![CDATA[目次 VBScriptのすぐに起動できるメリットを生かすウインドウを閉じるのに用いるWindows API関数は2つ①FindWindowA ②PostMessageウインドウを閉じるWindowsAPIを用いたVBAコ [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-10" checked><label class="toc-title" for="toc-checkbox-10">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">VBScriptのすぐに起動できるメリットを生かす</a></li><li><a href="#toc2" tabindex="0">ウインドウを閉じるのに用いるWindows API関数は2つ</a><ol><li><a href="#toc3" tabindex="0">①FindWindowA </a></li><li><a href="#toc4" tabindex="0">②PostMessage</a></li></ol></li><li><a href="#toc5" tabindex="0">ウインドウを閉じるWindowsAPIを用いたVBAコードとVBScript</a></li><li><a href="#toc6" tabindex="0">VBScriptでのWindowsAPI活用のポイント</a><ol><li><a href="#toc7" tabindex="0">①WindowsAPIを呼び出すのにExcelVBAを使う（もしくは他のVBAでもOKらしい（？））</a></li><li><a href="#toc8" tabindex="0">②WindowsAPI関数は「Excel.ExecuteExcel4Macro(“CALL(～)”)」で実行する</a></li></ol></li></ol>
    </div>
  </div>

<h2><span id="toc1">VBScriptのすぐに起動できるメリットを生かす</span></h2>
<p>最近RPA（Robotic Procee Automation）で業務効率化に取り組んでいますが、ある種類のアプリの中でウインドウを閉じる挙動がうまくいかず、開いているウインドウを閉じる方法を考えていました。</p>
<p>RPAだけを使っての解決方法は知識が足りず実現できず、とりあえず納期までにロボットを完成させないといけなかったのでWindowsAPIを使用してウインドウを閉じるツールを作り、それをRPAで起動して対処するという逃げ技を用いることにしました。</p>
<p>WindowsAPIはExcelVBAやAccessVBAで実行しても良かったのですが、できればファイルを開いたときにコンテンツの有効化などもクリックする必要なく、すぐに実行できる方法が良かったので、VBScriptでコードを書いてみました。</p>
<p>今回はアプリのWindowsAPIを用いてウインドウを閉じるVBScriptで操作する方法をVBAでの記載方法と比較しながら紹介します。</p>
<h2><span id="toc2">ウインドウを閉じるのに用いるWindows API関数は2つ</span></h2>
<p>開いているウインドウを閉じるのに直接用いるWindows API関数は下記2種類です。</p>
<h3><span id="toc3">①FindWindowA </span></h3>
<p>開いているウインドウのウインドウハンドルを取得するのに用います。</p>
<h3><span id="toc4">②PostMessage</span></h3>
<p>ハンドルを取得したウインドウに対して閉じるコードを送信します。</p>
<p>またその他にクラス名を調べるにはIsWindowVisible・GetClassName・GetNextWindowのWindows API関数を使用しています。</p>
<p>ウインドウのクラス名を取得する方法は<a href="https://officevba.info/window-list/" target="_blank">こちらのページ</a>をご確認ください。</p>
<h2><span id="toc5">ウインドウを閉じるWindowsAPIを用いたVBAコードとVBScript</span></h2>
<p>今回はAdobeReaderで開いたPDFファイルのウインドウを閉じるVBAコードとVBScriptをご紹介します。<br />
事前にAdobeReaderのクラス名が「AcrobatSDIWindow」であることを確認してからコードを作成しています。</p>
<li>VBAコード</li>
<div class="VBACode"><span class="VBA_Comment">’ウインドウハンドルを取得する</span><br />
Declare Function FindWindow Lib &quot;user32&quot; Alias &quot;FindWindowA&quot; _<br />
<span class="VBA_Tab2">(ByVal lpClassName As String, ByVal lpWindowName As String) As Long</span><br />
<br />
<span class="VBA_Comment">’メッセージの送信</span><br />
Declare Function PostMessage Lib &quot;user32&quot; Alias &quot;PostMessageA&quot; (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long<br />
<br />
Public Const WM_CLOSE = &amp;H10<br />
<br />
Sub PDFアプリのウインドウ閉じる()<br />
<br />
<span class="VBA_Tab1">Dim hWnd As Long</span><br />
<span class="VBA_Tab1">hWnd = FindWindow(&quot;AcrobatSDIWindow&quot;, vbNullString)</span><br />
<span class="VBA_Tab1">PostMessage hWnd, WM_CLOSE, 0, 0</span><br />
<br />
End Sub</div>
<p></p>
<li>VBScript</li>
<div class="VBACode">Dim hWnd<br />
Dim Excel<br />
Set Excel = WScript.CreateObject(&quot;Excel.Application&quot;)<br />
<br />
hWnd = Excel.ExecuteExcel4Macro(&quot;CALL(&quot;&quot;user32&quot;&quot;, &quot;&quot;FindWindowA&quot;&quot;, &quot;&quot;JCJ&quot;&quot;, &quot;&quot;AcrobatSDIWindow&quot;&quot;, 0 )&quot;)<br />
<br />
If hWnd = 0 Then WScript.Quit<br />
Call Excel.ExecuteExcel4Macro(&quot;CALL(&quot;&quot;user32&quot;&quot;, &quot;&quot;PostMessageA&quot;&quot;, &quot;&quot;JJJJJ&quot;&quot;, &quot; &amp; hWnd &amp; &quot;, 16, 0, 0)&quot;)<br />
<br />
Set Excel = Nothing</div>
<p>上記のコードをテキストエディタなどにコピペして、ファイルの拡張子を「.vbs」にすればファイルの完成です。<br />
ダブルクリックなどで起動すると開いているPDFのファイルが1つ閉じます。</p>
<h2><span id="toc6">VBScriptでのWindowsAPI活用のポイント</span></h2>
<p>VBScriptでのWindowsAPI活用のポイントは下記2点です。</p>
<h3><span id="toc7">①WindowsAPIを呼び出すのにExcelVBAを使う（もしくは他のVBAでもOKらしい（？））</span></h3>
<p>WindowsAPIをVBscriptで直接使用するのはハードルが高いようで、方法がわかりませんでした。<br />
調べたところVBAを中継して起動する方法が手軽そうだったのでExcelを介して使ってみました。</p>
<p>AccessやWordでもできるようですが、私が調べた限り方法がわかりませんでした。<br />
またわかればご紹介します。</p>
<h3><span id="toc8">②WindowsAPI関数は「Excel.ExecuteExcel4Macro(“CALL(～)”)」で実行する</span></h3>
<p>Excelを中継してVBScript内でWindowsAPI関数を使用する際はVBAで通常WindowsAPIを使用する場合と書き方が異なるようです。<br />
書式としてはExcel.ExecuteExcel4Macro(“CALL(～)”)でAPI関数を指定します。</p>
<p>Call関数の引数は「ライブラリ名」・「API関数名」・「API関数の返り値の型とAPI関数の引数の型をアルファベット1文字で表記したもの」・「API関数で使用する引数（複数の場合「,」で区切る）」の順で記載します。</p>
<p>3つ目の「API関数の返り値の型とAPI関数の引数の型をアルファベット1文字で表記したもの」はlongの値渡しの場合「J」、末尾がNullの文字列の値渡しは「C」となるようです。</p>
<p>関数自体の返り値を1文字目にして、2文字目以降はAPI関数の引数の型となりますが、複雑でわかりにくいので細かいこと考えず、どこかのネットのサンプル丸写しでもいい気がします。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/vbscript-windowsapi/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>WindowsAPIを用いてウインドウの一覧表を作成するVBAコード</title>
		<link>https://officevba.info/window-list/</link>
					<comments>https://officevba.info/window-list/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Wed, 05 Sep 2018 13:12:24 +0000</pubDate>
				<category><![CDATA[ウインドウの取得]]></category>
		<guid isPermaLink="false">http://officevba.info/?p=1527</guid>

					<description><![CDATA[目次 ウインドウのクラス名やキャプション名が取得できれば色々便利クラス名・キャプション名とはクラス名・キャプション名の一覧を取得するVBAコード ウインドウのクラス名やキャプション名が取得できれば色々便利 ウインドウ操作 [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-12" checked><label class="toc-title" for="toc-checkbox-12">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">ウインドウのクラス名やキャプション名が取得できれば色々便利</a></li><li><a href="#toc2" tabindex="0">クラス名・キャプション名とは</a></li><li><a href="#toc3" tabindex="0">クラス名・キャプション名の一覧を取得するVBAコード</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">ウインドウのクラス名やキャプション名が取得できれば色々便利</span></h2>
<p>ウインドウ操作をする際、対象となるウインドウを特定するにはウインドウハンドルを用いるのが一般的です。</p>
<p>このウインドウハンドルはウインドウごとの固有の値になりますが、開くたびに新しい値が割り振られるため、操作の度、毎回確認する必要があります。<br />
そのためウインドウハンドルを取得する前段階としてクラス名・キャプション名を把握しておく必要があります。</p>
<p>厳密には<a href="https://officevba.info/example-findwindow/" target="_blank">こちらの記事</a>に書いたようにどちらか片方の一部だけでも取得していれば繰り返し処理などでウインドルハンドルを取得できるのですが、コードが煩雑になったりわかりにくくなります。</p>
<p>今回は簡単にウインドウ取得に役立てられるクラス名・キャプション名一覧表を取得するVBAコードをご紹介します。</p>
<h2><span id="toc2">クラス名・キャプション名とは</span></h2>
<p>Windows上で開いているウインドウはすべてウインドウハンドル・クラス名・キャプション名などのプロパティを持っています。</p>
<p>詳しくは<a href="https://officevba.info/windowsapi-vba/" target="_blank">こちらの記事</a>でご紹介しましたが、クラス名はアプリケーションごとに固有の文字列、キャプション名はアプリケーションごとに異なり、ファイル名とアプリケーション名のどちらかまたは組み合わせの文字列です。</p>
<p>ウインドウハンドルはウインドウを開くたびに固有に割り振られる数字のため、変化することのないクラス名・キャプション名からウインドウハンドルを取得して操作対象を指定するのが一般的なウインドウ操作の流れになります。</p>
<p>VBAのコードを書く際はクラス名・キャプション名があらかじめわかっているアプリケーションを操作する方が、クラス名・キャプション名がまったくわからないアプリケーションを操作するよりもはるかにコードが簡単になります。</p>
<h2><span id="toc3">クラス名・キャプション名の一覧を取得するVBAコード</span></h2>
<p>今開いているウインドウのクラス名・キャプション名の一覧を取得するVBAコードは以下のようになります。<br />
使用するWindowsAPIは「FindWindow」「IsWindowVisible」「GetWindowText」「GetClassName」「GetNextWindow」の5つです。</p>
<p>それぞれの使い方については<a href="https://officevba.info/windowsapi-vba/" target="_blank">こちらの記事</a>を参考にしてください。</p>
<div class="VBACode"><span class="VBA_Comment">’ウインドウハンドルを取得する</span><br />
Declare Function FindWindow Lib &#8220;user32&#8221; Alias &#8220;FindWindowA&#8221; _<br />
<span class="VBA_Tab2">(ByVal lpClassName As String, ByVal lpWindowName As String) As Long</span><br />
<br />
<span class="VBA_Comment">’ウインドウが可視かどうかを取得する</span><br />
Declare Function IsWindowVisible Lib &#8220;user32&#8221; _<br />
<span class="VBA_Tab1">(ByVal hWnd As Long) As Long</span><br />
<br />
<span class="VBA_Comment">’ウインドウのキャプションタイトルを取得する</span><br />
Declare Function GetWindowText Lib &#8220;user32&#8221; Alias &#8220;GetWindowTextA&#8221; _<br />
<span class="VBA_Tab1">(ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long</span><br />
<br />
<span class="VBA_Comment">’ウインドウのクラス名を取得する</span><br />
Declare Function GetClassName Lib &#8220;user32&#8221; Alias &#8220;GetClassNameA&#8221; _<br />
<span class="VBA_Tab1">(ByVal hWnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long</span><br />
<br />
<span class="VBA_Comment">’取得中のウインドウの次または前のウインドウハンドルを取得する</span><br />
Declare Function GetNextWindow Lib &#8220;user32&#8221; Alias &#8220;GetWindow&#8221; _<br />
<span class="VBA_Tab1">(ByVal hWnd As Long, ByVal wFlag As Long) As Long</span><br />
<br />
Const GW_HWNDLAST = 1<br />
Const GW_HWNDNEXT = 2<br />
<br />
Sub ウインドウ一覧のクラス名とキャプション名を取得する()<br />
<br />
<span class="VBA_Tab1">Dim i As Long</span><br />
<span class="VBA_Tab1">i = 1</span><br />
<br />
<span class="VBA_Tab1">Dim strClassName As String * 100</span><br />
<span class="VBA_Tab1">Dim strCaption As String * 80</span><br />
<br />
<span class="VBA_Tab1">Dim hWnd As Long</span><br />
<span class="VBA_Tab1">hWnd = FindWindow(vbNullString, vbNullString) <span class="VBA_Comment">’引数を両方ともvbNullStringにして1つめのウインドウを取得する</span></span><br />
<br />
<span class="VBA_Tab1">Do</span><br />
<span class="VBA_Tab2">If IsWindowVisible(hWnd) Then</span><br />
<span class="VBA_Tab3">GetWindowText hWnd, strCaption, Len(strCaption)</span><br />
<span class="VBA_Tab3">GetClassName hWnd, strClassName, Len(strClassName)</span><br />
<br />
<span class="VBA_Tab3">Cells(i, 1).Value = Left(strClassName, InStr(strClassName, vbNullChar) &#8211; 1)</span><br />
<span class="VBA_Tab3">Cells(i, 2).Value = Left(strCaption, InStr(strCaption, vbNullChar) &#8211; 1)</span><br />
<br />
<span class="VBA_Tab3">i = Application.WorksheetFunction.Max(Cells(60000, 1).End(xlUp).Row, Cells(60000, 2).End(xlUp).Row, Cells(60000, 3).End(xlUp).Row) + 1</span><br />
<br />
<span class="VBA_Tab2">End If</span><br />
<br />
<span class="VBA_Tab2">hWnd = GetNextWindow(hWnd, GW_HWNDNEXT)</span><br />
<br />
<span class="VBA_Tab1">Loop Until hWnd = GetNextWindow(hWnd,<br />
 GW_HWNDLAST)</span><br />
<br />
End Sub</div>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/window-list/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VBAでWindowsAPIを用いてウインドウを取得する手順について</title>
		<link>https://officevba.info/example-findwindow/</link>
					<comments>https://officevba.info/example-findwindow/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Tue, 04 Sep 2018 13:31:15 +0000</pubDate>
				<category><![CDATA[ウインドウの取得]]></category>
		<guid isPermaLink="false">http://officevba.info/?p=1523</guid>

					<description><![CDATA[目次 WindowsAPIでのウインドウ取得には色々な手順があるウインドウの取得手順①キャプション名がわかっている場合②クラス名がわかっている場合③クラス名不明・キャプション名は一部だけわかる WindowsAPIでのウ [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-14" checked><label class="toc-title" for="toc-checkbox-14">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">WindowsAPIでのウインドウ取得には色々な手順がある</a></li><li><a href="#toc2" tabindex="0">ウインドウの取得手順</a><ol><li><a href="#toc3" tabindex="0">①キャプション名がわかっている場合</a></li><li><a href="#toc4" tabindex="0">②クラス名がわかっている場合</a></li><li><a href="#toc5" tabindex="0">③クラス名不明・キャプション名は一部だけわかる</a></li></ol></li></ol>
    </div>
  </div>

<h2><span id="toc1">WindowsAPIでのウインドウ取得には色々な手順がある</span></h2>
<p><a href="https://officevba.info/windowsapi-vba/" target="_blank">前回</a>はウインドウの取得によく使われるWindowsAPIの関数とウインドウのプロパティを紹介しました。<br />
これらのWindowsAPIの関数は機能が重複しているようで、ウインドウの取得に関してたくさんのアプローチがあります。</p>
<p>今回はこのWindowsAPIを使用して効率よくウインドウを取得する手順をいくつか紹介します。</p>
<h2><span id="toc2">ウインドウの取得手順</span></h2>
<p>ウインドウを取得する（=操作対象として確定する）のはウインドウハンドルを取得するのが基本となります。<br />
ウインドウハンドルを取得できれば、最前面に表示してアクティブにもできますし、キーボードやマウスでの操作も可能になります。</p>
<p>AppAtivateメソッドをキャプション名を引数に入れて実行してもウインドウを最前面にすることはできますが、その他の操作にはやはりウインドウハンドルを取得しておいた方が融通が利きます。</p>
<p>ここでは実際よく起こる下記のような条件の場合のウインドウを取得する手順についてご紹介します。</p>
<h3><span id="toc3">①キャプション名がわかっている場合</span></h3>
<p>WindowsAPIを使用する場合、キャプション名を指定した「FindWindow」関数でウインドウハンドルを取得します。</p>
<div class="VBACode">Declare Function FindWindow Lib &#8220;user32&#8221; Alias &#8220;FindWindowA&#8221; _<br />
<span class="VBA_Tab2">(ByVal lpClassName As String, ByVal lpWindowName As String) As Long</span><br />
<br />
Sub キャプション名からウインドウハンドルを取得する()<br />
<br />
<span class="VBA_Tab1">Dim hWnd As Long</span><br />
<span class="VBA_Tab1">hWnd = FindWindow(vbNullString, &#8220;キャプション名&#8221;)</span><br />
<br />
End Sub</div>
<h3><span id="toc4">②クラス名がわかっている場合</span></h3>
<p>同じクラス名のウインドウが他になければFindWindow(“クラス名”,vbNullString)で取得します。<br />
他に同じクラス名のウインドウがある場合、クラス名が不明の下記④の手順でウインドウハンドルを取得します。</p>
<div class="VBACode">Declare Function FindWindow Lib &#8220;user32&#8221; Alias &#8220;FindWindowA&#8221; _<br />
<span class="VBA_Tab2">(ByVal lpClassName As String, ByVal lpWindowName As String) As Long</span><br />
<br />
Sub クラス名からウインドウハンドルを取得する()<br />
<br />
<span class="VBA_Tab1">Dim hWnd As Long</span><br />
<span class="VBA_Tab1">hWnd = FindWindow(&#8220;OpusApp&#8221;, vbNullString)</span><br />
<br />
End Sub</div>
<h3><span id="toc5">③クラス名不明・キャプション名は一部だけわかる</span></h3>
<p>まず、クラス名・キャプション名をVbNullStringに指定してFindWindow関数を使用することで最初のウインドウを取得します。<br />
続けてそこからGetNextWindow関数で順番に最後までウインドウハンドルを取得します。</p>
<p>ウインドウハンドル一覧を取得すると当時にGetWindowText関数を用いてウインドウハンドルに対するキャプション名を取得し、あらかじめわかっていたキャプション名の一部を照合して該当のウインドウハンドルを取得します。</p>
<p>下記の例ではGoogleChromeのウインドウハンドルを取得する手順を記載しています。<br />
GoogleChromeはキャプション名が見えませんが、「Chrome」の文字で認識をかけることでウインドウハンドルを取得しています。</p>
<p>また、GetClassNameを使用すればキャプション名の全体を取得することも可能です。</p>
<div class="VBACode"><span class="VBA_Comment">’ウインドウハンドルを取得する</span><br />
Declare Function FindWindow Lib &#8220;user32&#8221; Alias &#8220;FindWindowA&#8221; _<br />
<span class="VBA_Tab2">(ByVal lpClassName As String, ByVal lpWindowName As String) As Long</span><br />
<br />
<span class="VBA_Comment">’ウインドウのキャプションタイトルを取得する</span><br />
Declare Function GetWindowText Lib &#8220;user32&#8221; Alias &#8220;GetWindowTextA&#8221; _<br />
<span class="VBA_Tab1">(ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long</span><br />
<br />
<span class="VBA_Comment">’ウインドウが可視かどうかを取得する</span><br />
Declare Function IsWindowVisible Lib &#8220;user32&#8221; (ByVal hWnd As Long) As Long<br />
<br />
<span class="VBA_Comment">’次または前のウインドウハンドルを取得する</span><br />
Declare Function GetNextWindow Lib &#8220;user32&#8221; Alias &#8220;GetWindow&#8221; _<br />
<span class="VBA_Tab1">(ByVal hWnd As Long, ByVal wFlag As Long) As Long</span><br />
<br />
Const GW_HWNDLAST = 1<br />
Const GW_HWNDNEXT = 2<br />
<br />
Sub キャプション名の一部からウインドウハンドルを取得する()<br />
<br />
<span class="VBA_Tab1">Dim hWnd As Long</span><br />
<span class="VBA_Tab1">Dim strCaption As String * 80</span><br />
<span class="VBA_Tab1">Dim 対象ウインドウハンドル As Long</span><br />
<br />
<span class="VBA_Tab1">hWnd = FindWindow(vbNullString, vbNullString) <span class="VBA_Comment">’1つめのウインドウを取得する</span></span><br />
<span class="VBA_Tab1">Do</span><br />
<span class="VBA_Tab2">If IsWindowVisible(hWnd) Then</span><br />
<span class="VBA_Tab3">GetWindowText hWnd, strCaption, Len(strCaption)</span><br />
<span class="VBA_Tab3">If InStr(strCaption, &#8220;Chrome&#8221;) <> 0 Then</span><br />
<span class="VBA_Tab4">対象ウインドウハンドル = hWnd</span><br />
<span class="VBA_Tab4">Exit Do</span><br />
<span class="VBA_Tab3">End If</span><br />
<span class="VBA_Tab2">End If</span><br />
<span class="VBA_Tab2">hWnd = GetNextWindow(hWnd, GW_HWNDNEXT)</span><br />
<span class="VBA_Tab1">Loop Until hWnd = GetNextWindow(hWnd, GW_HWNDLAST) <span class="VBA_Comment">’最後のウインドウになるまで繰り返す</span></span><br />
<br />
End Sub</div>
<p>キャプション名、クラス名いずれも全く見当がつかない場合、一覧を書き出してみて確認する方法もあります。<br />
ウインドウのキャプション名・クラス名の一覧表を作成するVBAコードはまた別の機会に紹介します。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/example-findwindow/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VBAでウインドウを取得するのによく使うWindowsAPIとその使い方</title>
		<link>https://officevba.info/windowsapi-vba/</link>
					<comments>https://officevba.info/windowsapi-vba/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Mon, 03 Sep 2018 09:32:11 +0000</pubDate>
				<category><![CDATA[ウインドウの取得]]></category>
		<guid isPermaLink="false">http://officevba.info/?p=1517</guid>

					<description><![CDATA[目次 WindowsAPIを使用すると色々な手順でウインドウが取得できるウインドウを取得するのに必要なプロパティ①ウインドウハンドル②クラス名③キャプション名ウインドウを取得するのによく使うWindowsAPI関数①Se [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-16" checked><label class="toc-title" for="toc-checkbox-16">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">WindowsAPIを使用すると色々な手順でウインドウが取得できる</a></li><li><a href="#toc2" tabindex="0">ウインドウを取得するのに必要なプロパティ</a><ol><li><a href="#toc3" tabindex="0">①ウインドウハンドル</a></li><li><a href="#toc4" tabindex="0">②クラス名</a></li><li><a href="#toc5" tabindex="0">③キャプション名</a></li></ol></li><li><a href="#toc6" tabindex="0">ウインドウを取得するのによく使うWindowsAPI関数</a><ol><li><a href="#toc7" tabindex="0">①SetForegroundWindow</a></li><li><a href="#toc8" tabindex="0">②FindWindow</a></li><li><a href="#toc9" tabindex="0">③GetWindowText</a></li><li><a href="#toc10" tabindex="0">④GetClassName</a></li><li><a href="#toc11" tabindex="0">⑤IsWindowVisible</a></li><li><a href="#toc12" tabindex="0">⑥GetNextWindow</a></li></ol></li></ol>
    </div>
  </div>

<h2><span id="toc1">WindowsAPIを使用すると色々な手順でウインドウが取得できる</span></h2>
<p><a href="https://officevba.info/appactivate/" target="_blank">前回</a>はAppActivateメソッドを用いて、操作対象のウインドウをアクティブにするVBAコードを紹介しました。</p>
<p>この方法はコードが簡単で記述しやすいのですが、キャプション名というウインドウに表示されている名称を正確に記載しなければエラーになる難点があり、キャプション名が不明な場合操作できない問題点があります。</p>
<p>WindowsAPIの関数を使用するとキャプション名がわからない場合やキャプション名の一部しかわからない場合でもウインドウを取得できるようになります。</p>
<p>今回はウインドウを取得するのによく使われるプロパティとWindowsAPIをいくつか紹介します。</p>
<h2><span id="toc2">ウインドウを取得するのに必要なプロパティ</span></h2>
<p>ウインドウには「ウインドウハンドル」「クラス名」「キャプション名」のプロパティがあり、これらのうち最低1つか、場合によっては2つの値を把握することでウインドウを取得できます。</p>
<p>キャプション名を把握している場合は以前にご紹介したAppActivateでウインドウを取得できます。</p>
<p>その他のケースにおいてはどの情報を取得しているかで使用するWindowsAPIの関数が変わってきます。</p>
<h3><span id="toc3">①ウインドウハンドル</span></h3>
<p>ウインドウごとに割り振られる固有の数値で、WindowsAPIを使用してのウインドウの取得に使う基本の値となります。</p>
<p>WindowsAPIではウインドウハンドルを取得して様々な処理行いますが、このウインドウハンドルはウインドウが開かれるたびに新しい値が割り振られるため、毎回値を取得しなおす必要があります。</p>
<p>ウインドウハンドルを取得している場合、「SetForegroundWindow」関数を用いて対象とするウインドウを最前面に表示する（=アクティブにする）ことができます。</p>
<h3><span id="toc4">②クラス名</span></h3>
<p>アプリケーションごとに異なる文字列を表します。<br />
エクスプローラーなら「CabinetWClass」、Excelなら「XLMAIN」となります。</p>
<p>同じアプリケーションのウインドウが1つだけの場合、クラス名を知っていれば「FindWindow」関数を使用してウインドウハンドルを取得することができます。</p>
<p>ウインドウハンドルを取得した後は①の通り「SetForegroundWindow」関数を使ってウインドウを最前面に表示することができます。</p>
<p>同じクラス名で複数のウインドウが起動されている場合はキャプション名が必要になります。<br />
キャプション名が一部しかわからない場合は全部のウインドウハンドルとキャプションを取得し、該当するウインドウハンドルを選ぶ必要があります。</p>
<h3><span id="toc5">③キャプション名</span></h3>
<p>キャプション名はほとんどの場合、アプリケーションウインドウの上部に表示される文字列です。<br />
ほぼ固有の名称ですが、同じアプリで同じファイルを複数開いた場合などまれに重複することがあります。</p>
<p>アプリケーションウインドウの上部に表示されている場合はそのままでOK、表示がなかったり、スペースが半角か全角かわからないなどの場合は全部のウインドウハンドルとキャプション名を取得してその中から該当するウインドウハンドルを決定します。</p>
<h2><span id="toc6">ウインドウを取得するのによく使うWindowsAPI関数</span></h2>
<p>ウインドウを取得するのによく使うWindowsAPIの関数は以下のようなものがあります。<br />
それぞれの使い方を記載します。</p>
<h3><span id="toc7">①SetForegroundWindow</span></h3>
<p>引数に指定したハンドルのウインドウを最前面に表示するAPIです。<br />
引数がウインドウハンドル一つで使い方がわかりやすいAPIです。</p>
<p>構文：SetForegroundWindow (ウインドウハンドル)<br />
宣言部分：Declare Sub SetForegroundWindow Lib &#8220;user32&#8221; (ByVal hWnd As Long)</p>
<div class="VBACode">Declare Sub SetForegroundWindow Lib &#8220;user32&#8221; (ByVal hWnd As Long)<br />
<br />
Sub ウインドウハンドルで取得したウインドウを最前面にする関数()<br />
<br />
<span class="VBA_Tab1">SetForegroundWindow 263636</span><br />
<br />
End Sub</div>
<p>上記は私の現在の環境でのウインドウハンドルを記入してウインドウを取得しています。</p>
<p>ウインドウハンドルの数字がそのままわかることはあまりないので、通常はSetForegroundWindowを使用する前にウインドウハンドルを取得するステップを行っていると思います。</p>
<h3><span id="toc8">②FindWindow</span></h3>
<p>ウインドウハンドルを取得するAPI関数です。</p>
<p>①のSetForegroundWindowとは異なり関数としての戻り値があり、ウインドウハンドルが返されます。</p>
<p>構文：ウインドウハンドル = FindWindow(&#8220;クラス名&#8221;, &#8220;キャプション名&#8221;)<br />
宣言部分：Declare Function FindWindow Lib &#8220;user32&#8221; Alias &#8220;FindWindowA&#8221; (ByVal lpClassName As String, ByVal lpWindowName As String) As Long</p>
<p>クラス名・キャプション名が不明な場合、「VbNullString」と記入すると複数あるウインドウのうち、ウインドウハンドルの値が最も小さいものを取得するようです。</p>
<p>クラス名・キャプション名をどちらも「VbNullString」にすると一番最初のウインドウを取得することができます。</p>
<h3><span id="toc9">③GetWindowText</span></h3>
<p>ウインドウハンドルからキャプション名を取得するAPI関数です。</p>
<p>構文：GetWindowText ウインドウハンドル, キャプション名を格納する変数, Len(キャプション名を格納する変数)<br />
宣言部分：Declare Function GetClassName Lib &#8220;user32&#8221; Alias &#8220;GetClassNameA&#8221; (ByVal hWnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long</p>
<p>下記の流れでキャプション名を表示させることができます。</p>
<div class="VBACode">Declare Function GetWindowText Lib &#8220;user32&#8221; _<br />
<span class="VBA_Tab1">Alias &#8220;GetWindowTextA&#8221; _</span><br />
<span class="VBA_Tab1">(ByVal hWnd As Long, ByVal lpString As String, _</span><br />
<span class="VBA_Tab1">ByVal cch As Long) As Long</span><br />
<br />
Sub ウインドウハンドルからキャプション名を取得する()<br />
<br />
<span class="VBA_Tab1">Dim strCaption As String * 80 <span class="VBA_Comment">’最大文字数80の文字列型変数 関数で取得した加工前のキャプション名を格納する</span></span><br />
<span class="VBA_Tab1">GetWindowText 263636, strCaption, Len(strCaption)</span><br />
<span class="VBA_Tab1"></span><br />
<span class="VBA_Tab1">Dim キャプション名 <span class="VBA_Comment">’加工後のキャプション名</span></span><br />
<span class="VBA_Tab1">キャプション名 = Left(strCaption, InStr(strCaption, vbNullChar) &#8211; 1)</span><br />
<br />
<span class="VBA_Tab1">MsgBox キャプション名</span><br />
<br />
End Sub</div>
<p>少し不思議な感じがするのですが、<br />
GetWindowText 263636, strCaption, Len(strCaption)<br />
と記入するとウインドウハンドル「263636」のウインドウのタイトルがstrCaptionという変数に格納されます。</p>
<p>この関数自体の戻り値をrc= GetWindowText 263636, strCaption, Len(strCaption) のように設定してrcがキャプション名になるわけではないので注意が必要です。</p>
<p>私はこのイメージがなかなかつかめなくてなかなか使いこなせませんでした。</p>
<p>また、GetWindowTextで取得したキャプション名には文字がなくなった後にNull値が含まれているらしく、そのNull値を除くためにLeft関数でNull値が出てくる直前までの文字数を抜き出す必要があります。</p>
<h3><span id="toc10">④GetClassName</span></h3>
<p>ウインドウハンドルからクラス名を取得するAPIです。<br />
使い方はキャプション名を取得するGetWindowTextとほぼ同じです。</p>
<p>構文：GetClassName ウインドウハンドル, クラス名を格納する変数, Len(クラス名を格納する変数)<br />
宣言部分：Declare Function GetClassName Lib &#8220;user32&#8221; Alias &#8220;GetClassNameA&#8221; (ByVal hWnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long</p>
<div class="VBACode">Declare Function GetClassName Lib &#8220;user32&#8221; _<br />
<span class="VBA_Tab1">Alias &#8220;GetClassNameA&#8221; _</span><br />
<span class="VBA_Tab1">(ByVal hWnd As Long, _</span><br />
<span class="VBA_Tab1">ByVal lpClassName As String, _</span><br />
<span class="VBA_Tab1">ByVal nMaxCount As Long) As Long</span><br />
<br />
Sub ウインドウハンドルからクラス名を取得する()<br />
<br />
<span class="VBA_Tab1">Dim strClassName As String * 80 <span class="VBA_Comment">’最大文字数80の文字列型変数 関数で取得した加工前のクラス名を格納する</span></span><br />
<span class="VBA_Tab1">GetClassName 263636, strClassName, Len(strClassName)</span><br />
<br />
<span class="VBA_Tab1">Dim クラス名 <span class="VBA_Comment">’加工後のクラス名</span></span><br />
<span class="VBA_Tab1">クラス名 = Left(strClassName, InStr(strClassName, vbNullChar) &#8211; 1)</span><br />
<br />
<span class="VBA_Tab1">MsgBox クラス名</span><br />
<br />
End Sub</div>
<h3><span id="toc11">⑤IsWindowVisible</span></h3>
<p>引数に指定したウインドウハンドルを持つウインドウが可視かどうか判定します。<br />
可視の場合「1」、不可視の（見えない）場合「0」を返します。</p>
<p>これはC言語のTRUE、FALSEに相当する値ですが、VBAとは正の場合の値が異なります。<br />
VBAでのTrueは「-1」なので「If～End If」を使用した条件分岐ではTrueを使ってもきちんと判定できません。</p>
<p>C言語、VBAで共通の値である「False（0）」を用いて判定するようにしてください。</p>
<p>構文：IsWindowVisible(ウインドウハンドル)<br />
宣言部分：Declare Function IsWindowVisible Lib &#8220;user32&#8221; (ByVal hWnd As Long) As Long</p>
<div class="VBACode">Declare Function IsWindowVisible Lib &#8220;user32&#8221; (ByVal hWnd As Long) As Long<br />
<br />
Sub ウインドウが可視か確認する()<br />
<br />
<span class="VBA_Tab1">If IsWindowVisible(263636) <> False Then</span><br />
<span class="VBA_Tab2">MsgBox &#8220;可視&#8221;</span><br />
<span class="VBA_Tab1">Else</span><br />
<span class="VBA_Tab2">MsgBox &#8220;不可視&#8221;</span><br />
<span class="VBA_Tab1">End If</span><br />
<br />
End Sub</div>
<p>「If IsWindowVisible(263636) <> False Then」の部分は「If IsWindowVisible(263636) Then」とすることでも判定可能です。<br />
慣れてくればこちらの使い方の方が本来の使い方の気がします。</p>
<p>この関数も①SetForegroundWindowと同じくウインドウハンドルが必要なので、他のAPIなどでウインドウハンドルを取得した上で使用したり、他のAPIを用いて、開いているウインドウの情報をすべて取得する場合などに使用するもので、このAPI関数単独で使用する機会はほぼないと思います。</p>
<h3><span id="toc12">⑥GetNextWindow</span></h3>
<p>構文①：ウインドウハンドル = GetNextWindow(ウインドウハンドル, GW_HWNDNEXT)<br />
構文②：ウインドウハンドル = GetNextWindow(ウインドウハンドル, GW_HWNDLAST)<br />
宣言部分：Declare Function GetNextWindow Lib &#8220;user32&#8221; Alias &#8220;GetWindow&#8221; (ByVal hWnd As Long, ByVal wFlag As Long) As Long<br />
Const GW_HWNDLAST = 1<br />
Const GW_HWNDNEXT = 2</p>
<p>指定したウインドウハンドルの次のウインドウハンドルを取得します。</p>
<p>Do～loopを用いて開いているウインドウのウインドウハンドルをすべて取得する場合などに重宝するAPIです。</p>
<p>定数部分は宣言せずに第二引数に数字を入力しても動作しますが、数字だけだとわかりにくくなるので宣言しておく方がおすすめです。<br />
最初のウインドウを指定することもできますが、最初の指定はウインドウハンドルを必要としない「FindWindow(vbNullString, vbNullString)」の方が便利です。</p>
<p>今回紹介したウインドウ取得に関するAPIを使用して実際にウインドウハンドルを取得したり、ウインドウを最前面に移動する方法は<a href="https://officevba.info/example-findwindow/" target="_blank">こちらの記事</a>で紹介します。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/windowsapi-vba/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ウインドウを取得するVBAコード</title>
		<link>https://officevba.info/appactivate/</link>
					<comments>https://officevba.info/appactivate/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Wed, 29 Aug 2018 13:37:56 +0000</pubDate>
				<category><![CDATA[ウインドウの取得]]></category>
		<guid isPermaLink="false">http://officevba.info/?p=1511</guid>

					<description><![CDATA[目次 ウインドウの取得は操作の基本VBAでのウインドウの取得ウインドウタイトル（キャプション名）がわからない場合や空白の全角半角が難しい場合 ウインドウの取得は操作の基本 色々なアプリをマクロやVBAで操作する場合、対象 [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-18" checked><label class="toc-title" for="toc-checkbox-18">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">ウインドウの取得は操作の基本</a></li><li><a href="#toc2" tabindex="0">VBAでのウインドウの取得</a></li><li><a href="#toc3" tabindex="0">ウインドウタイトル（キャプション名）がわからない場合や空白の全角半角が難しい場合</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">ウインドウの取得は操作の基本</span></h2>
<p>色々なアプリをマクロやVBAで操作する場合、対象となるウインドウ（=画面）をアクティブ取得して最前面に持ってくることが第一歩となります。</p>
<p>今回はデスクトップ上に開いている画面を取得するVBAコードを紹介します。</p>
<h2><span id="toc2">VBAでのウインドウの取得</span></h2>
<p>VBAでのウインドウの取得に関してはウインドウタイトル（キャプション名）を事前に把握しておく必要があります。<br />
ウインドウタイトル（キャプション名）を使用して下記のようなコードを入力することでウインドウを取得することができます。</p>
<div class="VBACode">AppActivate &#8220;編集前&#8221;, (True,False)</div>
<p>この例ではエクスプローラーで開かれている「編集前」というフォルダのウインドウを最前面に表示します。</p>
<p>第二引数のTrueかFalseはフォーカスがVBAもしくはExcelの画面に戻るまで実行するのを待つかどうかで、通常Falseを使用するのが一般的だと思います。<br />
1回だけしかウインドウの取得をしなければ別ですが、2回以上ウインドウを移動する場合、いちいち移動の度にVBAやExcelなどの画面にフォーカスを戻すのは現実的ではないと思います。</p>
<p>この第二引数は省略可能で、省略するとFalseになりますので、普段はTrueにするかFalseにするかは特に意識せず何も記載しなくて大丈夫です。</p>
<h2><span id="toc3">ウインドウタイトル（キャプション名）がわからない場合や空白の全角半角が難しい場合</span></h2>
<p>ウインドウタイトル（キャプション名）は下記のようなパターンで決められているようです。</p>
<li>①ファイル名</li>
<li>②アプリケーション名 &#8211; ファイル名</li>
<li>③ファイル名 – アプリケーション名</li>
<p>ExcelやVBAは②に該当し、Chromeはウインドウタイトル（キャプション名）が表示されませんが③に該当しているようです。</p>
<p>ウインドウタイトル（キャプション名）が表示されないアプリケーションの場合、ウインドウの取得するにはまずこれらのパターンを試してみるとよいかもしれません。</p>
<p>②と③のケースでは空白は半角が一般的ですが、もしこれらを試しても取得できない場合はWindowsAPIを使用してウインドウ名を取得する方法があります。</p>
<p>また、その他にウインドウハンドルと言われるウインドウごとの固有値を取得して特定のウインドウを最前面にする方法もあります。<br />
これらの手順はWindowsAPIの使い方の記事で紹介します。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/appactivate/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>画面上の任意の位置をクリックするVBAコード</title>
		<link>https://officevba.info/mouse_event/</link>
					<comments>https://officevba.info/mouse_event/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Mon, 16 Jul 2018 08:59:47 +0000</pubDate>
				<category><![CDATA[マウスイベント]]></category>
		<guid isPermaLink="false">http://officevba.info/?p=1476</guid>

					<description><![CDATA[目次 色々なアプリ、システムの操作マウスイベントのVBAでの実行はほぼ定型文を記載 色々なアプリ、システムの操作 コマンドプロンプトやその他の方法でアプリやシステムを操作できると手順を自動化するのに役立ちますが、この方法 [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-20" checked><label class="toc-title" for="toc-checkbox-20">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">色々なアプリ、システムの操作</a></li><li><a href="#toc2" tabindex="0">マウスイベントのVBAでの実行はほぼ定型文を記載</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">色々なアプリ、システムの操作</span></h2>
<p>コマンドプロンプトやその他の方法でアプリやシステムを操作できると手順を自動化するのに役立ちますが、この方法での操作ができないアプリやシステムも多いです。</p>
<p>システム上で直接操作できないアプリ・システムを操作したい場合、簡易的な方法としてキーボードやマウス操作の手順を実行するキーボードイベントやマウスイベントがあります。</p>
<p>今回はマウスの操作手順を実行させるマウスイベントについてご紹介します。<br />
キーボードの操作を実行させるキーボードイベントについては<a href="https://officevba.info/keybdevent/" target="_blank">こちら</a>をご確認ください。</p>
<h2><span id="toc2">マウスイベントのVBAでの実行はほぼ定型文を記載</span></h2>
<p>マウスの操作を行うにはWindowsAPIのライブラリから「SetCursorPos」「mouse_event」を引用します。</p>
<p>記載方法に関してはすべて把握しておかなくても定型文（おまじない）みたいな感じでコピペするだけでOKです。<br />
下記にサンプルコードを紹介します。</p>
<div class="VBACode">Declare Sub mouse_event Lib &#8220;user32&#8221; ( _<br />
<span class="VBA_Tab1">ByVal dwFlags As Long, _</span><br />
<span class="VBA_Tab1">Optional ByVal dx As Long, _</span><br />
<span class="VBA_Tab1">Optional ByVal dy As Long, _</span><br />
<span class="VBA_Tab1">Optional ByVal dwDate As Long, _</span><br />
<span class="VBA_Tab1">Optional ByVal dwExtraInfo As Long)</span><br />
<br />
Declare Function SetCursorPos Lib &#8220;user32&#8221; (ByVal x As Long, ByVal y As Long) As Long<br />
<br />
Const MOUSE_LEFTDOWN = &#038;H2<span class="VBA_Comment">’10進数の2</span></span><br />
Const MOUSE_LEFTUP = &#038;H4<span class="VBA_Comment">’10進数の4</span></span><br />
<br />
Const MOUSE_RIGHTDOWN = &#038;H8<span class="VBA_Comment">’10進数の8</span></span><br />
Const MOUSE_RIGHTUP = &#038;H10<span class="VBA_Comment">’10進数の16</span></span><br />
<br />
Sub マウスで画面の任意の位置をクリック()<br />
<br />
<span class="VBA_Tab1">SetCursorPos 50, 100<span class="VBA_Comment">’左から50ピクセル、上から100ピクセルの位置にカーソルを移動</span></span><br />
<span class="VBA_Tab1">mouse_event MOUSE_LEFTDOWN, 0, 0, 0, 0<span class="VBA_Comment">’左クリックを押す</span></span><br />
<span class="VBA_Tab1">mouse_event MOUSE_LEFTUP, 0, 0, 0, 0<span class="VBA_Comment">’左クリックを離す</span></span><br />
<br />
End Sub<br />
<br />
Sub マウスで画面の任意の位置を右クリック()<br />
<br />
<span class="VBA_Tab1">SetCursorPos 50, 100<span class="VBA_Comment">’左から50ピクセル、上から100ピクセルの位置にカーソルを移動</span></span><br />
<span class="VBA_Tab1">mouse_event MOUSE_RIGHTDOWN, 0, 0, 0, 0<span class="VBA_Comment">’右クリックを押す</span></span><br />
<span class="VBA_Tab1">mouse_event MOUSE_RIGHTUP, 0, 0, 0, 0<span class="VBA_Comment">’右クリックを離す</span></span><br />
<br />
End Sub</div>
<p>mouse_eventにおいてマウス左クリックを押す命令が「&#038;H2（10進数の2）」、左クリックを離す命令が「&#038;H4（10進数の4）」となります。<br />
また右クリックは同様に「&#038;H8（10進数の8）」「&#038;H10（10進数の16）」が割り振られています。<br />
それぞれを覚えておけば特に定数を宣言する必要がありませんが、わかりにくくなるために宣言して使用しています。</p>
<p>あと、16進数を使うのが一般的なようですが、10進数でも問題なく動作するようです。</p>
<p>おまじないの中で「Optional ByVal dx As Long」の部分を「Optional ByVal dx As Long = 0」としておくとmouse_eventを呼び出したときに「0」を記載する必要がなくなります。</p>
<p>「dy」「dwDate」「dwExtraInfo」も同様で、先に宣言しておくと「0,0,0,0」を省略できますので、たくさんマウスイベントを使用する際は参考にしてみてください。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/mouse_event/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
