kazurayam / vba-callgraph

Generating a Call Graph for Excel VBA workbooks
https://kazurayam.github.io/VBACallGraph/
Apache License 2.0
0 stars 0 forks source link

Excelワークブック「会費納入状況チェック_R6年度.xlsm」をApache POIで読んでVBProjectのNameプロパティを読み出せ、方法を解明せよ  #28

Closed kazurayam closed 3 months ago

kazurayam commented 3 months ago

26 から派生した。

Apache POIを使ってJavaコードがVBProjectのNameプロパティを読み出すことを実技で試してみよう。

下記の記事が手掛かりを与えてくれる

https://trycatchdebug.net/news/1307027/apache-poi-for-vba-macro-reading

kazurayam commented 3 months ago

POIのドキュメントを見たら VBAMacroReader というクラスがあると書いてあった。VBAMacoReader を実際に動かしてみようと思った。

新しいブランチ issue28 を作った。その中にテストコードをひとつ作った。

https://github.com/kazurayam/VBAProcedureUsageAnalyzer/blob/issue28/lib/src/test/java/com/kazurayam/vbastudy/GettingVBProjectNameTest.java

このテストを実行したらエラーになった。


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はまともに動く状態でないように思われた。

kazurayam commented 3 months ago

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

暗雲が垂れ込めてきた。

kazurayam commented 3 months ago

なぜPOIプロジェクトはxlsmファイルの中のMacroをまともに扱わないのだろうか?どんな困難があるというのか?

.xlsmファイルは実はzip形式のファイルであって、unzipツールで解凍することができる。中を見るとxxxxxx.xmlつまりXML形式のテキストファイルがいくつも格納されていることがわかる。ひとつ例としてこのxlsmファイルをunzipしてみた。

https://github.com/kazurayam/VBAProcedureUsageAnalyzer/blob/issue28/lib/src/test/fixture/hub/aogan-jimukyoku/office/%E4%BC%9A%E8%B2%BB%E7%B4%8D%E5%85%A5%E7%8A%B6%E6%B3%81%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF_R6%E5%B9%B4%E5%BA%A6.xlsm

解凍すると次のようなファイルツリーが得られた。

~/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を取り扱うことを投了したのだ。無理もないと思う。

kazurayam commented 3 months ago

結論: POIを使ったJavaプログラムがxlsmファイルの中のVBProjectのNameプロジェクトを読み出すことはできない。

24 を推進せよ。それが唯一の道だ。