Closed kazurayam closed 3 months ago
POIのドキュメントを見たら VBAMacroReader というクラスがあると書いてあった。VBAMacoReader を実際に動かしてみようと思った。
新しいブランチ issue28 を作った。その中にテストコードをひとつ作った。
このテストを実行したらエラーになった。
The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)
org.apache.poi.poifs.filesystem.OfficeXmlFileException: The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)
at org.apache.poi.poifs.storage.HeaderBlock.<init>(HeaderBlock.java:128)
at org.apache.poi.poifs.storage.HeaderBlock.<init>(HeaderBlock.java:115)
at org.apache.poi.poifs.filesystem.POIFSFileSystem.<init>(POIFSFileSystem.java:270)
at org.apache.poi.poifs.filesystem.POIFSFileSystem.<init>(POIFSFileSystem.java:183)
at org.apache.poi.poifs.filesystem.POIFSFileSystem.<init>(POIFSFileSystem.java:166)
at com.kazurayam.vbastudy.GettingVBProjectNameTest.test_read_macros(GettingVBProjectNameTest.java:42)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.testng.internal.invokers.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:141)
at org.testng.internal.invokers.TestInvoker.invokeMethod(TestInvoker.java:686)
at org.testng.internal.invokers.TestInvoker.invokeTestMethod(TestInvoker.java:230)
at org.testng.internal.invokers.MethodRunner.runInSequence(MethodRunner.java:63)
at org.testng.internal.invokers.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:992)
at org.testng.internal.invokers.TestInvoker.invokeTestMethods(TestInvoker.java:203)
at org.testng.internal.invokers.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:154)
at org.testng.internal.invokers.TestMethodWorker.run(TestMethodWorker.java:134)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.testng.TestRunner.privateRun(TestRunner.java:739)
at org.testng.TestRunner.run(TestRunner.java:614)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:421)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:413)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:373)
at org.testng.SuiteRunner.run(SuiteRunner.java:312)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:95)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1274)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1208)
at org.testng.TestNG.runSuites(TestNG.java:1112)
at org.testng.TestNG.run(TestNG.java:1079)
at org.gradle.api.internal.tasks.testing.testng.TestNGTestClassProcessor.runTests(TestNGTestClassProcessor.java:148)
at org.gradle.api.internal.tasks.testing.testng.TestNGTestClassProcessor.stop(TestNGTestClassProcessor.java:95)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:62)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at jdk.proxy1/jdk.proxy1.$Proxy2.stop(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
VBAMacroReaderはまともに動く状態でないように思われた。
POIプロジェクトの Apache POI™ - HSSF and XSSF Limitations にこう書いてあった。
Macros
Macros can not be created. The are currently no plans to support macros. However, reading and re-writing files containing macros will safely preserve the macros. Recent versions of Apache POI support extracting the macro data via VBAMacroExtractor and VBAMacroReader
暗雲が垂れ込めてきた。
なぜPOIプロジェクトはxlsmファイルの中のMacroをまともに扱わないのだろうか?どんな困難があるというのか?
.xlsmファイルは実はzip形式のファイルであって、unzipツールで解凍することができる。中を見るとxxxxxx.xmlつまりXML形式のテキストファイルがいくつも格納されていることがわかる。ひとつ例としてこのxlsmファイルをunzipしてみた。
解凍すると次のようなファイルツリーが得られた。
~/tmp/out $ tree .
.
├── [Content_Types].xml
├── _rels
├── docProps
│ ├── app.xml
│ └── core.xml
└── xl
├── _rels
│ └── workbook.xml.rels
├── calcChain.xml
├── ctrlProps
│ └── ctrlProp1.xml
├── drawings
│ ├── drawing1.xml
│ └── vmlDrawing1.vml
├── printerSettings
│ ├── printerSettings1.bin
│ └── printerSettings2.bin
├── sharedStrings.xml
├── styles.xml
├── tables
│ └── table1.xml
├── theme
│ └── theme1.xml
├── vbaProject.bin
├── workbook.xml
└── worksheets
├── _rels
│ ├── sheet2.xml.rels
│ └── sheet5.xml.rels
├── sheet1.xml
├── sheet2.xml
├── sheet3.xml
├── sheet4.xml
└── sheet5.xml
12 directories, 23 files
この中に xl/vbaProject.bin
というファイルがある。きっとこの中にVB Projectが格納されているに違いない。開いてみよう .... 開けなかった。XMLテキストではなかった。バイナリファイルだった。
なるほど、xl/vbaProject.bin
がバイナリファイルなのでPOIプロジェクトはMacroを取り扱うことを投了したのだ。無理もないと思う。
結論: POIを使ったJavaプログラムがxlsmファイルの中のVBProjectのNameプロジェクトを読み出すことはできない。
26 から派生した。
Apache POIを使ってJavaコードがVBProjectのNameプロパティを読み出すことを実技で試してみよう。
下記の記事が手掛かりを与えてくれる
https://trycatchdebug.net/news/1307027/apache-poi-for-vba-macro-reading