<?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>ExcelVBA | VBA・GAS・Pythonで仕事を楽しく効率化</title>
	<atom:link href="https://officevba.info/category/excelvba/feed/" rel="self" type="application/rss+xml" />
	<link>https://officevba.info</link>
	<description>仕事の役に立つVBA・GAS・Pythonのコードを紹介していきます。</description>
	<lastBuildDate>Sat, 31 Jan 2026 12:18:58 +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>ExcelVBA | VBA・GAS・Pythonで仕事を楽しく効率化</title>
	<link>https://officevba.info</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>VBAの実行前に作成したマクロを別ファイルで保存</title>
		<link>https://officevba.info/savecopyas/</link>
					<comments>https://officevba.info/savecopyas/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Tue, 26 Nov 2024 11:44:42 +0000</pubDate>
				<category><![CDATA[ブックの操作]]></category>
		<category><![CDATA[FSO(FileSystemObject)]]></category>
		<category><![CDATA[SaveCopyAs]]></category>
		<category><![CDATA[バックアップ]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2541</guid>

					<description><![CDATA[目次 保存する前に動作させたVBAがフリーズ通常のファイルの保存での問題点を解決するSaveCopyAsメソッド実行前にマクロファイルをバックアップとして保存するコードの紹介 保存する前に動作させた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">保存する前に動作させたVBAがフリーズ</a></li><li><a href="#toc2" tabindex="0">通常のファイルの保存での問題点を解決するSaveCopyAsメソッド</a></li><li><a href="#toc3" tabindex="0">実行前にマクロファイルをバックアップとして保存するコードの紹介</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">保存する前に動作させたVBAがフリーズ</span></h2>
<p>保存するのを忘れてVBAコードを実行した結果、Excelがフリーズしてもう一度コードを書きなおすのは多分あるあるだと思うのですが、この間も懲りずにやらかしました。</p>
<p>そこでコード実行時にバックアップを作成するコードを考えてみましたのでご紹介します。</p>
<h2><span id="toc2">通常のファイルの保存での問題点を解決するSaveCopyAsメソッド</span></h2>
<p>ファイルをバックアップ用に保存することを考えた場合、通常の上書き保存（ThisWorkbook.Save）だと「保存前の状況に戻したい」場合に困ることがあります。</p>
<p>また、「名前を付けて保存（ThisWorkbook.SaveAs ○○）」だとコピーの動作後に名前をつけて保存したファイルが現在開いている状態になり、元のファイルは保存前の状態で閉じられた扱いになってしまいます。</p>
<p>理想的には①現在のファイルは保存前の状態で維持しつつ、②作成したツールをバックアップとして保存することで、この挙動をかなえてくれるのがSaveCopyAsメソッドです。<br />
（FileSystemObjectのCopyFileだと以前の保存状態のものをバックアップに保存、今のファイルを新しく保存する運用でSaveCopyAsメソッドとはバックアップファイルと現在のファイルが逆になります。今回はSaveCopyAsメソッドの挙動が想定していたものでしたのでSaveCopyAsメソッドを利用します。）</p>
<h2><span id="toc3">実行前にマクロファイルをバックアップとして保存するコードの紹介</span></h2>
<p>以下のコードをツールに組み込んでおいて、作成したコードの先頭に「Call 実行前にVBAを含むツールファイルを自動保存」の1行を入力しておくことでツールファイルが格納されているフォルダに「backUp」フォルダが作成され、ツールファイルのバックアップが保存されます。</p>
<p>元になるツールファイル自体は保存前の状態、バックアップファイルはツールの動作時の状態になりますので、フリーズしてしまったり、無限ループに陥って強制終了した場合などは「backUp」フォルダ内の直近で作成されたファイルを利用すればすぐに復旧可能です。</p>
<div class="VBACode">
<pre>
Sub 実行前にVBAを含むツールファイルを自動保存()

    Dim fso As Object: Set fso = CreateObject("Scripting.FileSystemObject")
    '参照設定「Microsoft Scripting Runtime」を設定している場合
    'Dim fso As FileSystemObject: Set fso = New FileSystemObject
    
    If Not fso.folderExists(ThisWorkbook.Path &#038; "\backUp") Then
        fso.createFolder (ThisWorkbook.Path &#038; "\backUp")
    End If

    ThisWorkbook.SaveCopyAs (ThisWorkbook.Path &#038; "\backUp\" &#038; Left(ThisWorkbook.Name, Len(ThisWorkbook.Name) - 4) &#038; Format(Now, "yyyymmdd_hhmmss") &#038; ".xlsm")

End Sub
</pre>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/savecopyas/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>コピペを多用して動作が重くなってしまったブックをVBAで軽量化した事例</title>
		<link>https://officevba.info/misbehavingtextbox/</link>
					<comments>https://officevba.info/misbehavingtextbox/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 08 Sep 2024 01:26:43 +0000</pubDate>
				<category><![CDATA[シートの操作]]></category>
		<category><![CDATA[オブジェクト]]></category>
		<category><![CDATA[テキストボックス]]></category>
		<category><![CDATA[削除]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2511</guid>

					<description><![CDATA[目次 同僚からの動作の重いブックについて相談セルの結合があるので動作が重い？原因の調査と特定解決方法についてその他の改善点そもそものフォーマットの問題点と改善方法について 同僚からの動作の重いブックについて相談 私の勤務 [&#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">同僚からの動作の重いブックについて相談</a></li><li><a href="#toc2" tabindex="0">セルの結合があるので動作が重い？</a></li><li><a href="#toc3" tabindex="0">原因の調査と特定</a></li><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>
    </div>
  </div>

<h2><span id="toc1">同僚からの動作の重いブックについて相談</span></h2>
<p>私の勤務先の同僚から「原因がよくわからないが、処理が重くなってしまったファイルを改良して使いやすくしたい」と相談を受けました。<br />
その際ちょっと変わった状況になっていたので、備忘録と情報共有の目的で記事にしておきます。</p>
<h2><span id="toc2">セルの結合があるので動作が重い？</span></h2>
<p>同僚が言うには、セルの結合が入っているので処理が重くなっていて、また修正に手間がかかるのが困るとのことでブックを確認しました。<br />
ざっくりとした内容ですが、1つのブックの中にシートは一つで、その中には3種類の研修について1日のスケジュールが書かれた表になっていました。</p>
<p><a href="https://officevba.info/wp-content/uploads/2024/09/VBA174-1.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/2024/09/VBA174-1-88x300.jpg" alt="" width="88" height="300" class="alignnone size-medium wp-image-2513" srcset="https://officevba.info/wp-content/uploads/2024/09/VBA174-1-88x300.jpg 88w, https://officevba.info/wp-content/uploads/2024/09/VBA174-1.jpg 347w" sizes="(max-width: 88px) 100vw, 88px" /></a></p>
<p>5分刻みでスケジュールを調整したいらしく、コマ数に応じて行数を変更してセルを結合する書式で、確かにセルの結合は複数使われていました。<br />
ただ感覚的な判断ですが、100行15列くらいの使用でセル結合を多用しただけで動作が重くなるのは考えにくい気がしました。</p>
<p>以前に新しいブックにシートの全セルをコピーしてそのままペーストすると動作が軽くなったことがあるとも同僚が言っていたこともセル結合が影響していないのではないかと思うきっかけでした。</p>
<h2><span id="toc3">原因の調査と特定</span></h2>
<p>とりあえず、変なところに情報が残っていてそれが悪さをしている可能性を考慮して使用していない行列をいったん削除してみても動作は変わりませんでした。<br />
また、他のブックへのリンクも多少含まれていたのでリンクを切断してみたものの動作の重さは変わらなかったです。</p>
<p>次に同僚が以前に多少解消できたと話していた通りコピペで新しいシートに貼り付けようとしたら、PCがフリーズして動かなくなりました。<br />
使用しているセルを選択してコピーしたので、フリーズする原因は使用している範囲（Range）上にあるということがわかりました。</p>
<p>そこで、試しにセルに含まれているオブジェクトを確認してみたところ無茶苦茶な数のテキストボックスが含まれていました。<br />
後でVBAで数を確認したところ28,000個くらい配置されていましたw</p>
<p><a href="https://officevba.info/wp-content/uploads/2024/09/VBA174-2.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/2024/09/VBA174-2-99x300.jpg" alt="" width="99" height="300" class="alignnone size-medium wp-image-2514" srcset="https://officevba.info/wp-content/uploads/2024/09/VBA174-2-99x300.jpg 99w, https://officevba.info/wp-content/uploads/2024/09/VBA174-2.jpg 315w" sizes="(max-width: 99px) 100vw, 99px" /></a></p>
<p>誰かが何かのタイミングで思いがけず背景色・枠線なしのテキストボックスを作成し、行か列のコピーの際にそのテキストボックスも含めたコピーを繰り返した結果<br />
ゾンビのように増えたテキストボックスが動作を重くしていたようです。</p>
<h2><span id="toc4">解決方法について</span></h2>
<p>背景色・枠線なしの見えないテキストボックスが悪さをしているのは確認できましたが、選択するだけでフリーズしてしまう状況で、手作業での良い解決方法が思いつかず、VBAを使用してテキストボックスをすべて削除して解決を図りました。</p>
<p>使用したコードは以下の通りです。</p>
<div class="VBACode">
<pre>
Sub シート上のオブジェクトすべて削除()
    
    Dim x As Long: x = ActiveSheet.Shapes.Count
    Dim i As Long: 
    For i = x To 1 Step -1
        ActiveSheet.Shapes(i).Delete
    Next i

End Sub
</pre>
</div>
<p>こちらのコードを実行した結果、無事にテキストボックスはすべて削除され、サクサク動作するようになりました。<br />
テキストボックスなどのオブジェクトを見えない状態で作成した際、気づかずに増殖して動作に悪影響を及ぼすことがあるのは新しい発見でした。<br />
共有するファイルの扱いには気を付けようと思いました。</p>
<h2><span id="toc5">その他の改善点</span></h2>
<p>今回動作が重い原因は隠れたテキストボックスだったため不要なテキストボックスを削除することで動作は軽くなりました。<br />
ただ、コマ数に応じてセルの結合を変更するのはかなり手間そうでした。</p>
<p>そこで以前<a href="https://officevba.info/mergecells/" target="_blank">こちら</a>の記事で紹介したセルの結合を切り替えるVBAコードを組み込んでショートカットを割り当て、スムーズにコマの入力と時間の変更ができるようにして提案しました。</p>
<div class="VBACode">
<pre>
Sub セル結合とセル解除切替()
    
    If Selection.MergeCells = True Then
        Selection.MergeCells = False
    Else
        Selection.MergeCells = True
    End If

End Sub
</pre>
</div>
<h2><span id="toc6">そもそものフォーマットの問題点と改善方法について</span></h2>
<p>今回のワークシートの使い方については「データの格納場所」と「配布・閲覧資料」を同時に1つのシートで完結させようとしている点が問題だと思います。<br />
またセルの結合自体がそもそもその後の加工に向かないので使わないほうが良いという話もあります。<br />
あるべき使い方としてはデータの格納場所としてテーブルの形式で構造化されたデータを用意し、それとは別に閲覧用シートに出力する形をとるのが望ましいと思います。</p>
<p>ただし、あるべき形でExcelファイルを運用しようとすると、フォーマットの作り方においてほかのセルの値を呼び出したりする知識が必要だったり、構造化データを格納する意識が必要になったり、難易度が上がります。</p>
<p>Excelは手軽に利用できる反面、使用する多くの方が関数などのExcelを便利に使う知識がないケースも多いです。<br />
個人的にはどこまでデータ管理の知識を落とし込めるかは使用者（達）次第になるので今回の運用も致し方ないと思っています。</p>
<p>要するにデータと配布資料が1か所に集まっていて構造化データになっていないし、セルの結合を使用していて編集に時間がかかるフォーマットだったとしてもVBA<br />
を用いてセルの結合などを効率化することで、編集を簡略化してしまえば実際の運用上で問題になることはないと考えています。<br />
特に今回使用したコードはすごく簡単なのでメンテナンスも不要ですし、扱いが楽なのも問題ないと思う理由です。</p>
<p>逆に構造化データの概念とかをいきなり説明してフォーマットを強引に変更したり、運用を強制したりした方が使用者（達）の反感を買ってしまったり、なじめなかったりして実業務に悪影響になることも多い気がします。<br />
（効率的な業務を推進したい身としては悩ましいですが。）</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/misbehavingtextbox/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>MicrosoftEdgeのIEモードをVBAで操作する</title>
		<link>https://officevba.info/edgeiemodeoperation/</link>
					<comments>https://officevba.info/edgeiemodeoperation/#comments</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Mon, 28 Mar 2022 14:55:26 +0000</pubDate>
				<category><![CDATA[ブラウザ操作]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[Edge]]></category>
		<category><![CDATA[getElement]]></category>
		<category><![CDATA[IEモード]]></category>
		<category><![CDATA[VBA]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2370</guid>

					<description><![CDATA[目次 InternetExplorerが2022年6月13日以降使用できなくなる？VBAでIEを操作する方法の代替手段一般的なブラウザ操作の自動化手段について①SeleniumBasicを使う②PowerAutomate [&#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">InternetExplorerが2022年6月13日以降使用できなくなる？</a></li><li><a href="#toc2" tabindex="0">VBAでIEを操作する方法の代替手段</a><ol><li><a href="#toc3" tabindex="0">一般的なブラウザ操作の自動化手段について</a></li><li><a href="#toc4" tabindex="0">①SeleniumBasicを使う</a></li><li><a href="#toc5" tabindex="0">②PowerAutomateDeskTopなどのRPAを使用する</a></li></ol></li><li><a href="#toc6" tabindex="0">満を持してのEdgeを操作するVBAコードの登場</a></li><li><a href="#toc7" tabindex="0">MicrosoftEdgeのIEモードの操作サンプルコード</a><ol><li><a href="#toc8" tabindex="0">宣言部分</a></li><li><a href="#toc9" tabindex="0">実行部分</a></li></ol></li></ol>
    </div>
  </div>

<h2><span id="toc1">InternetExplorerが2022年6月13日以降使用できなくなる？</span></h2>
<p>先日ネットの記事でInternetExplorerが使えなくなるとの話がありました。<br />
どうやらサポート終了日以降はIEを起動しても強制的にMicrosoftEdgeにリダイレクトされるとのことでした。</p>
<p>以前からIEはセキュリティ上の問題が指摘されていていずれ使えなくなるという話はありましたが、サポート終了日以降も起動くらいは問題ないと思っていたので、この対応にかなり焦りました。</p>
<p>手作業の業務についてはMicrosoftEdgeのIEモードを使用すればほぼほぼ問題ないのですが、VBAを使用したDOM（DocumentObjectModel）を取得する方法が一切使えなくなるのがネックでした。</p>
<p>今回私が慌てて代替の方法を色々と検討した結果、先人が素晴らしいコードを作成してくれているのを見つけました。<br />
また、それを活用することで今までのIE操作で使用していたコードをほぼそのまま使用できることがわかりましたのでお困りの方にお伝えしたくコードと使い方をご紹介します。</p>
<h2><span id="toc2">VBAでIEを操作する方法の代替手段</span></h2>
<h3><span id="toc3">一般的なブラウザ操作の自動化手段について</span></h3>
<p>VBAでIEを操作して情報を取得したり、ブラウザ上の操作を自動化する方法についての代替手段はこれまで以下のものが挙げられていました。<br />
ただ後述するようにこれらの方法では色々な制約があり、これらの方法は試してみたものの私の行っている業務の代替手段としては使用できませんでした。</p>
<h3><span id="toc4">①SeleniumBasicを使う</span></h3>
<p>Pythonや他のプログラムなどでも使用されるSeleniumのVBA版を使用してブラウザの操作を自動化する方法です。</p>
<p>IEからDOMを取得する方法と少し挙動は異なりますが、どちらかというとIEからDOMを取得する方法よりSeleniumを使う方法の方が主流のため、ネット上で情報も多く手に入ります。</p>
<p>ウェブサイトのHTMLから特定の要素を簡単に特定できるCSSセレクタを使えて、Seleniumに慣れているエンジニアの方が多いからか、色々なサイトでSeleniumをおすすめされています。</p>
<p>しかし私のようにエンジニアではなく普段会社で事務仕事をしている場合Seleniumを使用する場合以下のような壁に当たります。</p>
<p><strong>1.VBAでDOMを取得してIEを操作する場合と異なり、ブラウザの起動から終了までをすべてプログラムで操作する必要がある。</strong></p>
<p>開いていたページをそのまま操作することができないので、二段階認証が必要なログインの処理などが間に入ると操作ができないケースがあります。</p>
<p><strong>2.Seleniumを使っていることがサーバー側に伝わるためにページの情報が取得できないことがある</strong></p>
<p>例えばGoogleのサインインした後の個人のページなどはSeleniumでアクセスしようとすると拒否され、操作ができないことがあります。</p>
<p><strong>3.私も詳細がわからないのですが、SeleniumとWebドライバでブラウザを開こうとすると拒否されるケースがありました。</strong></p>
<p>おそらく会社の社内ネットワークセキュリティのどこかに引っかかっていると思うのですが、原因は特定できておりません。<br />
とりあえず環境次第でSeleniumは使用できない場合があるようです。</p>
<p><strong>4.そもそもSeleniumBasicをインストールすることが禁止されている。</strong></p>
<p>会社からの貸与PCは自由にアプリケーションをインストールすることができない場合も多く、私もここが大きな問題となります。</p>
<p>また、そのほかVBAで使用するためのSeleniumBasicは少し前から更新されておらず、ドライバを手動で更新してから使用しないといけないなど今後の安定的な使用について若干の不安がありました。</p>
<h3><span id="toc5">②PowerAutomateDeskTopなどのRPAを使用する</span></h3>
<p>Windows標準のRPAであるPowerAutomateDeskTopやその他のRPAツールで操作を代替する紹介をされている場合も多いです。</p>
<p>ただ、専門的な操作をしようとすると有料のサービスを使用する必要があることや、IEからDOMを取得するVBAを書く場合と異なる操作が多く、習熟するのに少し時間がかかる印象でした。</p>
<h2><span id="toc6">満を持してのEdgeを操作するVBAコードの登場</span></h2>
<p>Seleniumは使えず、（作り慣れていないので）RPAを作るには時間がかかるうえに大掛かりな変更が必要になることから別の手段を検討していたところ、以下の2つの記事からEdgeでDOMを取得して操作する方法が紹介されていました。</p>
<p>〇<a rel="noopener" href="https://www.ka-net.org/blog/?p=6033" target="_blank">https://www.ka-net.org/blog/?p=6033</a></p>
<p>〇<a rel="noopener" href="https://social.msdn.microsoft.com/Forums/ja-JP/c0765a67-b8ba-40dc-ac52-aac7be9f1d6a/ie123981246912509125401248812364202224180626376152608512395320662?forum=vbajp" target="_blank">https://social.msdn.microsoft.com/Forums/ja-JP/c0765a67-b8ba-40dc-ac52-aac7be9f1d6a/ie123981246912509125401248812364202224180626376152608512395320662?forum=vbajp</a></p>
<p>上の初心者備忘録は専門的な知識を使ったコードをたくさん紹介してくださっているサイトで、私も普段よく参考にさせていただいています。</p>
<p>この記事は以前のChromiumベースになる前のEdge操作について書かれていますが、新しいEdgeについてもIEモードではClass「InternetExplorer_Server」が存在していて、基本的に同じ処理ができるそうです。</p>
<h2><span id="toc7">MicrosoftEdgeのIEモードの操作サンプルコード</span></h2>
<p>上記の2つの記事の情報を合わせて、かつ長くなるDOM取得部分を一個のコードで使いまわせるように以下の通りコードを書いてみました。</p>
<p>以下の「検索実行」のプロシージャを実行するとEdgeを開き、IEモードに切り替えた後、Googleで「VBA・GAS・Pythonで業務を楽しく効率化」を検索するという操作を自動化しています。</p>
<h3><span id="toc8">宣言部分</span></h3>
<div class="GASCode">
<pre>
Option Explicit

#If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal ms As LongPtr)
#Else
Private Declare Sub Sleep Lib "kernel32" (ByVal ms As Long)
#End If

Private Declare Function GetTopWindow Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Function GetParent Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, lpdwProcessId As Long) As Long
Private Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, lParam As Long) As Long
Private Const GW_HWNDNEXT = &#038;H2
Private Declare Function GetNextWindow Lib "user32" Alias "GetWindow" (ByVal hWnd As Long, ByVal wFlag As Long) As Long
Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hWnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Private Declare Function IIDFromString Lib "ole32" (lpsz As Any, lpiid As Any) As Long
Private Declare Function RegisterWindowMessage Lib "user32" Alias "RegisterWindowMessageA" (ByVal lpString As String) As Long
Private Const SMTO_ABORTIFHUNG = &#038;H2

Private Declare Function SendMessageTimeout Lib "user32" Alias "SendMessageTimeoutA" (ByVal hWnd As Long, ByVal msg As Long, _
                            ByVal wParam As Long, ByVal lParam As Long, ByVal fuFlags As Long, ByVal uTimeout As Long, lpdwResult As Long) As Long
Private Declare Function ObjectFromLresult Lib "oleacc" (ByVal lResult As Long, riid As Any, ByVal wParam As Long, ppvObject As Object) As Long
        
'オブジェクトの位置取得？
Private Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(0 To 7) As Byte
End Type

Private Declare PtrSafe Function IUnknown_QueryService Lib "shlwapi.dll" _
    (ByVal punk As IUnknown, guidService As GUID, riid As GUID, ppvOut As IAccessible) _
        As Long

Private hIES As Long
Private IeEdge As Object
Private hWnd As Long

'EdgeをIEモードに切り替える場合に使用する
'対象ウインドウを最前面にする
Private Declare Sub SetForegroundWindow Lib "user32" (ByVal hWnd As Long)

'キーボード操作
Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, _
                                                ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Private Const KEYEVENTF_EXTENDEDKEY = &#038;H1
Private Const KEYEVENTF_KEYUP = &#038;H2
Private Const fKEYDOWN = KEYEVENTF_EXTENDEDKEY
Private Const fKEYUP = KEYEVENTF_EXTENDEDKEY Or KEYEVENTF_KEYUP

Private Const VK_UP = &#038;H26
Private Const VK_Alt = &#038;H12
Private Const VK_F = &#038;H46
Private Const VK_RETURN = &#038;HD

Private Sub DOM取得()

    'Chromium版EdgeのIEモードをDOM操作(32ビット版Excel)
    
    Dim con As Object, items As Object
    Dim pId As Long
    Const ProcessName = "msedge.exe"

    Dim msg As Long
    Dim res As Long
    Dim IID_IHTMLDocument2 As GUID

    Dim Dom

    Set con = CreateObject("WbemScripting.SWbemLocator").ConnectServer
    hWnd = GetTopWindow(0)
        Do
            If GetParent(hWnd) = 0 Then
                'ウィンドウハンドルからプロセスIDを取得し、プロセス名を使用してEdgeのウィンドウかどうかを判別する
                GetWindowThreadProcessId hWnd, pId
                Set items = con.ExecQuery("Select * From Win32_Process Where (ProcessId = '" &#038; pId &#038; "') And (Name = '" &#038; ProcessName &#038; "')")
                If items.Count > 0 Then
                    'Edgeの子ウィンドウ列挙
                    EnumChildWindows hWnd, AddressOf EnumChildProcIES, 0 'Internet Explorer ServerのhWndをhIESに格納
                    If hIES <> 0 Then Exit Do
                End If
            End If
            hWnd = GetNextWindow(hWnd, GW_HWNDNEXT)
        Loop While hWnd <> 0

    If hIES = 0 Then Exit Sub

    'IHTMLDocument2取得
    msg = RegisterWindowMessage("WM_HTML_GETOBJECT")
    SendMessageTimeout hIES, msg, 0, 0, SMTO_ABORTIFHUNG, 1000, res
    If res Then
        With IID_IHTMLDocument2
            .Data1 = &#038;H332C4425
            .Data2 = &#038;H26CB
            .Data3 = &#038;H11D0
            .Data4(0) = &#038;HB4
            .Data4(1) = &#038;H83
            .Data4(2) = &#038;H0
            .Data4(3) = &#038;HC0
            .Data4(4) = &#038;H4F
            .Data4(5) = &#038;HD9
            .Data4(6) = &#038;H1
            .Data4(7) = &#038;H19
        End With
        
        'IeEdgeを使いたいために適当にDomという変数を宣言して左辺に
        '正しくは「If ObjectFromLresult(res, IID_IHTMLDocument2, 0, IeEdge) = 0 Then」などの使用方法が望ましい？
        
        Dom = ObjectFromLresult(res, IID_IHTMLDocument2, 0, IeEdge)
    
    End If

End Sub

Private Function EnumChildProcIES(ByVal hWnd As Long, ByVal lParam As Long) As Long
  
    Dim buf As String * 255
    Dim ClassName As String

    GetClassName hWnd, buf, Len(buf)
    ClassName = Left(buf, InStr(buf, vbNullChar) - 1)
    If ClassName = "Internet Explorer_Server" Then
        hIES = hWnd
        EnumChildProcIES = False
        Exit Function
    End If
    EnumChildProcIES = True

End Function
</pre>
</div>
<h3><span id="toc9">実行部分</span></h3>
<div class="GASCode">
<pre>
Sub 検索実行()

    Edge起動
    
    'GoogleのページをIEモードで開いた後に実行
    DOM取得 'IEモードのEdgeのDOMをIeEdgeに格納
    
    '最前面に持ってくるとIEモードのポップアップが消える
    Call SetForegroundWindow(hWnd)
    Sleep 3000
    
    IeEdge.getElementsByName("q")(0).Value = "VBA・GAS・Pythonで業務を楽しく効率化" '検索バーに文字を入力
    Sleep 1000
    IeEdge.getElementsByName("btnG")(0).Click '検索クリック

    While LCase(IeEdge.readyState) <> "complete"
        Sleep 1000
    Wend
    
End Sub

Private Sub Edge起動()

    'EdgeでGoogleのページを開く
    CreateObject("Shell.Application").ShellExecute "microsoft-edge:https://www.google.co.jp/"
    Sleep 3000

    'Alt+F
    keybd_event VK_Alt, 0, fKEYDOWN, 0   'Altを押す
    keybd_event VK_F, 0, fKEYDOWN, 0   'fを押す
    keybd_event VK_F, 0, fKEYUP, 0   'fを離す
    keybd_event VK_Alt, 0, fKEYUP, 0   'Altを離す

    Sleep 1000

    '上5回
    Dim i
    For i = 1 To 5
        keybd_event VK_UP, 0, fKEYDOWN, 0   'UPを押す
        keybd_event VK_UP, 0, fKEYUP, 0   'UPを離す
        Sleep 100
    Next i
        
    'Enter
    keybd_event VK_RETURN, 0, fKEYDOWN, 0   'Enterを押す
    keybd_event VK_RETURN, 0, fKEYUP, 0   'Enterを離す

    Sleep 1000

    'Enter
    keybd_event VK_RETURN, 0, fKEYDOWN, 0   'Enterを押す
    keybd_event VK_RETURN, 0, fKEYUP, 0   'Enterを離す

    Sleep 3000
    
End Sub
</pre>
</div>
<div style="width: 1280px;" class="wp-video"><video class="wp-video-shortcode" id="video-2370-1" width="1280" height="720" preload="metadata" controls="controls"><source type="video/mp4" src="https://officevba.info/wp-content/uploads/2022/03/vba164edge.mp4?_=1" /><a href="https://officevba.info/wp-content/uploads/2022/03/vba164edge.mp4">https://officevba.info/wp-content/uploads/2022/03/vba164edge.mp4</a></video></div>
<p>あくまでIEモードの操作に限られますのでいずれ使えなくなるとは思いますが、これまでVBAでIEを操作していたコードをほぼそのまま転用できるのは大きなメリットです。</p>
<p>IEモードの切り替え部分が若干不安定なこともありますが、最初から特定のページをIEモードで開くように設定しておけば切り替えるコードを使う必要がなく、より安定した挙動になると思います。</p>
<p>このコードはWindowsAPIをたくさん使用していて私も意味が理解できていない部分が多くありますので、勉強してコードの意味が理解できるようになればまた紹介記事を記載します。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/edgeiemodeoperation/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		<enclosure url="https://officevba.info/wp-content/uploads/2022/03/vba164edge.mp4" length="0" type="video/mp4" />

			</item>
		<item>
		<title>たくさんのExcelファイルから特定のセルの値を抜き出し一覧にするVBAコード</title>
		<link>https://officevba.info/extractcellsvalue/</link>
					<comments>https://officevba.info/extractcellsvalue/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 05 Sep 2021 14:36:39 +0000</pubDate>
				<category><![CDATA[ブックの操作]]></category>
		<category><![CDATA[VBA]]></category>
		<category><![CDATA[セルの値]]></category>
		<category><![CDATA[マクロ]]></category>
		<category><![CDATA[一括]]></category>
		<category><![CDATA[取得]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2337</guid>

					<description><![CDATA[目次 以前のVBAのコードをVBA未経験者でも自由に使えるように修正用意したワークシート一つのフォルダに格納された複数のExcelファイルから特定のセルの値を抜き出し一覧にするExcelVBAコード 以前の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-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">以前のVBAのコードをVBA未経験者でも自由に使えるように修正</a></li><li><a href="#toc2" tabindex="0">用意したワークシート</a></li><li><a href="#toc3" tabindex="0">一つのフォルダに格納された複数のExcelファイルから特定のセルの値を抜き出し一覧にするExcelVBAコード</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">以前のVBAのコードをVBA未経験者でも自由に使えるように修正</span></h2>
<p>以前<a href="https://officevba.info/fileikkatsushori/" target="_blank">こちら</a>の記事で一つのフォルダに格納されたたくさんのファイルに同じ処理をするVBAコードをご紹介しました。</p>
<p>その際に紹介した内容は「ファイルに決まった文字を入力する」という内容だったのですが、今回は逆にファイルの中に「記載されている値を抜き出す」というものになります。</p>
<p>また、どこの値を抜き出すのかをコードの中で指定するのではなく、ワークシートの中に対象セルの情報を記載するようにし、コードを編集しなくても抽出対象のセルを変更できるようにしました。</p>
<h2><span id="toc2">用意したワークシート</span></h2>
<p>今回はフォルダに含まれているすべてのファイル（ブック）について、ファイルを開いてすべてのシートからセルの値を取得するVBAです。</p>
<p>以前からの改良点として、どこのセルの値を抜き出すか、セルの番地（A1とかB8など）をあらかじめツールのワークシートに記載しておくことで、VBAを知らない人でも使いこなせるように作ってみました。</p>
<p>一覧として抽出後、どのファイルのどのシートから抜き出した値かを確認できるようにするため、ファイル名・シート名を記載する列も用意しています。</p>
<h2><span id="toc3">一つのフォルダに格納された複数のExcelファイルから特定のセルの値を抜き出し一覧にするExcelVBAコード</span></h2>
<p>作成したコードは以下の通りです。</p>
<p>2行目の3列目以降にセルの番地を入力しておくことで、そのセルの値を対象シートから順に抜き出します。<br />
1行目は何かメモを取ったりするために空けています。</p>
<div class="GASCode">
<pre>Sub フォルダの中に含まれるファイルから値を順に取得する()

    Dim FolderName As String
    Dim FileName As String
    Dim Index As Integer
    Dim targetBook
    Dim targetSheet
    Dim i, k
    
    FolderName = Application.GetOpenFilename 'ダイアログを用いて選択したファイルのパスをFolderNameとする①
    
    If FolderName = "False" Then  'FolderNameが選択されていなければ作業を終了する
        Exit Sub
    End If
    
    '今のフォルダ名には選択したファイル名含まれているので、ファイル名の部分を切り取る作業。
    Index = InStrRev(FolderName, "\")  'フォルダ名部分の文字数をカウントする
    FolderName = Left(FolderName, Index) ' カウントした文字数までの部分を切り取ってフォルダ名とする
    FileName = Dir(FolderName &#038; "*xls*")  ' フォルダの中に含まれるファイルを取り出す

    i = 3 '行の位置を設定するカウンタ
    Do While FileName <> ""  ' ファイルがなくなるまで繰り返す
        Set targetBook = Workbooks.Open(FolderName &#038; FileName)  'ファイルを開く
        For Each targetSheet In targetBook.Worksheets
            With ThisWorkbook.ActiveSheet
                .Cells(i, 1) = targetBook.Name  '1列名はブック名
                .Cells(i, 2) = targetSheet.Name '2列名はシート名
                
                '3列目以降セルの値を抜き出す
                For k = 3 To .Cells(1, Columns.Count).End(xlToLeft).Column
                    .Cells(i, k) = targetSheet.Range(.Cells(2, k))  '2行目に書かれている番地を対象シートから抽出
                Next k
            End With
        
            i = i + 1
        Next
        targetBook.Close
        Set targetBook = Nothing
    FileName = Dir()
    Loop

End Sub</pre>
</div>
<p>それほど複雑なコードでなく、行数も少ないわりにファイルが多いとめちゃくちゃ役に立つのでコスパが良いコードを作ることができました。<br />
値の入力に関しても同じように作ったものをまたご紹介するようにします。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/extractcellsvalue/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Dictionaryオブジェクトを用いたVBAの高速化②（SUMIFS関数の代用）</title>
		<link>https://officevba.info/dictionary-sumifs/</link>
					<comments>https://officevba.info/dictionary-sumifs/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 18 Jul 2021 03:46:57 +0000</pubDate>
				<category><![CDATA[マクロの処理時間の短縮]]></category>
		<category><![CDATA[連想配列]]></category>
		<category><![CDATA[Dictionary]]></category>
		<category><![CDATA[sumifs]]></category>
		<category><![CDATA[高速化]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2262</guid>

					<description><![CDATA[目次 行数が多いExcelシートを操作する際の集計作業の高速化SUMIFS関数とはDictionaryオブジェクト（連想配列）についてDictionaryオブジェクトを用いたSUMIFS関数と同様の処理の高速化用意したサ [&#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">行数が多いExcelシートを操作する際の集計作業の高速化</a></li><li><a href="#toc2" tabindex="0">SUMIFS関数とは</a></li><li><a href="#toc3" tabindex="0">Dictionaryオブジェクト（連想配列）について</a></li><li><a href="#toc4" tabindex="0">Dictionaryオブジェクトを用いたSUMIFS関数と同様の処理の高速化</a><ol><li><a href="#toc5" tabindex="0">用意したサンプルシート</a></li><li><a href="#toc6" tabindex="0">処理内容について</a></li></ol></li><li><a href="#toc7" tabindex="0">処理時間の比較</a><ol><li><a href="#toc8" tabindex="0">①VBAでWorkSheetFunctionのSUMIFS関数を使用する</a></li><li><a href="#toc9" tabindex="0">②計算式としてSUMIFS関数をセルに格納する</a></li><li><a href="#toc10" tabindex="0">③計算式としてSUMIFS関数をセルに格納するが、1つ目のみ関数を入力、下の行についてはコピペをする</a></li></ol></li></ol>
    </div>
  </div>

<h2><span id="toc1">行数が多いExcelシートを操作する際の集計作業の高速化</span></h2>
<p>以前にVlookup関数の代わりにVBAでDictionaryオブジェクトを使用して高速化する手段をご紹介しました。<br />
この方法はVlookup関数以外の他の関数についても適用することができ、使いこなせるようになるとかなり便利です。</p>
<p>今回はDictionaryオブジェクトを使用してSUMIFS関数で算出される計算の高速化をご紹介します。</p>
<h2><span id="toc2">SUMIFS関数とは</span></h2>
<p>SUM関数・IF関数を使用される方にとってはSUMIFS関数を使ったことがなくても字面でイメージは湧きやすいと思いますが、SUMIFS関数は「範囲の中で特定の条件に一致するものを合計する」関数です。</p>
<p>私は売上一覧の明細の中から特定の商品の売上を抜き出したり、同じものが何行も含まれている一覧から商品カテゴリごとの売上を算出するのによく使用しています。</p>
<p>SUMIFS関数と似た関数でSUMIF関数というものもあります。<br />
SUMIF関数はSUMIFS関数とよく似ていますが、合致する条件項目を1つしか設定できません。</p>
<p>SUMIFS関数の方が条件設定を複数設定できる分汎用性が高く、私は基本的に条件が1つでもSUMIFS関数を使用しています。</p>
<h2><span id="toc3">Dictionaryオブジェクト（連想配列）について</span></h2>
<p>Dictionaryオブジェクトは特定のキーに対するアイテムを設定することで、該当のキーに対するアイテムを素早く呼び出す仕組みです。</p>
<p>通常のワークシート関数では行数が増えるごとに処理に時間がかかりますが、Dictionaryオブジェクトを用いた計算では処理時間が大幅に短縮されるのが特徴です。</p>
<p>また、キーは一意のものになりますので重複データから必要なものだけ取り出すときにも便利です。</p>
<h2><span id="toc4">Dictionaryオブジェクトを用いたSUMIFS関数と同様の処理の高速化</span></h2>
<h3><span id="toc5">用意したサンプルシート</span></h3>
<p>用意したシートは以下のようなものになります。<br />
シート1はJANコードと品名・包装単位・金額（仮）を記載していて、品名は何度か重複しています。<br />
全体の行数は約27万行、重複除いた品名の種類は約2万行で、重複の削除機能を使ってシート2にJANコードの一覧を用意しています。</p>
<li>シート1</li>
<p><a href="https://officevba.info/wp-content/uploads/2021/07/439815dd8e5784bbb079387253538967.jpg"><img fetchpriority="high" decoding="async" src="https://officevba.info/wp-content/uploads/2021/07/439815dd8e5784bbb079387253538967-700x814.jpg" alt="vba156シート1" width="700" height="814" class="alignnone size-large wp-image-2260" srcset="https://officevba.info/wp-content/uploads/2021/07/439815dd8e5784bbb079387253538967-700x814.jpg 700w, https://officevba.info/wp-content/uploads/2021/07/439815dd8e5784bbb079387253538967-258x300.jpg 258w, https://officevba.info/wp-content/uploads/2021/07/439815dd8e5784bbb079387253538967-768x893.jpg 768w, https://officevba.info/wp-content/uploads/2021/07/439815dd8e5784bbb079387253538967.jpg 992w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<li>シート2</li>
<p><a href="https://officevba.info/wp-content/uploads/2021/07/bfe92b580f9418d7de5f6fcdb3a50475.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2021/07/bfe92b580f9418d7de5f6fcdb3a50475.jpg" alt="vba156シート2" width="537" height="1161" class="alignnone size-full wp-image-2261" srcset="https://officevba.info/wp-content/uploads/2021/07/bfe92b580f9418d7de5f6fcdb3a50475.jpg 537w, https://officevba.info/wp-content/uploads/2021/07/bfe92b580f9418d7de5f6fcdb3a50475-139x300.jpg 139w" sizes="(max-width: 537px) 100vw, 537px" /></a></p>
<h3><span id="toc6">処理内容について</span></h3>
<p>今回私が作成したコードは以下のものになります。</p>
<div class="VBACode">Sub Dictionaryを使ってSUMIFS関数の代わりを実行()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’開始時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;Dictionary開始-&#8221; &#038; Time</span><br />
<span class="VBA_Tab1">Dim dicJAN</span><br />
<span class="VBA_Tab1">Set dicJAN = CreateObject(&#8220;Scripting.Dictionary&#8221;)</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’JAN と金額の合計をDictionaryに格納</span></span><br />
<span class="VBA_Tab1">Dim y, i</span><br />
<span class="VBA_Tab1">With Sheets(1)</span><br />
<span class="VBA_Tab2">y = .Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab2">For i = 2 To y</span><br />
<span class="VBA_Tab3"><span class="VBA_Comment0">’JANコードの登録がない場合、Dictionaryに追加</span></span><br />
<span class="VBA_Tab3">If Not dicJAN.Exists(.Cells(i, 1).Value) Then</span><br />
<span class="VBA_Tab4">dicJAN.Add .Cells(i, 1).Value, .Cells(i, 5)</span><br />
<span class="VBA_Tab3"><span class="VBA_Comment0">’JANコードの登録がある場合、Dictionaryに格納されている値を更新</span></span><br />
<span class="VBA_Tab3">Else</span><br />
<span class="VBA_Tab4">dicJAN(.Cells(i, 1).Value) = dicJAN(.Cells(i, 1).Value) + .Cells(i, 5)</span><br />
<span class="VBA_Tab3">End If</span><br />
<span class="VBA_Tab2">Next i</span><br />
<span class="VBA_Tab1">End With</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’JAN一覧B列に合計金額の出力</span></span><br />
<span class="VBA_Tab1">With Sheets(2)</span><br />
<span class="VBA_Tab2">For i = 2 To dicJAN.Count + 1</span><br />
<span class="VBA_Tab3">.Cells(i, 2) = dicJAN(.Cells(i, 1).Value)</span><br />
<span class="VBA_Tab2">Next i</span><br />
<span class="VBA_Tab1">End With</span><br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’終了時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;Dictionary終了-&#8221; &#038; Time</span><br />
<br />
End Sub</div>
<p>まずシート1のJANコードと金額をDicrionaryオブジェクトに格納します。</p>
<p>Dictionary関数に格納する際、Vlookup関数の代わりの場合は重複分を無視することで同じ挙動になりますが、SUMIFS関数の代わりに使用する場合は以下の条件分岐を用います。</p>
<p>・①JANコードがキーとして登録されていない場合：キー（JANコード）とアイテム（金額）を追加<br />
・②JANコードがキーとして登録されていない場合：キー（JANコード）に登録されているアイテム（金額）に現在の行の金額を追加</p>
<h2><span id="toc7">処理時間の比較</span></h2>
<p>上記Dictionaryオブジェクトを用いた処理時間と通常通りSUMIFS関数を用いた場合の処理時間について比較してみました。<br />
比較するのに用いたコードは以下の3通りです。</p>
<h3><span id="toc8">①VBAでWorkSheetFunctionのSUMIFS関数を使用する</span></h3>
<div class="VBACode">Sub WorksheetFunctionのSUMIFS関数を使う()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’開始時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;worksheet関数開始-&#8221; &#038; Time</span><br />
<span class="VBA_Tab1">Dim y1, y2, i</span><br />
<br />
<span class="VBA_Tab1">y1 = Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab1">With Sheets(2)</span><br />
<span class="VBA_Tab2">y2 = .Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab2">For i = 2 To y2</span><br />
<span class="VBA_Tab3">.Cells(i, 3) = WorksheetFunction.SumIfs(Range(Sheets(1).Cells(2, 5), Sheets(1).Cells(y1, 5)), Range(Sheets(1).Cells(2, 1), Sheets(1).Cells(y1, 1)), .Cells(i, 1))</span><br />
<span class="VBA_Tab2">Next i</span><br />
<span class="VBA_Tab1">End With</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’終了時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;worksheet関数終了-&#8221; &#038; Time</span><br />
<br />
End Sub</div>
<h3><span id="toc9">②計算式としてSUMIFS関数をセルに格納する</span></h3>
<div class="VBACode">Sub SUMIFS関数()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’開始時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;関数開始-&#8221; &#038; Time</span><br />
<span class="VBA_Tab1">Dim y1, y2, i</span><br />
<br />
<span class="VBA_Tab1">y1 = Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab1">With Sheets(2)</span><br />
<span class="VBA_Tab2">y2 = .Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab2">For i = 2 To y2</span><br />
<span class="VBA_Tab3">.Cells(i, 4) = &#8220;=SUMIFS(Sheet1!$E$2:$E$&#8221; &#038; y1 &#038; &#8220;,Sheet1!$A$2:$A$&#8221; &#038; y1 &#038; &#8220;,Sheet2!&#8221; &#038; Replace(.Cells(i, 1).Address, &#8220;$&#8221;, &#8220;&#8221;) &#038; &#8220;)&#8221;</span><br />
<span class="VBA_Tab2">Next i</span><br />
<span class="VBA_Tab1">End With</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’終了時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;関数終了-&#8221; &#038; Time</span><br />
<br />
End Sub</div>
<h3><span id="toc10">③計算式としてSUMIFS関数をセルに格納するが、1つ目のみ関数を入力、下の行についてはコピペをする</span></h3>
<div class="VBACode">Sub SUMIFS関数コピペ()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’開始時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;関数コピペ開始-&#8221; &#038; Time</span><br />
<span class="VBA_Tab1">Dim y1, y2, i</span><br />
<br />
<span class="VBA_Tab1">y1 = Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab1">With Sheets(2)</span><br />
<span class="VBA_Tab2">y2 = .Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab2">.Cells(2, 5) = &#8220;=SUMIFS(Sheet1!$E$2:$E$&#8221; &#038; y1 &#038; &#8220;,Sheet1!$A$2:$A$&#8221; &#038; y1 &#038; &#8220;,Sheet2!&#8221; &#038; Replace(.Cells(2, 1).Address, &#8220;$&#8221;, &#8220;&#8221;) &#038; &#8220;)&#8221;</span><br />
<span class="VBA_Tab2">.Cells(2, 5).Copy</span><br />
<span class="VBA_Tab2">Range(.Cells(3, 5), Cells(y2, 5)).PasteSpecial</span><br />
<span class="VBA_Tab1">End With</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’終了時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;関数コピペ終了-&#8221; &#038; Time</span><br />
<br />
End Sub</div>
<p>処理時間の違いは以下の表の通りです。</p>
<table border=1>
<tr>
<td align=center></td>
<th align=center>Dictionary</th>
<th align=center>①WorkSheetFunction</th>
<th align=center>②計算式の入力</th>
<th align=center>③計算式コピペ</th>
</tr>
<tr>
<td align=center>1回目</td>
<td align=center>0:00:03</td>
<td align=center>0:21:14</td>
<td align=center>0:21:09</td>
<td align=center>0:01:57</td>
</tr>
<tr>
<td align=center>2回目</td>
<td align=center>0:00:04</td>
<td align=center>0:21:02</td>
<td align=center>0:21:09</td>
<td align=center>0:01:56</td>
</tr>
<tr>
<td align=center>3回目</td>
<td align=center>0:00:03</td>
<td align=center>0:21:04</td>
<td align=center>0:21:05</td>
<td align=center>0:01:54</td>
</tr>
<tr>
<td align=center>平均</td>
<td align=center>0:00:03</td>
<td align=center>0:21:07</td>
<td align=center>0:21:08</td>
<td align=center>0:01:56</td>
</tr>
</table>
<p>Dictionaryオブジェクトを用いることで処理が圧倒的に高速になりました。</p>
<p>関数自体のコピペも遅くはないですが、もっと条件分岐を複雑にすれば差はより大きく広がります。<br />
WorkSheetFunctionなどは行数が増えると処理にかなり時間がかかることから、ある程度たくさんの行を扱う業務では使いにくいと思います。</p>
<p>ピポットテーブルも方法としてはありですが、そのあとの加工がやりにくい、フォーマットが自由に設定できないなどがあるため、今回のように単純に関数の代わりに高速処理ができるVBAのコードは必要な場面も多いと思います。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/dictionary-sumifs/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Dictionaryオブジェクトを用いたVBAの高速化①（VlookUp関数の代用）</title>
		<link>https://officevba.info/dictionary-vlookup/</link>
					<comments>https://officevba.info/dictionary-vlookup/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Tue, 03 Nov 2020 04:58:29 +0000</pubDate>
				<category><![CDATA[マクロの処理時間の短縮]]></category>
		<category><![CDATA[連想配列]]></category>
		<category><![CDATA[Dictionary]]></category>
		<category><![CDATA[VBA]]></category>
		<category><![CDATA[配列]]></category>
		<category><![CDATA[高速化]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2192</guid>

					<description><![CDATA[目次 行数が多いExcelシートを操作するのにVBAで関数を使うと遅いDictionaryオブジェクトの使用するための事前準備今回処理を行うワークシートについてVlookUp関数を使用した値の記載方法についてDictio [&#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">行数が多いExcelシートを操作するのにVBAで関数を使うと遅い</a></li><li><a href="#toc2" tabindex="0">Dictionaryオブジェクトの使用するための事前準備</a></li><li><a href="#toc3" tabindex="0">今回処理を行うワークシートについて</a></li><li><a href="#toc4" tabindex="0">VlookUp関数を使用した値の記載方法について</a></li><li><a href="#toc5" tabindex="0">Dictionaryオブジェクトを使用したVBAの使用方法とサンプルコードについて</a></li><li><a href="#toc6" tabindex="0">処理速度の差について集計</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">行数が多いExcelシートを操作するのにVBAで関数を使うと遅い</span></h2>
<p>VlookUp関数をVBAで使うにあたって、対象となるデータが少ない場合は特に問題ないのですが、多くなるにつれてデータ処理に時間がかかります。</p>
<p>私は仕事で20万行のデータに10万行のマスタデータからコードを抽出する必要があり、VBAでWorkSteetFunctionのVlookUp関数を使用すると数時間単位で時間がかかってしまうことがありました。</p>
<p>行数の多いExcelについてはVBAの処理速度が遅くなってしまうことが多くの人にとって悩みになると思います。</p>
<p>私の事例では連想配列（Dictionaryオブジェクト）を使用してコードを書きなおすことで処理速度が格段に速くなりましたので、連想配列（Dictionary）オブジェクトの使い方について何回かに分けてご紹介します。</p>
<p>今回はVlookup関数の代用としてDictionaryオブジェクトを使用する方法についてご紹介します。</p>
<h2><span id="toc2">Dictionaryオブジェクトの使用するための事前準備</span></h2>
<p>事前準備については<a href="https://officevba.info/dictionary-top/" target="_blank">こちら</a>のページに記載しておりますので詳しく知りたい方はリンク先をご確認ください。</p>
<p>また、セルの値を取得するのにそのまま取得するのではなく、配列に一度格納する方法を使用しております。<br />
これはVBAの高速化の手順で最も有効な手段の1つです。</p>
<p>詳細は<a href="https://officevba.info/cellstoarray/" target="_blank">こちら</a>のページに記載しておりますので併せてご確認いただけるとありがたいです。</p>
<h2><span id="toc3">今回処理を行うワークシートについて</span></h2>
<p>私が今回業務で行ったものの一部になるのですが、商品の仕入伝票一覧に商品マスタの「YJコード」というコードを結合するものです。<br />
（データの内容は変更していますので、世の中に存在しない商品などが記載されています。）</p>
<p>商品の仕入一覧が約20万行、商品マスタが約10万行あり、VlookUpを使用するだけでは時間がかかっていました。<br />
サンプルの伝票一覧と商品マスタは下記のようなイメージです。</p>
<p><a href="https://officevba.info/wp-content/uploads/2020/11/5315246759b65c0eb4f5124e675f2ced.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2020/11/5315246759b65c0eb4f5124e675f2ced-300x49.jpg" alt="" width="300" height="49" class="alignnone size-medium wp-image-2191" srcset="https://officevba.info/wp-content/uploads/2020/11/5315246759b65c0eb4f5124e675f2ced-300x49.jpg 300w, https://officevba.info/wp-content/uploads/2020/11/5315246759b65c0eb4f5124e675f2ced-700x113.jpg 700w, https://officevba.info/wp-content/uploads/2020/11/5315246759b65c0eb4f5124e675f2ced-768x124.jpg 768w, https://officevba.info/wp-content/uploads/2020/11/5315246759b65c0eb4f5124e675f2ced.jpg 1136w" sizes="(max-width: 300px) 100vw, 300px" /></a>　<a href="https://officevba.info/wp-content/uploads/2020/11/bf33ca4973fac54e1e0161293426745d.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2020/11/bf33ca4973fac54e1e0161293426745d-300x174.jpg" alt="" width="300" height="174" class="alignnone size-medium wp-image-2190" srcset="https://officevba.info/wp-content/uploads/2020/11/bf33ca4973fac54e1e0161293426745d-300x174.jpg 300w, https://officevba.info/wp-content/uploads/2020/11/bf33ca4973fac54e1e0161293426745d-700x405.jpg 700w, https://officevba.info/wp-content/uploads/2020/11/bf33ca4973fac54e1e0161293426745d-768x444.jpg 768w, https://officevba.info/wp-content/uploads/2020/11/bf33ca4973fac54e1e0161293426745d.jpg 795w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<h2><span id="toc4">VlookUp関数を使用した値の記載方法について</span></h2>
<p>Dictionaryオブジェクトを使用したVBAコードがどれくらい高速になるかを検証するために、通常のVlookUp関数をWorkSheetFunctionで呼び出した場合と比較することにします。</p>
<p>配列に格納してからセルの値を変更するものも用意しています。</p>
<p>コードは「伝票一覧」の最後の列に「商品マスタ」に記載されている「YJコード」を追記するものになります。</p>
<div class="VBACode">Sub 配列なしのVlookupのVBAコード1()<br />
<br />
<span class="VBA_Tab1">Debug.Print &#8220;開始&#8221; &#038; &#8220;_&#8221; &#038; Time  <span class="VBA_Comment">’時間の測定</span></span><br />
<br />
<span class="VBA_Tab1">Dim l</span><br />
<span class="VBA_Tab1">Dim マスタ下端行</span><br />
<span class="VBA_Tab1">マスタ下端行 = Sheets(&#8220;商品マスタ&#8221;).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<br />
<span class="VBA_Tab1">Dim 伝票下端行</span><br />
<span class="VBA_Tab1">伝票下端行 = Sheets(&#8220;伝票&#8221;).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<br />
<span class="VBA_Tab1">Dim i</span><br />
<br />
<span class="VBA_Tab1">Sheets(&#8220;伝票&#8221;).Cells(1, 9) = &#8220;YJコード&#8221;</span><br />
<span class="VBA_Tab1">On Error Resume Next <span class="VBA_Comment">’VlookUpで引きあたるものがないとき無視する</span></span><br />
<span class="VBA_Tab1">For i = 2 To 伝票下端行</span><br />
<span class="VBA_Tab2">Sheets(&#8220;伝票&#8221;).Cells(i, 9) = WorksheetFunction.VLookup(Sheets(&#8220;伝票&#8221;).Cells(i, 4), Range(Sheets(&#8220;商品マスタ&#8221;).Cells(1, 1), Sheets(&#8220;商品マスタ&#8221;).Cells(マスタ下端行, 4)), 2, False)</span><br />
<span class="VBA_Tab1">Next i</span><br />
<span class="VBA_Tab1">On Error GoTo 0</span><br />
<br />
<span class="VBA_Tab1">Debug.Print &#8220;終了&#8221; &#038; &#8220;_&#8221; &#038; Time <span class="VBA_Comment">’時間の測定</span></span><br />
<br />
End Sub</div>
<p>こちらはセルに直接値を書き込むのではなく、配列に格納して変更を加えてから一括でセルに書き込むフローになります。<br />
一般的な処理ではステップは増えますが処理速度が高速になります。</p>
<div class="VBACode">Sub 配列ありのVlookupのVBAコード2()<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’セルに直接書き込むのではなく配列に格納して変更した後、一括で書き込む</span></span><br />
<br />
<span class="VBA_Tab1">Debug.Print &#8220;開始&#8221; &#038; &#8220;_&#8221; &#038; Time  <span class="VBA_Comment">’時間の測定</span></span><br />
<br />
<span class="VBA_Tab1">Dim l</span><br />
<span class="VBA_Tab1">Dim マスタ下端行</span><br />
<span class="VBA_Tab1">マスタ下端行 = Sheets(&#8220;商品マスタ&#8221;).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab1">Dim 参照範囲</span><br />
<span class="VBA_Tab1">参照範囲 = Range(Sheets(&#8220;商品マスタ&#8221;).Cells(1, 1), Sheets(&#8220;商品マスタ&#8221;).Cells(マスタ下端行, 4))</span><br />
<br />
<span class="VBA_Tab1">Dim 伝票下端行</span><br />
<span class="VBA_Tab1">伝票下端行 = Sheets(&#8220;伝票&#8221;).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’セルに直接書き込むよりスピードアップのために配列に格納する</span></span><br />
<span class="VBA_Tab1">Dim 伝票範囲</span><br />
<span class="VBA_Tab1">伝票範囲 = Range(Sheets(&#8220;伝票&#8221;).Cells(1, 1), Sheets(&#8220;伝票&#8221;).Cells(伝票下端行, 9))</span><br />
<br />
<span class="VBA_Tab1">Dim i</span><br />
<span class="VBA_Tab1">伝票範囲(1, 9) = &#8220;YJコード&#8221;</span><br />
<span class="VBA_Tab1">On Error Resume Next <span class="VBA_Comment">’VlookUpで引きあたるものがないとき無視する</span></span><br />
<span class="VBA_Tab1">For i = 2 To 伝票下端行</span><br />
<span class="VBA_Tab2">伝票範囲(i, 9) = WorksheetFunction.VLookup(伝票範囲(i, 4), 参照範囲, 2, False)</span><br />
<span class="VBA_Tab1">Next i</span><br />
<span class="VBA_Tab1">On Error GoTo 0</span><br />
<br />
<span class="VBA_Tab1">Range(Sheets(&#8220;伝票&#8221;).Cells(1, 1), Sheets(&#8220;伝票&#8221;).Cells(伝票下端行, 9)) = 伝票範囲</span><br />
<br />
<span class="VBA_Tab1">Debug.Print &#8220;終了&#8221; &#038; &#8220;_&#8221; &#038; Time <span class="VBA_Comment">’時間の測定</span></span><br />
<br />
End Sub</div>
<h2><span id="toc5">Dictionaryオブジェクトを使用したVBAの使用方法とサンプルコードについて</span></h2>
<p>キーは商品固有の値「JANコード」としています。<br />
キーに持ってくるのは重複のないものなら何でも大丈夫ですが、半角・全角・ひらがな・カタカナが全部揃えないといけないので日本語の名称などは使いにくいと思います。</p>
<p>こちらもそのままセルの値を取得するか、もしくはセルを配列に格納してから参照、書き込みをするかの2パターン用意しています。</p>
<div class="VBACode">Sub 配列なしのDictionaryオブジェクトを用いたコード()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’配列を用いない</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;開始&#8221; &#038; &#8220;_&#8221; &#038; Time  <span class="VBA_Comment">’時間の測定</span></span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’Dictionaryの利用準備</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment">’<span class="VBA_Comment">’参照設定をする場合</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment">’Dim dictJANYJ As Dictionary</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment">’Set dictJANYJ = New Dictionary</span></span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’参照設定をしない場合</span></span><br />
<span class="VBA_Tab1">Dim dictJANYJ As Object</span><br />
<span class="VBA_Tab1">Set dictJANYJ = CreateObject(&#8220;Scripting.Dictionary&#8221;)</span><br />
<br />
<span class="VBA_Tab1">Dim l</span><br />
<span class="VBA_Tab1">Dim マスタ下端行</span><br />
<span class="VBA_Tab1">マスタ下端行 = Sheets(&#8220;商品マスタ&#8221;).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’JANをKey、YJをItemに格納</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment">’セルの値を直接Dictionaryに格納する場合、Textプロパティを指定しないとうまくいかない</span></span><br />
<span class="VBA_Tab1">For l = 2 To マスタ下端行</span><br />
<span class="VBA_Tab2">If dictJANYJ.Exists(Sheets(&#8220;商品マスタ&#8221;).Cells(l, 1).Text) = False Then <span class="VBA_Comment">’キーは重複できないので、存在有無を確認してから追加</span></span><br />
<span class="VBA_Tab3">dictJANYJ.Add Key:=Sheets(&#8220;商品マスタ&#8221;).Cells(l, 1).Text, Item:=Sheets(&#8220;商品マスタ&#8221;).Cells(l, 2).Text</span><br />
<span class="VBA_Tab2">End If</span><br />
<span class="VBA_Tab1">Next l</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’dictJANYJに格納した値を参照してセルに記載</span></span><br />
<span class="VBA_Tab1">Dim 伝票下端行</span><br />
<span class="VBA_Tab1">伝票下端行 = Sheets(&#8220;伝票&#8221;).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<br />
<span class="VBA_Tab1">Dim i</span><br />
<span class="VBA_Tab1">Sheets(&#8220;伝票&#8221;).Cells(1, 9) = &#8220;YJコード&#8221;</span><br />
<span class="VBA_Tab1">For i = 2 To 伝票下端行</span><br />
<span class="VBA_Tab2">Sheets(&#8220;伝票&#8221;).Cells(i, 9) = dictJANYJ.Item(Sheets(&#8220;伝票&#8221;).Cells(i, 4).Text)</span><br />
<span class="VBA_Tab1">Next i</span><br />
<br />
<span class="VBA_Tab1">Debug.Print &#8220;終了&#8221; &#038; &#8220;_&#8221; &#038; Time <span class="VBA_Comment">’時間の測定</span></span><br />
<br />
End Sub</div>
<div class="VBACode">Sub 配列ありでDictionaryオブジェクトを用いたコード2()<br />
<br />
<span class="VBA_Tab1">Debug.Print &#8220;開始&#8221; &#038; &#8220;_&#8221; &#038; Time  <span class="VBA_Comment">’時間の測定</span></span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’Dictionaryの利用準備</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment">’<span class="VBA_Comment">’参照設定をする場合</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment">’Dim dictJANYJ As Dictionary</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment">’Set dictJANYJ = New Dictionary</span></span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’参照設定をしない場合</span></span><br />
<span class="VBA_Tab1">Dim dictJANYJ As Object</span><br />
<span class="VBA_Tab1">Set dictJANYJ = CreateObject(&#8220;Scripting.Dictionary&#8221;)</span><br />
<br />
<span class="VBA_Tab1">Dim l</span><br />
<span class="VBA_Tab1">Dim マスタ下端行</span><br />
<span class="VBA_Tab1">マスタ下端行 = Sheets(&#8220;商品マスタ&#8221;).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab1">Dim 参照範囲</span><br />
<span class="VBA_Tab1">参照範囲 = Range(Sheets(&#8220;商品マスタ&#8221;).Cells(1, 1), Sheets(&#8220;商品マスタ&#8221;).Cells(マスタ下端行, 4))</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’JANをKey、YJをItemに格納</span></span><br />
<span class="VBA_Tab1">For l = 2 To マスタ下端行</span><br />
<span class="VBA_Tab2">If dictJANYJ.Exists(参照範囲(l, 1)) = False Then <span class="VBA_Comment">’キーは重複できないので、存在有無を確認してから追加</span></span><br />
<span class="VBA_Tab3">dictJANYJ.Add Key:=参照範囲(l, 1), Item:=参照範囲(l, 2)</span><br />
<span class="VBA_Tab2">End If</span><br />
<span class="VBA_Tab1">Next l</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’dictJANYJに格納した値を参照してセルに記載</span></span><br />
<span class="VBA_Tab1">Dim 伝票下端行</span><br />
<span class="VBA_Tab1">伝票下端行 = Sheets(&#8220;伝票&#8221;).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’セルに直接書き込むよりスピードアップのために配列に格納する</span></span><br />
<span class="VBA_Tab1">Dim 伝票範囲</span><br />
<span class="VBA_Tab1">伝票範囲 = Range(Sheets(&#8220;伝票&#8221;).Cells(1, 1), Sheets(&#8220;伝票&#8221;).Cells(伝票下端行, 9))</span><br />
<br />
<span class="VBA_Tab1">Dim i</span><br />
<span class="VBA_Tab1">伝票範囲(1, 9) = &#8220;YJコード&#8221;</span><br />
<span class="VBA_Tab1">For i = 2 To 伝票下端行</span><br />
<span class="VBA_Tab2">伝票範囲(i, 9) = dictJANYJ.Item(伝票範囲(i, 4))</span><br />
<span class="VBA_Tab1">Next i</span><br />
<br />
<span class="VBA_Tab1">Range(Sheets(&#8220;伝票&#8221;).Cells(1, 1), Sheets(&#8220;伝票&#8221;).Cells(伝票下端行, 9)) = 伝票範囲</span><br />
<br />
<span class="VBA_Tab1">Debug.Print &#8220;終了&#8221; &#038; &#8220;_&#8221; &#038; Time <span class="VBA_Comment">’時間の測定</span></span><br />
<br />
End Sub</div>
<h2><span id="toc6">処理速度の差について集計</span></h2>
<p>①WorkSheetFunctionでVlookUp関数を呼び出して使用する方法、②今回の連想配列（Dictionaryオブジェクト）を使用する方法を比較してみました。</p>
<p>それぞれの方法において、VBA高速化で有名なのセル値を配列に格納して抽出・書き込む方法も試しています。</p>
<p>商品マスタシート・伝票シートいずれも行数は100,000行にして測定した速度結果は下記の通りです。</p>
<table border=1>
<tr>
<td align=center></td>
<th align=center>WorkSheetFunctionの<br />
VlookUp</th>
<th align=center>Dictionary<br />
オブジェクト</th>
</tr>
<tr>
<td align=center>セルの値を配列へ<br />
格納しない</td>
<td align=center>0:11:13</td>
<td align=center>0:00:32</td>
</tr>
<tr>
<td align=center>セルの値を配列へ<br />
格納する</td>
<td align=center>2:05:08</td>
<td align=center>0:00:04</td>
</tr>
</table>
<p>WorkSheetFunctionでVlookUp関数を用いた場合、配列をセルに格納することで逆に速度が著しく低下してしまったのは驚きでした。</p>
<p>原因としては私が実験した環境において、「セルの値を配列に格納することでメモリが上限に達してしまったこと」が推測されますが、詳細は不明です。</p>
<p>セルを配列に格納した際の速度アップはセルの参照・セルへの値の書き込みの部分が高速化されることによるのですが、WrorkSheetFunctionのVlookUp関数はそのものの処理が重く、セルの値の参照やセルへの書き込みが律速ではないようです。</p>
<p>そしていずれにしても①セルの値を配列に格納して②Dictionaryを使用するという両方のステップを行えば最も高速に処理が可能になることがわかりましたので、今後はこの方法を使っていこうと思います。<br />
11分かかる処理が4秒になるのはものすごい改善だと思います。</p>
<p>今回はVlookUpの代わりになるDictionaryオブジェクトを使用する方法をご紹介しました。<br />
最初のKeyとItemの値を格納する方法を変更すればSumIfsなどの代わりとしても使えるようになりますのでその方法については別の記事で紹介します。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/dictionary-vlookup/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VBAでOutlookのメールを作成する際に署名を表示させる方法</title>
		<link>https://officevba.info/outlooksignatures/</link>
					<comments>https://officevba.info/outlooksignatures/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 06 Sep 2020 12:57:08 +0000</pubDate>
				<category><![CDATA[Outlook操作]]></category>
		<category><![CDATA[Outlook]]></category>
		<category><![CDATA[メール作成署名]]></category>
		<category><![CDATA[ユーザー定義関数]]></category>
		<category><![CDATA[差し込み]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2173</guid>

					<description><![CDATA[目次 Outlookの署名をVBAで自動追加する方法が案外難しい署名の内容の呼び出しと取り込み方法について署名を追加した状態でOutlookメールを作成するExcelVBA Outlookの署名を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-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">Outlookの署名をVBAで自動追加する方法が案外難しい</a></li><li><a href="#toc2" tabindex="0">署名の内容の呼び出しと取り込み方法について</a></li><li><a href="#toc3" tabindex="0">署名を追加した状態でOutlookメールを作成するExcelVBA</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">Outlookの署名をVBAで自動追加する方法が案外難しい</span></h2>
<p>以前OutlookでメールをExcelVBAで大量に作成するコードを<a href="https://officevba.info/excelvbaoutlookmailpreparation4/">こちらの記事</a>でご紹介しました。</p>
<p>これは私が現在業務で最も使用しているコードの1つですが、使用した際に署名が反映されないという課題がありました。</p>
<p>この部分は手で付け足したり、もしくは本文に署名も付け加えていたりしたのですが、手で付け加えるのはそもそも面倒、本文に署名を付け加える場合、HTMLの仕様の問題だと思いますが、スペースの幅・数がずれることがありました。</p>
<p>そこで現在使用している署名を本文の下に追加した状態でメールを作成できるコードに改良してみました。</p>
<h2><span id="toc2">署名の内容の呼び出しと取り込み方法について</span></h2>
<p>署名はC:\ユーザー\[ユーザー名]\AppData\Roaming\Microsoft\Signaturesというフォルダにテキスト形式で保存されています。<br />
（AppDataが表示されない場合、隠しファイルを表示にチェックをして探してみてください。）</p>
<p>このファイルを読み込んで、内容を本文の下に追加するフローで署名を追加するコードを考えました。</p>
<h2><span id="toc3">署名を追加した状態でOutlookメールを作成するExcelVBA</span></h2>
<p>今回署名つきのコードを作成するにあたり、<a rel="noopener" href="https://www.it-swarm.dev/ja/vba/outlook%E3%81%A7%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E3%81%AE%E7%BD%B2%E5%90%8D%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/942649320/" target="_blank">こちら</a>のページを参考にさせていただきました。</p>
<p>もしかしたら外国の方が作成されているのかもしれません。<br />
少し日本語が変な気がするのでちょっと心配していましたが、きちんとした情報を提供してくださっていました。<br />
（疑ってスミマセン…）</p>
<p>メールを作成する部分のコードは以前ご紹介した<a href="https://officevba.info/excelvbaoutlookmailpreparation4/">Outlookの差し込みメールを作成するExcelVBA</a>を用いています。</p>
<div class="VBACode">Sub Outlook起動して署名つきメール作成する()<br />
<br />
<span class="VBA_Tab1">Dim oApp As New Outlook.Application</span><br />
<span class="VBA_Tab1">Dim oItem As Outlook.MailItem</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 DesktopPath As String</span><br />
<span class="VBA_Tab1">DesktopPath = WSH.SpecialFolders(&#8220;Desktop&#8221;)</span><br />
<br />
<span class="VBA_Tab1">Dim 本文 As String, 件名 As String</span><br />
<span class="VBA_Tab1">Dim 宛先列番号 As Long</span><br />
<span class="VBA_Tab1">Dim i, k, x</span><br />
<br />
<span class="VBA_Tab1">i = 7</span><br />
<span class="VBA_Tab1"></span><br />
<span class="VBA_Tab1">Do Until Cells(i, 2) = &#8220;&#8221;</span><br />
<span class="VBA_Tab2">If Cells(i, 21) = &#8220;&#8221; Then</span><br />
<span class="VBA_Tab3">件名 = Cells(2, 2).Text</span><br />
<span class="VBA_Tab3">本文 = Cells(3, 2).Text</span><br />
<span class="VBA_Tab3">k = 5</span><br />
<span class="VBA_Tab3">Do Until Cells(6, k) = &#8220;&#8221;</span><br />
<span class="VBA_Tab4">件名 = Replace(件名, &#8220;<" &#038; Cells(6, k) &#038; ">&#8220;, Cells(i, k))</span><br />
<span class="VBA_Tab4">本文 = Replace(本文, &#8220;<" &#038; Cells(6, k) &#038; ">&#8220;, Cells(i, k))</span><br />
<span class="VBA_Tab4">k = k + 1</span><br />
<span class="VBA_Tab3">Loop</span><br />
<br />
<span class="VBA_Tab3">本文 = 本文 &#038; vbLf &#038; vbLf &#038; 署名(&#8220;署名1.txt&#8221;)</span><br />
<span class="VBA_Tab3">本文 = Replace(本文, vbLf, &#8220;&lt;br /&gt;&#8221;)</span><br />
<br />
<span class="VBA_Tab3">Set oItem = oApp.CreateItem(olMailItem)</span><br />
<span class="VBA_Tab3"><span class="VBA_Comment">’oItem.SendUsingAccount = Session.Accounts(“指定する場合アカウント名を設定”)</span></span><br />
<br />
<span class="VBA_Tab3">oItem.To = Replace(Cells(i, 2), vbLf, &#8220;;&#8221;)</span><br />
<span class="VBA_Tab3">oItem.Subject = 件名</span><br />
<span class="VBA_Tab3">oItem.HTMLBody = &#8220;&#8221; &#038; 本文 &#038; &#8220;&#8221;</span><br />
<br />
<span class="VBA_Tab3">oItem.CC = Replace(Cells(i, 3), vbLf, &#8220;;&#8221;)</span><br />
<span class="VBA_Tab3">oItem.BCC = Replace(Cells(i, 4), vbLf, &#8220;;&#8221;)</span><br />
<span class="VBA_Tab3"><span class="VBA_Comment">’oItem.Importance = olImportanceHigh</span></span><br />
<br />
<span class="VBA_Tab3">Dim 添付()</span><br />
<span class="VBA_Tab3">x = 0</span><br />
<span class="VBA_Tab3">ReDim Preserve 添付(x)</span><br />
<span class="VBA_Tab3">添付(x) = Cells(i, 20).Text</span><br />
<br />
<span class="VBA_Tab3">If 添付(x) <> &#8220;&#8221; Then</span><br />
<span class="VBA_Tab4">Select Case InStr(添付(x), vbLf)</span><br />
<span class="VBA_Tab4">Case Is = 0</span><br />
<span class="VBA_Tab5">oItem.Attachments.Add DesktopPath &#038; &#8220;\&#8221; &#038; 添付(x)</span><br />
<span class="VBA_Tab4">Case Else</span><br />
<span class="VBA_Tab5">Do Until InStr(添付(x), vbLf) = 0</span><br />
<span class="VBA_Tab6">x = x + 1</span><br />
<span class="VBA_Tab6">ReDim Preserve 添付(x)</span><br />
<span class="VBA_Tab6">添付(x) = Mid(添付(x － 1), InStr(添付(x － 1), vbLf) + 1)</span><br />
<span class="VBA_Tab6">添付(x － 1) = Left(添付(x － 1), InStr(添付(x － 1), vbLf) － 1)</span><br />
<br />
<span class="VBA_Tab6">If 添付(x － 1) <> &#8220;&#8221; Then</span><br />
<span class="VBA_Tab7">oItem.Attachments.Add DesktopPath &#038; &#8220;\&#8221; &#038; 添付(x － 1)</span><br />
<span class="VBA_Tab6">End If</span><br />
<span class="VBA_Tab5">Loop</span><br />
<br />
<span class="VBA_Tab5">If 添付(x) <> &#8220;&#8221; Then</span><br />
<span class="VBA_Tab6">oItem.Attachments.Add DesktopPath &#038; &#8220;\&#8221; &#038; 添付(x)</span><br />
<span class="VBA_Tab5">End If</span><br />
<span class="VBA_Tab4">End Select</span><br />
<br />
<span class="VBA_Tab3">End If</span><br />
<br />
<span class="VBA_Tab3">oItem.Display <span class="VBA_Comment">’送信せずに画面を表示する</span></span><br />
<span class="VBA_Tab3"><span class="VBA_Comment">’oItem.Save ’下書き保存</span></span><br />
<span class="VBA_Tab3"><span class="VBA_Comment">’oItem.Close 0 ’閉じる</span></span><br />
<span class="VBA_Tab3"><span class="VBA_Comment">’oItem.Send ’送る場合</span></span><br />
<br />
<span class="VBA_Tab2">End If</span><br />
<br />
<span class="VBA_Tab1">i = i + 1</span><br />
<span class="VBA_Tab1">Loop</span><br />
<br />
End Sub<br />
<br />
Function 署名(ByVal 署名ファイル As String) As String<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 WSH As Object</span><br />
<span class="VBA_Tab1">Set WSH = CreateObject(&#8220;Wscript.Shell&#8221;)</span><br />
<br />
<span class="VBA_Tab1">Dim 署名フォルダ As String</span><br />
<span class="VBA_Tab1">署名フォルダ = WSH.SpecialFolders(&#8220;AppData&#8221;) &#038; &#8220;\Microsoft\Signatures&#8221;</span><br />
<br />
<span class="VBA_Tab1">Dim ts As Object</span><br />
<span class="VBA_Tab1">Set ts = FSO.GetFile(署名フォルダ &#038; &#8220;\&#8221; &#038; 署名ファイル).OpenAsTextStream(1, -2)</span><br />
<br />
<span class="VBA_Tab1">署名 = ts.readall</span><br />
<span class="VBA_Tab1">ts.Close</span><br />
<br />
<span class="VBA_Tab1">Set WSH = Nothing</span><br />
<span class="VBA_Tab1">Set FSO = Nothing</span><br />
<span class="VBA_Tab1">Set ts = Nothing</span><br />
<br />
End Function</div>
<p>テキストの内容を取得するためにはOpenAsTextStreamメソッドを使用しました。</p>
<p>引数の1つ目は「1」とすると読み取り専用で開きます。（2 = 書き込み用、8 = ファイルの末尾に書き込み用）<br />
引数の1つ目は「-2」とするとシステムの規定の設定でファイルを開きます。（-1 = Unicode形式、0 = Ascii形式）</p>
<p>どちらも意識せずに、OpenAsTextStream(1 , -2)で入力して良い気がします。</p>
<p>上記のコードを実行するとメールが作成され、署名が追加されていることを確認できました。<br />
下記に動作をキャプチャした動画を記載します。</p>
<div style="width: 1356px;" class="wp-video"><video class="wp-video-shortcode" id="video-2173-2" width="1356" height="763" preload="metadata" controls="controls"><source type="video/mp4" src="https://officevba.info/wp-content/uploads/2020/09/VBA151.mp4?_=2" /><a href="https://officevba.info/wp-content/uploads/2020/09/VBA151.mp4">https://officevba.info/wp-content/uploads/2020/09/VBA151.mp4</a></video></div>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/outlooksignatures/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://officevba.info/wp-content/uploads/2020/09/VBA151.mp4" length="0" type="video/mp4" />

			</item>
		<item>
		<title>Wordファイルの特定のページをPDF化するExcelVBA</title>
		<link>https://officevba.info/excelvbawordtopdf2/</link>
					<comments>https://officevba.info/excelvbawordtopdf2/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 19 Jul 2020 05:44:03 +0000</pubDate>
				<category><![CDATA[Word操作]]></category>
		<category><![CDATA[ExcelVBA]]></category>
		<category><![CDATA[PDF]]></category>
		<category><![CDATA[Word]]></category>
		<guid isPermaLink="false">http://officevba.info/?p=2165</guid>

					<description><![CDATA[目次 Word→PDFの繰り返しを自動化して楽にするWordファイルを分割してPDFにする手順作成したExcelワークシート作成したExcelVBAコード1.対象ファイルの取得（①）2.連続PDF化の実行（④） Word [&#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">Word→PDFの繰り返しを自動化して楽にする</a></li><li><a href="#toc2" tabindex="0">Wordファイルを分割してPDFにする手順</a></li><li><a href="#toc3" tabindex="0">作成したExcelワークシート</a></li><li><a href="#toc4" tabindex="0">作成したExcelVBAコード</a><ol><li><a href="#toc5" tabindex="0">1.対象ファイルの取得（①）</a></li><li><a href="#toc6" tabindex="0">2.連続PDF化の実行（④）</a></li></ol></li></ol>
    </div>
  </div>

<h2><span id="toc1">Word→PDFの繰り返しを自動化して楽にする</span></h2>
<p>以前に<a href="https://officevba.info/excelvbawordtopdf/">WordをPDFにするExcelVBAのコード</a>をご紹介しましたが、その時のコードはWordファイルの全ページをPDFにするものでした。</p>
<p>今回は同僚から「300ページくらいあるWordの一つのファイルを3-4ページ位ごとに分割してPDFにするのに何か楽にする方法がないか」と相談があり、負担を軽くするためのコードをk考えてみました。</p>
<h2><span id="toc2">Wordファイルを分割してPDFにする手順</span></h2>
<p>今回のツールもExcelVBAを使用して作成することにしました。<br />
まず対象となるWordファイルを開いて、指定したページごとに区切ってPDF化していくのを繰り返すフローを想定しています。</p>
<p>全ページをPDF化してそこからPDFファイルを分割する方法も考えましたが、PDFを分割するソフトが必要なのと、VBAでの完全自動化が難しそうだったのでやめました。<br />
（他のプログラムだと簡単にできるのかもしれませんが。）</p>
<h2><span id="toc3">作成したExcelワークシート</span></h2>
<p>今回作成したツールを使用するために必要な情報は下記のようにワークシートに記載するようにしています。</p>
<p>①対象フォルダ名・対象ファイル名を取得し、②変更後のPDFファイル名を記入、③印刷の開始位置、終了位置を必要な行だけ記入して、④PDF出力すると元ファイルと同じフォルダに作成したPDFファイルが格納されるフローとなります。</p>
<p><a href="https://officevba.info/wp-content/uploads/2020/07/vba150.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2020/07/vba150-300x76.jpg" alt="vba150" width="300" height="76" class="alignnone size-medium wp-image-2164" srcset="https://officevba.info/wp-content/uploads/2020/07/vba150-300x76.jpg 300w, https://officevba.info/wp-content/uploads/2020/07/vba150-700x178.jpg 700w, https://officevba.info/wp-content/uploads/2020/07/vba150-768x195.jpg 768w, https://officevba.info/wp-content/uploads/2020/07/vba150.jpg 1059w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<h2><span id="toc4">作成したExcelVBAコード</span></h2>
<h3><span id="toc5">1.対象ファイルの取得（①）</span></h3>
<p>対象ファイルをパス含めて手入力しても良いのですが、手間なのでファイルを選択すると自動で記入されるようにしてみました。<br />
下記のコードを実行すると、選択したファイルについて、A2セルにフォルダ名・A4セルにファイル名が入ります。</p>
<div class="VBACode">Sub Wordファイル名パス取得()<br />
<br />
<span class="VBA_Tab1">Dim FolderName As String</span><br />
<span class="VBA_Tab1">Dim FileName As String  <span class="VBA_Comment">’文字列を入れる変数として「FileName」を使う</span></span><br />
<span class="VBA_Tab2">FileName = Application.GetOpenFilename <span class="VBA_Comment">’ダイアログを用いて選択したファイルのフルパスを取得</span></span><br />
<br />
<span class="VBA_Tab1">FolderName = Left(FileName, InStrRev(FileName, &#8220;&#8221;) － 1)  <span class="VBA_Comment">’フォルダ名とファイル名に分離</span></span><br />
<span class="VBA_Tab1">FileName = Mid(FileName, InStrRev(FileName, &#8220;&#8221;) + 1)</span><br />
<br />
<span class="VBA_Tab1">ThisWorkbook.ActiveSheet.Cells(2, 1) = FolderName  <span class="VBA_Comment">’セルに記入</span></span><br />
<span class="VBA_Tab1">ThisWorkbook.ActiveSheet.Cells(4, 1) = FileName</span><br />
<br />
End Sub</div>
<h3><span id="toc6">2.連続PDF化の実行（④）</span></h3>
<p>連続PDF化のコードは下記の通りです。</p>
<p>実行前に②出力するファイル名を指定することと、③PDF化対象のページを指定しておく必要があります。</p>
<div class="VBACode">Sub wordファイルページ指定して印刷()<br />
<br />
<span class="VBA_Tab1">Dim objWord As Word.Application</span><br />
<span class="VBA_Tab2">Set objWord = CreateObject(&#8220;Word.Application&#8221;)</span><br />
<span class="VBA_Tab2">objWord.Visible = True</span><br />
<br />
<span class="VBA_Tab1">Dim objDoc As Word.Document</span><br />
<br />
<span class="VBA_Tab1">Dim FileName As String</span><br />
<span class="VBA_Tab1">Dim i</span><br />
<span class="VBA_Tab1">Dim 出力FileName As String</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’出力ファイル名の設定</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment">’拡張子が記入されていてもなくても対応できるように一旦削除</span></span><br />
<br />
<span class="VBA_Tab1">出力FileName = Cells(2, 1) &#038; &#8220;&#8221; &#038; Cells(4, 1)</span><br />
<span class="VBA_Tab1">出力FileName = Replace(出力FileName, &#8220;.pdf&#8221;, &#8220;&#8221;)</span><br />
<br />
<span class="VBA_Tab1">FileName = Cells(2, 1) &#038; &#8220;&#8221; &#038; Cells(4, 1)</span><br />
<span class="VBA_Tab1">Set objDoc = objWord.Documents.Open(FileName)</span><br />
<br />
<span class="VBA_Tab1">i = 7</span><br />
<span class="VBA_Tab1">Do While Cells(i, 1) <> &#8220;&#8221; <span class="VBA_Comment">’ 印刷対象のページがなくなるまで実行</span></span><br />
<span class="VBA_Tab3">objDoc.ExportAsFixedFormat OutputFileName:=出力FileName &#038; &#8220;-&#8221; &#038; Format(i － 6, &#8220;000&#8221;) &#038; &#8220;.pdf&#8221;, ExportFormat:=wdExportFormatPDF, _</span><br />
<span class="VBA_Tab5">    Range:=wdExportFromTo, From:=Cells(i, 1), To:=Cells(i, 3)</span><br />
<span class="VBA_Tab1">i = i + 1</span><br />
<span class="VBA_Tab1">Loop</span><br />
<br />
<span class="VBA_Tab1">objDoc.Close</span><br />
<span class="VBA_Tab1">Set objDoc = Nothing</span><br />
<br />
<span class="VBA_Tab1">objWord.Quit</span><br />
<span class="VBA_Tab1">Set objWord = Nothing</span><br />
<br />
End Sub</div>
<p>「ExportAsFixedFormat」の引数「Range」「From」「To」を指定することで印刷（PDF化）範囲を設定します。<br />
設定内容は下記の通りです。</p>
<li>Range:=wdExportFromTo　範囲指定の場合この記述が必須になります。</li>
<li>From:=Cells(i, 1)　各行の設定開始ページ</li>
<li>To:=Cells(i, 3)　 各行の設定終了ページ</li>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/excelvbawordtopdf2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ExcelVBAでWordファイルをPDF化する</title>
		<link>https://officevba.info/excelvbawordtopdf/</link>
					<comments>https://officevba.info/excelvbawordtopdf/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Mon, 29 Jun 2020 14:04:09 +0000</pubDate>
				<category><![CDATA[Word操作]]></category>
		<category><![CDATA[ExcelVBA]]></category>
		<category><![CDATA[PDF出力]]></category>
		<guid isPermaLink="false">http://officevba.info/?p=2147</guid>

					<description><![CDATA[目次 ファイルの数が多いと面倒な作業をVBAで自動化Excelのシートに対象のファイルを書き出す今回使用するワークシート作成したExcelVBAコードと実行手順について①ファイル名一覧の取得②PDFとして出力する際のファ [&#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">ファイルの数が多いと面倒な作業をVBAで自動化</a></li><li><a href="#toc2" tabindex="0">Excelのシートに対象のファイルを書き出す</a></li><li><a href="#toc3" tabindex="0">今回使用するワークシート</a></li><li><a href="#toc4" tabindex="0">作成したExcelVBAコードと実行手順について</a><ol><li><a href="#toc5" tabindex="0">①ファイル名一覧の取得</a></li><li><a href="#toc6" tabindex="0">②PDFとして出力する際のファイル名の記入</a></li><li><a href="#toc7" tabindex="0">③一覧に記載されているWordファイルを順にPDFとして出力する</a></li></ol></li></ol>
    </div>
  </div>

<h2><span id="toc1">ファイルの数が多いと面倒な作業をVBAで自動化</span></h2>
<p>最近仕事でWordのファイルを大量にPDFに変更する必要がありました。<br />
1つ2つくらいのファイルなら手作業で済ませるのですが、今回は50件行う必要があり、また他のタイミングでも継続的に業務として発生することが決まっていました。</p>
<p>そこで、（サラリーマンらしくこの作業は本当に必要あるのかは考えるのをやめて）VBAで自動化の方法を考えてみました。</p>
<h2><span id="toc2">Excelのシートに対象のファイルを書き出す</span></h2>
<p>WordのVBAは使いにくいですし、もともと大量のファイルを処理することが前提でしたので、ExcelのワークシートとExcelVBAを使用することにしています。</p>
<p>私の経験上この組み合わせはExcelのファイルを操作するだけでなく、色々な業務で使いやすいと思います。<br />
手順は下記の3ステップを想定しています。</p>
<li>①Excelのシートに対象とするファイル名とパスを書きだす</li>
<li>②出力するPDFのファイル名を設定する</li>
<li>③対象となるWordのファイルを開いて順にPDF化する</li>
<h2><span id="toc3">今回使用するワークシート</span></h2>
<p>今回使用するワークシートは下記の通りです。<br />
対象ファイルを特定のフォルダにまとめて格納しておけば一括でファイル名を取得できるようにしています。</p>
<p><a href="https://officevba.info/wp-content/uploads/2020/06/VBA148.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2020/06/VBA148-300x82.jpg" alt="" width="300" height="82" class="alignnone size-medium wp-image-2150" srcset="https://officevba.info/wp-content/uploads/2020/06/VBA148-300x82.jpg 300w, https://officevba.info/wp-content/uploads/2020/06/VBA148-700x192.jpg 700w, https://officevba.info/wp-content/uploads/2020/06/VBA148-768x211.jpg 768w, https://officevba.info/wp-content/uploads/2020/06/VBA148.jpg 1255w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<h2><span id="toc4">作成したExcelVBAコードと実行手順について</span></h2>
<h3><span id="toc5">①ファイル名一覧の取得</span></h3>
<p>まず①Wordファイル一覧を一括取得するExcelVBAコードは下記の通りです。<br />
実行するとファイルの選択ダイアログが開き、対象ファイルの一つを選択するとそのフォルダにあるWordファイルのパスとファイル名をA列に記入します。</p>
<div class="VBACode">Sub Wordファイル名一覧取得()<br />
<br />
<span class="VBA_Tab1">Dim FolderName As String  <span class="VBA_Comment">’文字列を入れる変数として「FolderName」を使う</span></span><br />
<span class="VBA_Tab1">Dim index As Integer  <span class="VBA_Comment">’数字を入れる変数として「index」を使う</span></span><br />
<span class="VBA_Tab1">Dim FileName As String  <span class="VBA_Comment">’文字列を入れる変数として「FileName」を使う</span></span><br />
<span class="VBA_Tab2">FolderName = Application.GetOpenFilename <span class="VBA_Comment">’ダイアログを用いて選択したファイルのパスをFolderNameとする①</span></span><br />
<span class="VBA_Tab1">Dim i</span><br />
<br />
<span class="VBA_Tab1">If FolderName = &#8220;False&#8221; Then  <span class="VBA_Comment">’FolderNameが選択されていなければ作業を終了する</span></span><br />
<span class="VBA_Tab2">Exit Sub</span><br />
<span class="VBA_Tab1">End If</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’今のフォルダ名には選択したファイル名含まれているので、ファイル名の部分を切り取る作業。</span></span><br />
<span class="VBA_Tab1">index = InStrRev(FolderName, &#8220;&#8221;)  <span class="VBA_Comment">’フォルダ名部分の文字数をカウントする</span></span><br />
<span class="VBA_Tab1">FolderName = Left(FolderName, index) <span class="VBA_Comment">’ カウントした文字数までの部分を切り取ってフォルダ名とする</span></span><br />
<span class="VBA_Tab1">FileName = Dir(FolderName &#038; &#8220;*docx&#8221;)  <span class="VBA_Comment">’ フォルダの中に含まれるファイルを取り出す</span></span><br />
<span class="VBA_Tab1">i = 2</span><br />
<br />
<span class="VBA_Tab1">Do While FileName <> &#8220;&#8221;  <span class="VBA_Comment">’ ファイルがなくなるまで繰り返す</span></span><br />
<span class="VBA_Tab2">ThisWorkbook.ActiveSheet.Cells(i, 1) = FolderName &#038; FileName <span class="VBA_Comment">’ 変更を行う</span></span><br />
<span class="VBA_Tab2">i = i + 1</span><br />
<span class="VBA_Tab1">FileName = Dir() <span class="VBA_Comment">’</span></span><br />
<span class="VBA_Tab1">Loop</span><br />
<br />
End Sub</div>
<h3><span id="toc6">②PDFとして出力する際のファイル名の記入</span></h3>
<p>出力先フォルダとファイル名はC列に入力してから出力を開始します。<br />
ExcelのSubstitute関数やVBAのReplace関数を使って自動で表記することも可能です。</p>
<p>一例ですが、デスクトップにある「Wordファイル」というフォルダから同じくデスクトップにある「PDFファイル」というフォルダに拡張子以外の部分を同じファイル名で出力する場合にはC2セルに下記のように関数を入力しておきます。<br />
（「.docx」を「.pdf」に置換、「Wordファイル」を「PDFファイル」に置換しているだけです。）</p>
<li>=SUBSTITUTE(SUBSTITUTE(A2,&#8221;.docx&#8221;,&#8221;.pdf&#8221;),&#8221;Wordファイル&#8221;,&#8221;PDFファイル&#8221;)</li>
<h3><span id="toc7">③一覧に記載されているWordファイルを順にPDFとして出力する</span></h3>
<p>WordファイルをPDFとして出力するコードは下記の通りです。<br />
実行するとC列に記載したフォルダ・ファイル名でPDFが出力されます。</p>
<div class="VBACode">Sub Wordファイル一括PDF変換()<br />
<br />
<span class="VBA_Tab1">Dim objWord As Word.Application</span><br />
<span class="VBA_Tab2">Set objWord = CreateObject(&#8220;Word.Application&#8221;)</span><br />
<span class="VBA_Tab2">objWord.Visible = True</span><br />
<br />
<span class="VBA_Tab1">Dim objDoc As Word.Document</span><br />
<br />
<span class="VBA_Tab1">Dim FileName As String</span><br />
<span class="VBA_Tab1">Dim i</span><br />
<br />
<span class="VBA_Tab1">i = 2</span><br />
<br />
<span class="VBA_Tab1">Do While Cells(i, 1) <> &#8220;&#8221; <span class="VBA_Comment">’ ファイルがなくなるまで繰り返す</span></span><br />
<span class="VBA_Tab2">FileName = Cells(i, 1)</span><br />
<span class="VBA_Tab2">Set objDoc = objWord.Documents.Open(FileName)</span><br />
<span class="VBA_Tab3">objDoc.ExportAsFixedFormat OutputFileName:=Cells(i, 3), ExportFormat:=wdExportFormatPDF</span><br />
<span class="VBA_Tab2">objDoc.Close</span><br />
<span class="VBA_Tab2">Set objDoc = Nothing</span><br />
<span class="VBA_Tab2">i = i + 1</span><br />
<span class="VBA_Tab1">Loop</span><br />
<br />
<span class="VBA_Tab1">objWord.Quit</span><br />
<span class="VBA_Tab1">Set objWord = Nothing</span><br />
<br />
End Sub</div>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/excelvbawordtopdf/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ワークシートの印刷設定を変更するExcelVBAコード</title>
		<link>https://officevba.info/pagesetup/</link>
					<comments>https://officevba.info/pagesetup/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 14 Jun 2020 23:26:55 +0000</pubDate>
				<category><![CDATA[シートの操作]]></category>
		<category><![CDATA[Excel]]></category>
		<category><![CDATA[印刷設定]]></category>
		<guid isPermaLink="false">http://officevba.info/?p=2142</guid>

					<description><![CDATA[目次 配布資料には印刷設定をしておくことが多い私が印刷設定で必要なこと印刷設定を行うExcelVBAのサンプルコード印刷設定を行う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">私が印刷設定で必要なこと</a></li><li><a href="#toc3" tabindex="0">印刷設定を行うExcelVBAのサンプルコード</a></li><li><a href="#toc4" tabindex="0">印刷設定を行うVBAコードのトラブル？エラーについて</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">配布資料には印刷設定をしておくことが多い</span></h2>
<p>私自身は紙でファイルを使う機会が少ないのですが、紙の文化が根強く残る業態のため取引先に送る資料には印刷設定をしておくことが多いです。</p>
<p>たくさんのファイルのたくさんのページに対して印刷設定をするのが面倒なのでExcelVBAで実行できる方法を調べてみました。</p>
<p>今回は印刷設定の機能のうち、私がよく使用するプロパティの部分をご紹介します。</p>
<h2><span id="toc2">私が印刷設定で必要なこと</span></h2>
<p>私が印刷範囲を設定する際に必要な項目は下記の通りです。</p>
<li>①印刷範囲の設定</li>
<li>②改ページ位置の変更（解除と設定）</li>
<li>③上下左右の余白の設定</li>
<li>④タイトル行の設定</li>
<li>⑤印刷の向き</li>
<li>⑥ページ数の設定（縦何ページ×横何ページに設定するか）</li>
<p>他にも設定できるものはたくさんありますが、とりあえずこれだけ抑えていれば私の普段使いには十分です。</p>
<h2><span id="toc3">印刷設定を行うExcelVBAのサンプルコード</span></h2>
<p>今回私が作成したExcelVBAコードは下記の通りです。</p>
<p>50行に1回改ページを入れる設定にしています。<br />
数字を変更すると他の設定も可能です。</p>
<div class="VBACode">Sub 印刷設定()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’印刷範囲の設定はA1形式なのでAddressを使う</span></span><br />
<span class="VBA_Tab1">ActiveSheet.PageSetup.PrintArea = Range(Cells(1, 1), Cells(Rows.Count, 6).End(xlUp)).Address</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’改ページをすべて削除</span></span><br />
<span class="VBA_Tab1">ActiveSheet.ResetAllPageBreaks</span><br />
<br />
<span class="VBA_Tab1">Dim i As Long</span><br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’50行に1回改ページを追加</span></span><br />
<span class="VBA_Tab1">For i = 2 To Cells(Rows.Count, 6).End(xlUp).Row</span><br />
<span class="VBA_Tab2">If i Mod 50 = 0 Then</span><br />
<span class="VBA_Tab3">ActiveSheet.HPageBreaks.Add before:=Cells(i + 1, 1)</span><br />
<span class="VBA_Tab2">End If</span><br />
<span class="VBA_Tab1">Next i</span><br />
<br />
<span class="VBA_Tab1">With ActiveSheet.PageSetup</span><br />
<br />
<span class="VBA_Tab2">.LeftMargin = Application.InchesToPoints(0)   <span class="VBA_Comment">’左の余白</span></span><br />
<span class="VBA_Tab2">.RightMargin = Application.InchesToPoints(0)  <span class="VBA_Comment">’右の余白</span></span><br />
<span class="VBA_Tab2">.TopMargin = Application.InchesToPoints(0)    <span class="VBA_Comment">’上の余白</span></span><br />
<span class="VBA_Tab2">.BottomMargin = Application.InchesToPoints(0) <span class="VBA_Comment">’下の余白</span></span><br />
<br />
<span class="VBA_Tab2">.Orientation = xlPortrait   <span class="VBA_Comment">’縦向き</span></span><br />
<span class="VBA_Tab2"><span class="VBA_Comment0">’.Orientation = xlLandscape <span class="VBA_Comment0">’横向き</span></span><br />
<br />
<span class="VBA_Tab2">.PaperSize = xlPaperA4  <span class="VBA_Comment">’A4サイズで印刷</span></span><br />
<span class="VBA_Tab2">.Order = xlDownThenOver <span class="VBA_Comment">’印刷の順序（今回は列側を1ページにしているので関係なし）</span></span><br />
<br />
<span class="VBA_Tab2">.PrintTitleRows = &#8220;$1:$1&#8221; <span class="VBA_Comment">’行のタイトル設定</span></span><br />
<br />
<span class="VBA_Tab2">.Zoom = False<span class="VBA_Comment">’用紙に合わせて拡大縮小をする場合Falseを指定</span><br />
<span class="VBA_Tab2">.FitToPagesWide = 1       <span class="VBA_Comment">’列側のページ数</span></span><br />
<span class="VBA_Tab2">.FitToPagesTall = False   <span class="VBA_Comment">’行のページ指定（指定しない場合、Falseを記入）</span></span><br />
<br />
<span class="VBA_Tab1">End With</span><br />
<br />
End Sub</div>
<h2><span id="toc4">印刷設定を行うVBAコードのトラブル？エラーについて</span></h2>
<p>印刷設定にはPrintCommunicationというプロパティがあり、これはプリンタとの通信を規定するものになります。</p>
<p>印刷設定を行う前に「Application.PrintCommunication = False」と記載しておいて、プリンタとの接続を切ってから印刷設定をし、最後に「Application.PrintCommunication = True」として印刷設定を戻すことで、処理が高速化されるとよく紹介されています。</p>
<p>私の普段業務を行っている環境で、このPrintCommunicationの設定をコードに入れていると、1つのファイルに印刷設定の処理をしているときは問題なかったのですが、フォルダの中に含まれているファイルにすべて印刷設定を行ったりするコードを記載すると印刷設定が反映されないトラブルがありました。</p>
<p>原因は特定できていませんが、PrintCommunicationのコードを削除することできちんと動作するようになりましたので、PrintCommunicationが影響しているのは間違いなさそうです。</p>
<p>今回の印刷設定のVBAコードはそもそも高速化が必要な処理ではないため、当面このコードについては使わないようにしていきたいと思います。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/pagesetup/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
