タイガーラック クリエイティブブログ
2025
April
23

【完全保存版】Microsoft AccessをGit/GitHub管理する方法

はじめに

こんにちは。今回は Microsoft Accessのソースコードを出力し、GitやGitHubで管理する方法 をご紹介します。弊社でもMicrosoft Accessを使ったシステム開発を行っていますが、これまで以下のような課題がありました。

  • ソースコードを変更した際、「いつ」「誰が」「どこを」変更したのかが分からない
  • 過去のソースコードを遡って確認するのが難しい
  • Accessはもともと複数人での開発を前提としていないため、作業が属人化しやすい

このような課題を解消するため、Accessのソースコードを出力してバージョン管理できる方法を今回まとめました。実は、Accessのソースコードを出力する手段自体は以前から存在していました。しかし、ある大きな課題があり、GitやGitHubでの運用が難しい状況でした(詳細は後述します)。今回は、その課題も含めて解決する方法をご紹介します。今回の記事がAccess開発の効率化やチーム開発を視野に入れている方の参考になれば幸いです。

使用ツールの紹介

今回使用するのは、いげ太さんがGitHubで公開している ariawase というツールです。
このツールを使うことで、Microsoft Accessのソースコードをテキストファイルとして出力することができます。ダウンロード方法は2通りあります。リンク先はこちらです。

方法①:ZIPファイルで取得する

1.GitHubのリポジトリページで「Code」ボタンをクリック

2.「Download ZIP」を選択

3.任意のフォルダに解凍

 

②:Gitコマンドで取得する

Gitがインストールされている場合は、以下のコマンドでクローン可能です。

git clone https://github.com/vbaidiot/ariawase.git

ariawaseの初期フォルダ構成

解凍またはクローン直後のフォルダ構成は以下のようになっています:

ariawase
│  .gitattributes
│  .gitignore
│  build.bat
│  LICENSE.txt
│  README.md
│  vbac.wsf
│
└─src
    └─Ariawase.xlsm
        ├─ ArrayEx.cls
        ├─ Assert.bas
        ├─ Core.bas
        ├─ Ext.bas
        ├─ Func.cls
        ├─ IO.bas
        ├─ MonkeyTest.cls
        └─ Tuple.cls

フォルダ構成の整理(初期設定)

今回の用途に合わせて、以下のようにフォルダを整理してください:

1.src フォルダの中身を すべて削除

2.ariawase フォルダ内に bin フォルダを 新しく作成

3.以下のファイルを 削除

  • .gitattributes
  • .gitignore
  • build.bat
  • LICENSE.txt
  • README.md

最終的なフォルダ構成は以下のようになります。

ariawase
│  vbac.wsf
│
├─bin
└─src

問題点

ariawaseには1つ大きな課題があります。それは Microsoft Accessの「テーブル」が出力できない という点です。この課題に対して、@nonoshuさんがQiitaで紹介している ariawaseの改良版 を使うことで解決できます。このバージョンではテーブル情報の出力にも対応しています。ただし、この修正はariawase v0.6.0 に対するもので、最新版の v0.9.0 とは異なり、やや古いバージョンです。

そして、Accessのフォームやレポートにはもう1つ厄介な問題があります。何も変更していないのに、ソースコード出力時に差分が出てしまうことです。例えば

1.フォームを通常通り表示して、ソースコードを出力

2.右クリック → 「デザインビュー」に変更。何も変更せず、Ctrl + S で保存

3.フォームビューに戻す

4.再度ソースコードを出力

この手順を行うと、見た目はまったく同じフォームでも、出力されたソースコードには多くの差分が生まれてしまいます。この仕様が原因で、バージョン管理時に「何が本当に変わったのか」がわかりにくくなり、Gitでの運用が非常に煩雑になります。

解決編

Qiitaで@nonoshuさんが紹介している「ariawase」の改良版を最新版(v0.9.0)に反映させることは可能です。ただし、前述した通り、Accessでは「デザインビュー」で何も編集していないのに、ソースコード出力時に差分が発生する現象があります。これはGitでのバージョン管理において非常にやっかいです。

ネット上でもこの問題に対する明確な解決策は見つからず、自分で調査・検証したところ、以下のような項目で差分が生じる傾向があることがわかりました。

  • ChecksumLeft, Top などの値
  • PrtDevModePrtDevNames に続くバイナリデータ

これらの値は保存のたびに変化することがあり、特に規則性は見られませんでした。

原因(推測):チェックサムによる差分発生

この現象の明確な原因について、Microsoftからの公式な発表はありません。しかし、ファイルに含まれる Checksum という項目名から、チェックサムが差分発生の一因であると推測できます。

チェックサムとは、ファイルの整合性を確認するための値であり、ファイルの内容が変更されていなくても、保存操作を行うことで値が変化することがあります。その結果、Gitでは差分として認識されてしまうのです。

また、PrtDevModePrtDevNames の後に続く 0x から始まる値(16進数のバイナリ情報)も、保存時に変化しやすく、これも差分の原因になります。

解決策:常に一定の値で上書きする

そこで私が考えた解決策は、これらの不定な値を固定値で上書きすることです。これにより、保存するたびに変わることがなくなり、不要な差分を防ぐことができます。

具体的には以下のように処理します:

  • Checksum, Left, Top などの値は、=123456 に書き換える
  • PrtDevModePrtDevNames に続くバイナリ値は、
    =0x0000000000000000000000000000000000000000000000000000000000000000 に固定

実装方法

この処理は、ariawase ディレクトリ内の vbac.wsf に記述します。以下の場所にあるファイルを修正してください。そのほかに一部コードを修正している箇所がありますが、修正した理由はコメントをご覧ください。

ariawase/
├── vbac.wsf  ← ここを編集
├── bin/
└── src/
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<package>
    <comment>
        The vbac is not VBA compiler.
        Instead, this unsophisticated script frees VBA code from binary files.
        This script is distributed as part of the Ariawase library.

        The Project Page: https://github.com/vbaidiot/Ariawase
    </comment>
    <job id="">
        <?job error="true" debug="false"?>
        <runtime>
            <description></description>
            <example></example>
        </runtime>
        <resource id="HelpMessage">
            vbac (version 0.9.0)

            Usage: cscript vbac.wsf &lt;command&gt; [&lt;options&gt;]

            Commands:
            combine Import all VBComponents
            decombine Export all VBComponents
            clear Remove all VBComponents
            help Display this help message

            Options:
            /binary:&lt;dir&gt; Specify directory of macro-enabled Office files
            (default: bin)
            /source:&lt;dir&gt; Specify directory of source code files
            (default: src)
            /vbaproj Use .vbaproj file
            /dbcompact With Access DB compaction
        </resource>
        <script language="JScript">
<![CDATA[
// Enumerations:
//   Word   - http://msdn.microsoft.com/en-us/library/office/jj684104.aspx
//   Excel  - http://msdn.microsoft.com/en-us/library/office/ff838815.aspx
//   Access - http://msdn.microsoft.com/en-us/library/office/jj713155.aspx

// WdSaveFormat
var wdFormatDocument97 = 0 //.doc
var wdFormatTemplate97 = 1 //.dot
var wdFormatXMLDocumentMacroEnabled = 13 //.docm
var wdFormatXMLTemplateMacroEnabled = 15 //.dotm

// XlFileFormat
var xlExcel9795 = 43; //.xls 97-2003 format in Excel 2003 or prev
var xlExcel8    = 56; //.xls 97-2003 format in Excel 2007
var xlExcel12   = 50; //.xlsb
var xlOpenXMLWorkbookMacroEnabled = 52; //.xlsm

// AcNewDatabaseFormat
var acNewDatabaseFormatAccess2000 =  9; //.mdb
var acNewDatabaseFormatAccess2002 = 10; //.mdb
var acNewDatabaseFormatAccess2007 = 12; //.accdb

// AcSysCmdAction
var acSysCmdAccessVer = 7;

// AcObjectType
var acTable  = 0;
var acQuery  = 1;
var acForm   = 2;
var acReport = 3;
var acMacro  = 4;
var acModule = 5;

// AcExport/Import/Link
var acImport = 0;
var acExport = 1;
var acLink   = 2

// AcImportXMLOption
var acStructureOnly = 0;
var acStructureAndData = 1;
var acAppendData = 2;
// vbext_ct_* (ref: http://msdn.microsoft.com/en-us/library/office/gg264162.aspx)
var vbext_ct_StdModule   = 1;
var vbext_ct_ClassModule = 2;
var vbext_ct_MSForm      = 3;
var vbext_ct_Document    = 100;

// FileSystemObject (ref: http://msdn.microsoft.com/en-us/library/hww8txat%28v=vs.84%29.aspx)
var fso = WScript.CreateObject("Scripting.FileSystemObject");

var forReading   = 1;
var forWriting   = 2;
var forAppending = 8;

var scriptPath = WScript.ScriptFullName;

var args = (function() {
    var a = new Array(WScript.Arguments.length);
    for (var i = 0; i < a.length; i++) a[i] = WScript.Arguments.item(i);
    return a;
}());

var getResource = function(str) {
    return scriptlet.getResource(str).replace(/^\s+|\s+$/g, "");
};

var println = function(str) {
    WScript.Echo(str);
};

var foreachEnum = function(collection, callback) {
    for ( var xs=new Enumerator(collection), x=xs.item(), i=0;
          !xs.atEnd();
          xs.moveNext(), x=xs.item(), i++
        ) {
        
        if (!!callback(x, i)) break;
    }
}

var dateTimeString = function(dt) {
    var g = function(y) { return (y < 2000) ? 1900 + y : y; };
    var f = function(n) { return (n < 10) ? "0" + n : n.toString(); };
    var ymd = g(dt.getYear())  + f(dt.getMonth() + 1) + f(dt.getDate());
    var hns = f(dt.getHours()) + f(dt.getMinutes())   + f(dt.getSeconds());
    return ymd + " " + hns;
};

var typename = function(obj) {
    if (obj === undefined) return 'Undefined';
    if (obj == null) return 'Null';
    return Object.prototype.toString.call(obj).slice(8, -1);
};

var isPathRooted = function(path) {
    if (!path) return false;
    
    var p1 = path.substring(0, 1);
    if (p1 == '\\' || p1 == '/') return true;
    var p2 = path.substring(1, 2);
    if (p2 == ':') return true;
    
    return false;
};

var Conditional = function(val) {
    this.flag  = false;
    this.value = val;
};
Conditional.prototype.change = function(val) {
    this.flag  = true;
    if (val !== undefined) this.value = val;
};

var CmdParam = function(paramObj) {
    this.defaultParameterName = paramObj.defaultParameterName;
    delete paramObj.defaultParameterName;
    this.parameters = paramObj;
    
    this.paramNames = {};
    for (var pname in this.parameters)
        this.paramNames[pname.toLowerCase()] = pname;
};
CmdParam.prototype.exists = function(paramName) {
    return paramName.toLowerCase() in this.paramNames;
};
CmdParam.prototype.get = function(paramName) {
    var pname = this.paramNames[paramName.toLowerCase()];
    return this.parameters[pname];
};
CmdParam.prototype.set = function(paramName, value) {
    var pname = this.paramNames[paramName.toLowerCase()];
    this.parameters[pname] = value;
};
CmdParam.prototype.setParam = function(pname, arg) {
    // the 'Object' assumed Conditional class
    switch (typename(this.get(pname))) {
    case 'Boolean':
        if (arg === undefined) arg = true;
        this.set(pname, Boolean(arg));
        break;
    case 'Number':
        this.set(pname, Number(arg));
        break;
  //case 'Date':
  //    this.set(pname, Date.parse(arg));
  //    break;
    case 'Object':
        this.get(pname).change(arg); //FIXME: type of value is string only?
        break;
    case 'Array':
        this.get(pname).push(arg);   //FIXME: type of value is string only?
        break;
    case 'Undefined':
        break;
    default:
        this.set(pname, arg || "");
        break;
    }
};
CmdParam.prototype.parse = function(args) {
    var pname = this.defaultParameterName;
    for (var i = 0; i < args.length; i++) {
        var value = undefined;
        
        switch (args[i].charAt(0)) {
        case '-': case '/':
            pname = args[i].substring(1);
            var j = -1;
            if (j < 0) j = pname.indexOf(':');
            if (j < 0) j = pname.indexOf('=');
            if (j > -1) {
                value = pname.substring(j + 1);
                pname = pname.substring(0, j);
            }
            break;
        default:
            value = args[i];
            break;
        }
        
        this.setParam(pname, value);
    }
    
    return this.parameters;
};

var Config = function(binary, source, binbak) {
    var root = fso.GetParentFolderName(scriptPath);
    this.bin = (isPathRooted(binary)) ? binary : fso.BuildPath(root, binary);
    this.src = (isPathRooted(source)) ? source : fso.BuildPath(root, source);
    this.bak = (!binbak.flag) ? undefined
             : (isPathRooted(binbak.value)) ? binbak.value
             : fso.BuildPath(root, binbak.value);
};
Config.prototype.getBins = function() { return fso.GetFolder(this.bin).Files; };
Config.prototype.getSrcs = function() { return fso.GetFolder(this.src).SubFolders; };

var VBAProjFile = function(vbproj, srcdir) {
    this.vbproj   = vbproj;
    this.fileName = 'App.vbaproj';
    this.path     = fso.BuildPath(srcdir, this.fileName);
};
VBAProjFile.prototype.projPropName = {
    'Name': 1, 'Description': 1, 'HelpFile': 1, 'HelpContextID': 1
};
VBAProjFile.prototype.removeAllRefs = function() {
    var self = this;
    foreachEnum(this.vbproj.References, function(ref) {
        if (ref.BuiltIn) return false;
        self.vbproj.References.Remove(ref);
    });
};
VBAProjFile.prototype.read = function(is64BitOffice) {
    var isSection = function(line) { return line.match(/^\[.*\]$/) != null; };
    var getParam  = function(line) {
        var i = line.indexOf('=');
        return (i > -1) ? { key: line.substring(0, i), val: line.substring(i+1) } : null;
    };
    
    if (!fso.FileExists(this.path)) return;
    
    var fl = fso.OpenTextFile(this.path, forReading);
    while (!fl.AtEndOfStream) {
        var line    = fl.ReadLine();
        var section = line;
        
        switch (section) {
        case '[General]':
            while (!fl.AtEndOfStream) {
                line = fl.ReadLine();
                if (isSection(line)) break;
                
                var p = getParam(line);
                // quick-fix solution
                if (is64BitOffice && p.key == "HelpContextID")
                    println("! Warning: can not 'VBProject.HelpContextID = \"" + p.val + "\"'. probably 64-bit Office have a bug.");
                else
                    this.vbproj[p.key] = p.val;
            }
            break;
        case '[Reference]':
            this.removeAllRefs();
            
            while (!fl.AtEndOfStream) {
                line = fl.ReadLine();
                if (isSection(line)) break;
                
                var p = getParam(line);
                var refinf = p.key.split(" ");
                this.vbproj.References.AddFromGuid(refinf[0], refinf[1], refinf[2]);
            }
            break;
        default:
            break;
        }
    }
};
VBAProjFile.prototype.write = function() {
    var fl = fso.OpenTextFile(this.path, forWriting, true);
    
    fl.WriteLine('[General]');
    for (var prop in this.projPropName)
        fl.WriteLine(prop + "=" + this.vbproj[prop]);
    
    fl.WriteLine('[Reference]');
    foreachEnum(this.vbproj.References, function(ref) {
        if (ref.BuiltIn) return false;
        fl.WriteLine(ref.GUID + " " + ref.Major + " " + ref.Minor + "=" + ref.Description);
    });
    
    fl.Close();
};

var Office = function() {};
Office.prototype.progID1 = undefined;
Office.prototype.progID2 = "Application";
Office.prototype.getProgID = function() {
    return (this.progID1 !== undefined) ? this.progID1 + "." + this.progID2 : undefined;
};
Office.prototype.setCmdParam = function(cmdParam) {
    this.cmdParam = cmdParam;
};
Office.prototype.isDirectiveOnly = function(codeModule) {
    var ml = codeModule.CountOfLines;
    var dl = codeModule.CountOfDeclarationLines;
    if (ml > dl) return false;
    if (ml < 1)  return true;
    for (var i=0,arr=codeModule.Lines(1, dl).split("\r\n"),len=arr.length; i<len; i++) {
        var s = arr[i].replace(/^\s+|\s+$/g, "");
        if (s != "" && s.charAt(0).toLowerCase() != "o") return false;
    }
    return true;
};
Office.prototype.isValidFileName = function(fname) {
    return fname.match(/[\\/:\*\?"<>\|]/) == null;
};
Office.prototype.loanOfOfficeDocument = function(path, isCreate, callback) {
    throw "Not Implemented";
};
Office.prototype.checkMacroSecurity = function(ofDoc) {
    try {
        ofDoc.VBProject;
    }
    catch (ex) {
        switch (ex.number) {
        case -2146822220:
            ex.description = [ex.description, "See also http://support.microsoft.com/kb/282830"].join("\n");
            break;
        case -2146827284:
            ex.description = [ex.description, "See also http://support.microsoft.com/kb/813969"].join("\n");
            break;
        default:
            break;
        }
        
        throw ex;
    }
};
Office.prototype.is64Bit = function(ofApp) {
    // ref: http://support.microsoft.com/kb/2186281
    return parseInt(ofApp.Version) >= 14
        && ofApp.ProductCode.substring(20, 21) == "1";
};
Office.prototype.extensionTypeTable = (function() {
    var tbl = {};
    tbl['bas'] = vbext_ct_StdModule;
    tbl['cls'] = vbext_ct_ClassModule;
    tbl['frm'] = vbext_ct_MSForm;
    tbl['frx'] = vbext_ct_MSForm;
    tbl['dcm'] = vbext_ct_Document;
    return tbl;
})();
Office.prototype.typeExtensionTable = (function () {
    var tbl = {};
    tbl[vbext_ct_StdModule]   = 'bas';
    tbl[vbext_ct_ClassModule] = 'cls';
    tbl[vbext_ct_MSForm]      = 'frm'; // with 'frx'
    tbl[vbext_ct_Document]    = 'dcm';
    return tbl;
})();
Office.prototype.addTargetType = function(typ) {
};
Office.prototype.cleanupBinary = function(ofDoc, verbose) {
    var compos = ofDoc.VBProject.VBComponents;
    var self   = this;
    foreachEnum(compos, function(compo) {
        var bname = compo.Name;
        //if (!(compo.Type.toString() in self.typeExtensionTable)) return false;
        if (compo.Type == vbext_ct_Document) {
            if (self.isDirectiveOnly(compo.CodeModule)) return false;
            compo.CodeModule.DeleteLines(1, compo.CodeModule.CountOfLines);
        }
        else {
            compos.Remove(compo);
        }
        if (!!verbose) println("- Remove: " + bname);
    });
};
Office.prototype.cleanupSource = function(dir, verbose) {
    if (!fso.FolderExists(dir)) {
         fso.CreateFolder(dir);
         return;
    }
    
    var self = this;
    foreachEnum(fso.GetFolder(dir).Files, function(fl) {
        var fname = fso.GetFileName(fl.Path);
        var xname = fso.GetExtensionName(fl.Path);
        if (!(xname in self.extensionTypeTable)) return false;
        
        fl.Delete();
        if (!!verbose) println("- Remove: " + fname);
    });
};
Office.prototype.importComponent = function(path, ofDoc) {
    var compos = ofDoc.VBProject.VBComponents;
    compos.Import(path);
};
Office.prototype.importDocument = function(path, ofDoc) {
    throw "Not Implemented";
};
Office.prototype.importSource = function(impdir, ofDoc) {
    var self = this;
    foreachEnum(fso.GetFolder(impdir).Files, function(fl) {
        var xname = fso.GetExtensionName(fl.Path);
        var bname = fso.GetBaseName(fl.Path);
        if (!(xname in self.extensionTypeTable)) return false;
        if (xname == 'frx') return false;
        
        if (xname != 'dcm')
            self.importComponent(fl.Path, ofDoc);
        else
            self.importDocument(fl.Path, ofDoc);
        
        println("- Import: " + fso.GetFileName(fl.Path));
        if (xname == 'frm') println("- Import: " + bname + ".frx");
    });
};
Office.prototype.importProject = function(impdir, vbproj, is64BitOffice) {
    var proj = new VBAProjFile(vbproj, impdir);
    if (fso.FileExists(proj.path)) {
        proj.read(is64BitOffice);
        println("- Import: " + proj.fileName);
    }
};
Office.prototype.exportSource = function(ofDoc, expdir) {
    var self = this;
    foreachEnum(ofDoc.VBProject.VBComponents, function(compo) {
        //if (!(compo.Type.toString() in self.typeExtensionTable)) return false;
        if (compo.Type == vbext_ct_Document) {
            if (self.isDirectiveOnly(compo.CodeModule)) return false;
        }
        
        var xname = self.typeExtensionTable[compo.Type.toString()];
        var bname = compo.Name;
        var fname = bname + "." + xname;
        compo.Export(fso.BuildPath(expdir, fname));
        
        println("- Export: " + fname);
        if (xname == 'frm') println("- Export: " + bname + ".frx");
    });
};
Office.prototype.exportProject = function(vbproj, expdir) {
    var proj = new VBAProjFile(vbproj, expdir)
    proj.write();
    println("- Export: " + proj.fileName);
};
Office.prototype.combine = function(tsrc, tbin) {
    println("> Target: " + fso.GetFileName(tbin));
    
    var self = this;
    this.loanOfOfficeDocument(tbin, true, function(ofDoc) {
        self.cleanupBinary(ofDoc);
        if (self.cmdParam.vbaproj) {
            var is64BitOffice = self.is64Bit(ofDoc.Application);
            self.importProject(tsrc, ofDoc.VBProject, is64BitOffice);
        }
        self.importSource(tsrc, ofDoc);
        ofDoc.Save();
    });
    
    println();
};
Office.prototype.decombine = function(tbin, tsrc) {
    println("> Target: " + fso.GetFileName(tbin));
    
    var self = this;
    this.loanOfOfficeDocument(tbin, false, function(ofDoc) {
        self.cleanupSource(tsrc);
        if (self.cmdParam.vbaproj) self.exportProject(ofDoc.VBProject, tsrc);
        self.exportSource(ofDoc, tsrc);
    });
    
    println();
};
Office.prototype.clear = function(tbin) {
    println("> Target: " + fso.GetFileName(tbin));
    
    var self = this;
    this.loanOfOfficeDocument(tbin, false, function(ofDoc) {
        self.cleanupBinary(ofDoc, true);
        ofDoc.Save();
    });
    
    println();
};

var Dummy = function() {};
Dummy.prototype = new Office();
Dummy.prototype.combine   = function() {};
Dummy.prototype.decombine = function() {};
Dummy.prototype.clear     = function() {};

var Word = function() {};
Word.prototype = new Office();
Word.prototype.progID1 = "Word";
Word.prototype.createOpenFile = function(wdApp, path) {
    var wdSaveFormat;
    var vernum = parseInt(wdApp.Version);
    switch (fso.GetExtensionName(path).toLowerCase()) {
    case 'doc':  wdSaveFormat = wdFormatDocument97;
                 break;
    case 'dot':  wdSaveFormat = wdFormatTemplate97;
                 break;
    case 'docm': wdSaveFormat = wdFormatXMLDocumentMacroEnabled;
                 break;
    case 'dotm': wdSaveFormat = wdFormatXMLTemplateMacroEnabled;
                 break;
    default:     wdSaveFormat = (vernum < 12) ? wdFormatDocument97 : wdFormatXMLDocumentMacroEnabled;
                 path        += (vernum < 12) ? '.doc'             : '.docm';
                 break;
    }
    
    var wdDoc;
    try {
        if (fso.FileExists(path)) {
            wdDoc = wdApp.Documents.Open(path)
        }
        else {
            wdDoc = wdApp.Documents.Add();
            wdDoc.SaveAs(path, wdSaveFormat);
        }
    }
    catch (ex) {
        if (wdDoc != null) wdDoc.Close();
        throw ex;
    }
    return wdDoc;
};
Word.prototype.loanOfOfficeDocument = function(path, isCreate, callback) {
    var wdApp, wdDoc, ret;
    
    try {
        wdApp = new ActiveXObject(this.getProgID());
        wdApp.DisplayAlerts = false;
        //wdApp.EnableEvents  = false; //In Word, Application class does not have this property
    try {
        wdDoc = (isCreate) ? this.createOpenFile(wdApp, path) : wdApp.Documents.Open(path);
        this.checkMacroSecurity(wdDoc);
        
        ret = callback(wdDoc);
    } finally { if (wdDoc != null) wdDoc.Close(); }
    } finally { if (wdApp != null) wdApp.Quit();  }
    
    return ret;
};
Word.prototype.importDocument = function(path, wdDoc) {
    var compos = wdDoc.VBProject.VBComponents;
    var impCompo = compos.Import(path);
    
    var origCompo;
    var cname=impCompo.Name, bname=fso.GetBaseName(path);
    if (cname != bname) {
        origCompo = compos.item(bname);
    }
    else {
        var doc = wdDoc.Documents.Add();
        compos  = wdDoc.VBProject.VBComponents; // refresh Component collection
        origCompo = compos.item(doc.CodeName);
        
        var tmpname = "ImportTemp";
        var find = function(compos, name) {
            var ret = false;
            foreachEnum(compos, function(c) { return ret = (c.Name == name); });
            return ret;
        };
        while (find(compos, tmpname)) tmpname += "1";
        
        impCompo.Name  = tmpname;
        origCompo.Name = cname;
    }
    
    var imod=impCompo.CodeModule, omod=origCompo.CodeModule;
    omod.DeleteLines(1, omod.CountOfLines);
    omod.AddFromString(imod.Lines(1, imod.CountOfLines));
    compos.Remove(impCompo);
};

var Excel = function() {};
Excel.prototype = new Office();
Excel.prototype.progID1 = "Excel";
Excel.prototype.createOpenFile = function(xlApp, path) {
    var xlFileFormat;
    var vernum = parseInt(xlApp.Version);
    switch (fso.GetExtensionName(path)) {
    case 'xls':  case 'xla':  case 'xlt':
        xlFileFormat = (vernum < 12) ? xlExcel9795 : xlExcel8;
        break;
    case 'xlsb':
        xlFileFormat = xlExcel12;
        break;
    case 'xlsm': case 'xlam': case 'xltm':
        xlFileFormat = xlOpenXMLWorkbookMacroEnabled;
        break;
    default:
        xlFileFormat = (vernum < 12) ? xlExcel9795 : xlOpenXMLWorkbookMacroEnabled;
        path        += (vernum < 12) ? '.xls'      : '.xlsm';
        break;
    }
    
    var xlBook;
    try {
        if (fso.FileExists(path)) {
            xlBook = xlApp.Workbooks.Open(path);
        }
        else {
            xlBook = xlApp.Workbooks.Add();
            xlBook.SaveAs(path, xlFileFormat);
        }
    }
    catch (ex) {
        if (xlBook != null) xlBook.Close();
        throw ex;
    }
    return xlBook;
};
Excel.prototype.loanOfOfficeDocument = function(path, isCreate, callback) {
    var xlApp, xlBook, ret;
    
    try {
        xlApp = new ActiveXObject(this.getProgID());
        xlApp.DisplayAlerts = false;
        xlApp.EnableEvents  = false;
    try {
        xlBook = (isCreate) ? this.createOpenFile(xlApp, path) : xlApp.Workbooks.Open(path);;
        this.checkMacroSecurity(xlBook);
        
        ret = callback(xlBook);
    } finally { if (xlBook != null) xlBook.Close(); }
    } finally { if (xlApp  != null) xlApp.Quit();   }
    
    return ret;
};
Excel.prototype.importDocument = function(path, xlBook) {
    var compos = xlBook.VBProject.VBComponents;
    var impCompo = compos.Import(path);
    
    var origCompo;
    var cname=impCompo.Name, bname=fso.GetBaseName(path);
    if (cname != bname) {
        origCompo = compos.item(bname);
    }
    else {
        var sht = xlBook.Worksheets.Add();
        compos  = xlBook.VBProject.VBComponents; // refreash Component collection
        origCompo = compos.item(sht.CodeName);
        
        var tmpname = "ImportTemp";
        var find = function(compos, name) {
            var ret = false;
            foreachEnum(compos, function(c) { return ret = (c.Name == name); });
            return ret;
        };
        while (find(compos, tmpname)) tmpname += "1";
        
        impCompo.Name  = tmpname;
        origCompo.Name = cname;
    }
    
    var imod=impCompo.CodeModule, omod=origCompo.CodeModule;
    omod.DeleteLines(1, omod.CountOfLines);
    omod.AddFromString(imod.Lines(1, imod.CountOfLines));
    compos.Remove(impCompo);
};

var Outlook = function() {};
Outlook.prototype = new Office();
Outlook.prototype.sadNews = function(tbin) {
    var notSupported =
        "Unfortunately, Outlook does not support access to VBA project from the outside.\n"
        + "See also http://support.microsoft.com/kb/290779.";
    
    println("> Target: " + fso.GetFileName(tbin));
    println(notSupported);
    println();
};
Outlook.prototype.combine   = function(tsrc, tbin) { this.sadNews(tbin); };
Outlook.prototype.decombine = function(tbin, tsrc) { this.sadNews(tbin); };
Outlook.prototype.clear     = function(tbin)       { this.sadNews(tbin); };

var Access = function() {};
Access.prototype = new Office();
Access.prototype.progID1 = "Access";
Access.prototype.createOpenFile = function(acApp, path) {
    var dbFormat;
    var vernum = parseInt(acApp.SysCmd(acSysCmdAccessVer));
    switch (fso.GetExtensionName(path).toLowerCase()) {
    case 'mdb':   dbFormat = acNewDatabaseFormatAccess2000;
                  break;
    case 'accdb': dbFormat = acNewDatabaseFormatAccess2007;
                  break;
    default:      dbFormat = (vernum < 12) ? acNewDatabaseFormatAccess2002 : acNewDatabaseFormatAccess2007;
                  path    += (vernum < 12) ? '.mdb'                        : '.accdb';
                  break;
    }
    
    if (!fso.FileExists(path))
        acApp.NewCurrentDatabase(path, dbFormat);
    else
        acApp.OpenCurrentDatabase(path);
    
    return path;
};
Access.prototype.getDbProperty = function(db, propName) {
    var prop = undefined;
    try { prop = db.Properties(propName); }
    catch (e) {}
    return prop;
};
Access.prototype.loanOfAcProj = function(path, isCreate, callback) {
    var acApp, acDb, ret;
    
    try {
        acApp = new ActiveXObject(this.getProgID());
        acApp.Visible = false;
    try {
        if (!!path) {
            if (isCreate)
                this.createOpenFile(acApp, path);
            else
                acApp.OpenCurrentDatabase(path);
        }
        
        acDb = acApp.CurrentDB();
        var startUp = this.getDbProperty(acDb, "StartUpForm");
        if (startUp !== undefined) acApp.DoCmd.Close(acForm, startUp.Value);
        
        ret = callback(acApp.CurrentProject);
    } finally { if (acDb  != null) acDb.Close(); }
    } finally { if (acApp != null) { if (acDb != null) acApp.CloseCurrentDatabase(); acApp.Quit(); } }
    
    return ret;
};
Access.prototype.extensionTypeTable = (function() {
    var tbl = {};
    tbl['mdl'] = acModule;
    tbl['bas'] = acModule;
    tbl['cls'] = acModule;
    tbl['frm'] = acForm;
    tbl['rpt'] = acReport;
    tbl['mcr'] = acMacro;
    tbl['xml'] = acTable;
    tbl['ltb'] = acTable;// is short name of "Linked Table"
    return tbl;
})();
Access.prototype.typeExtensionTable = (function() {
    var tbl = {};
    tbl[acModule] = 'mdl'; // rename 'bas' or 'cls'
    tbl[acForm]   = 'frm';
    tbl[acReport] = 'rpt';
    tbl[acMacro]  = 'mcr';
    tbl[acTable]  = 'xml'; // rename 'ltb'is short name of "Linked Table"
    return tbl;
})();
Access.prototype.addTargetType = function(acTyp) {
    var ext = undefined;
    switch (acTyp) {
    case acQuery: ext = 'qry'; break;
    default: break;
    }
    
    if (ext !== undefined) {
        this.extensionTypeTable[ext] = acTyp;
        this.typeExtensionTable[acTyp] = ext;
    }
};
Access.prototype.iterAllObjects = function(acApp, action) {
    var i;
    var objs = new Array();
    
    var acProj = acApp.CurrentProject;
    for (i = 0; i < acProj.AllModules.Count; i++) objs.push(acProj.AllModules.item(i));
    for (i = 0; i < acProj.AllForms.Count;   i++) objs.push(acProj.AllForms.item(i));
    for (i = 0; i < acProj.AllReports.Count; i++) objs.push(acProj.AllReports.item(i));
    for (i = 0; i < acProj.AllMacros.Count;  i++) objs.push(acProj.AllMacros.item(i));
    
    var acData = acApp.CurrentData;
    for (i = 0; i < acData.AllQueries.Count; i++) objs.push(acData.AllQueries.item(i));
    for (i = 0; i < acData.AllTables.Count; i++) {
	if (acData.AllTables.item(i).Name.substr(0,4)!=="MSys")	objs.push(acData.AllTables.item(i));
    }
    for (i = 0; i < objs.length; i++) {
        if (!!action(objs[i], i)) break;
    }
};
Access.prototype.getAcDocs = function(acApp) {
    var acDb;
    try {
        acDb = acApp.CurrentDB();
        var docs = new Array();
        var conts = ['Modules', 'Forms', 'Reports', 'Scripts'];
        var c2eTable = this.containerExtensionTable;
        for (var i = 0; i < conts.length; i++) {
            var cont = conts[i];
            foreachEnum(acDb.Containers(cont).Documents, function(doc) {
                docs.push({ container: cont, name: doc.Name });
            });
        }
    } finally { if (acDb != null) acDb.Close(); }
    
    return docs;
};
Access.prototype.cleanupBinary = function(acProj, verbose) {
    var acApp = acProj.Application;
    var self  = this;
    this.iterAllObjects(acApp, function(obj) {
        var name = obj.Name;
        if (!(obj.Type.toString() in self.typeExtensionTable)) return false;
        
        acApp.DoCmd.DeleteObject(obj.Type, name);
        if (!!verbose) println("- Remove: " + name);
    });
};
Access.prototype.cleanupSource = function(dir, verbose) {
    if (!fso.FolderExists(dir)) {
         fso.CreateFolder(dir);
         return;
    }
    
    var self = this;
    foreachEnum(fso.GetFolder(dir).Files, function(fl) {
        var fname = fso.GetFileName(fl.Path);
        var xname = fso.GetExtensionName(fl.Path);
        if (!(xname in self.extensionTypeTable)) return false;
        
        fl.Delete();
        if (!!verbose) println("- Remove: " + fname);
    });
};
Access.prototype.importSource = function(impdir, acProj) {
    var acApp  = acProj.Application;
    var compos = acApp.VBE.ActiveVBProject.VBComponents;
    var self   = this;
    foreachEnum(fso.GetFolder(impdir).Files, function(fl) {
        var path  = fl.Path;
        var fname = fso.GetFileName(path);
        var xname = fso.GetExtensionName(path);
        var bname = fso.GetBaseName(path);

        if (!(xname in self.extensionTypeTable)) return false;

        var typ = self.extensionTypeTable[xname];
	switch (typ){
	     case acModule:
		var c = compos.Import(path);
	        c.Name = bname;
	        // 【変更履歴】  
			// 以前は acApp.DoCmd.Save を使用していたが、  
			// combine(ソースコードをファイルにインポート)時に途中で動作が止まるケースが多発していた。  
			// 何回か実行すれば問題なく進むが、手間がかかるため acApp.DoCmd.Close を使用することにした。  
			//  acApp.DoCmd.Close なら変更を保存して閉じるため、同様の効果が得られる。  \
			// acApp.DoCmd.Save(typ, bname);  // ← 以前のコード(問題が発生したためコメントアウト)
			
			acApp.DoCmd.Close(typ, bname);  // ← 変更を保存して閉じる処理に変更
		break;
	     case acTable:
		if (xname == 'xml'){
	            acApp.ImportXML (fso.BuildPath(impdir, bname + ".xml"),acStructureAndData);
		}
		if (xname == 'ltb'){
		     var acIOMode = 1;// ForReading
		     var acCreate = false; 
		     var acFormat = -1; // Unicode
		     var txtStream = fso.OpenTextFile(fso.BuildPath(impdir, bname + ".ltb"), acIOMode, acCreate, acFormat);

		     var dbType ="";
		     var dbName ="";
		     var dbSource ="";
		     var dbDestination = "";
		     var counter = 1;
		     while (txtStream.AtEndOfLine==false){
			     switch (counter){
				case 1:
				   dbType = txtStream.ReadLine();				   
				   counter = counter + 1;
				   break;
				case 2:
				   dbName = txtStream.ReadLine();
				   counter = counter + 1;
				   break;
				case 3:
				   dbSource = txtStream.ReadLine();
				   counter = counter + 1;
				   break;
				case 4:
				   dbDestination = txtStream.ReadLine();
				   counter = counter + 1;
				   break;
				default:
				   break;
			     }
			     if (counter > 4) {
				break;
			     }
		      }
		      txtStream.Close();
		      txtStream = null;
		      acApp.DoCmd.TransferDatabase(acLink, dbType, dbName, acTable, dbSource, dbDestination, true, true);
		}
		break;
	     default: 
		acApp.LoadFromText(typ, bname, path);
	    break;
	}
        println("- Import: " + fname);
    });
};
Access.prototype.exportSource = function(acProj, expdir) {
    var acApp  = acProj.Application;
    var compos = acApp.VBE.ActiveVBProject.VBComponents;
    var self   = this;
    this.iterAllObjects(acApp, function(obj) {
        var bname = obj.Name;
        if (!(obj.Type.toString() in self.typeExtensionTable)) return false;
        if (!self.isValidFileName(bname)) {
            println("! Warning: skip export. object '" + bname + "' is invalid file name");
            return false;
        }
        
        var xname = self.typeExtensionTable[obj.Type.toString()];
        if (obj.Type == acModule) {
            switch (compos.item(bname).Type) {
            case vbext_ct_StdModule:   xname = 'bas'; break;
            case vbext_ct_ClassModule: xname = 'cls'; break;
            default: break;
            }
        }
        
	if (obj.Type == acTable) {
            switch (acApp.CurrentDb().TableDefs.item(bname).Connect) {
            case "": xname = 'xml'; break;
            default: xname = 'ltb';break;
            }
        }
        var fname = bname + "." + xname;
	switch (xname){
	case 'cls':compos.item(bname).Export(fso.BuildPath(expdir, fname));break;
	case 'ltb':
		var ts = fso.CreateTextFile(fso.BuildPath(expdir,bname + ".ltb"), true, true);
		if (acApp.CurrentDb().TableDefs.item(bname).Connect.substr(0,4)=="ODBC"){
		ts.WriteLine("ODBC");
		ts.WriteLine(acApp.CurrentDb().TableDefs.item(bname).Connect);
		}else{
		ts.WriteLine("Microsoft Access");
		var re = /;Database=/gi;
		var cn= acApp.CurrentDb().TableDefs.item(bname).Connect;
		ts.WriteLine(cn.replace(re,""));
		}
		ts.WriteLine(acApp.CurrentDb().TableDefs.item(bname).SourceTableName);
		ts.WriteLine(acApp.CurrentDb().TableDefs.item(bname).Name);
		ts.Close();
		break;
	case 'xml':
		var xml = fso.BuildPath(expdir, fname);
		var xsd = fso.BuildPath(expdir, bname + ".xsd");
		acApp.ExportXML(0, bname, xml, xsd, "", "", 0, 32);

		// Load xml text 
		var ts = WScript.CreateObject("ADODB.Stream");
	    ts.Charset = 'UTF-8';
	    ts.Open();
		ts.LoadFromFile(xml);
		var allText = "";
		var counter =0;
		while (!ts.EOS){
		  var lineText = ts.ReadText(-2);//-2:adreadLine
		  counter += 1;
		  if (counter == 2) {
		  allText += '<dataroot xmlns:od="urn:schemas-microsoft-com:officedata" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:noNamespaceSchemaLocation="' + bname + '.xsd" >' + '\n';
		  }else{
		  allText += lineText + '\n'; 
		  }
		}
		ts.Close();
		if (counter == 2){
			allText += '</dataroot>'; 
		}

		// Prepare text
		var pre = WScript.CreateObject("ADODB.Stream");
		pre.Type = 2; // 2:adTypeText
		pre.Charset = "UTF-8";
		pre.Open();
		pre.WriteText(allText);
		pre.Position = 0;
		pre.Type = 1; // 1:adTypeBinary
		pre.Position = 3;// Skip "BOM"
		var bin = pre.Read();
		pre.Close();

		// write text and save file
		var stm = WScript.CreateObject("ADODB.Stream");
		stm.Type = 1; // 2:adTypeBinary
		stm.Open();
		stm.Write(bin);
		stm.SaveToFile(xml, 2);// 2:adSaveCreateOverWrite
		stm.Close();

		break;
	
	case 'frm':
	case 'rpt':
		acApp.SaveAsText(obj.Type, bname, fso.BuildPath(expdir, fname));
		var ts = fso.OpenTextFile(fso.BuildPath(expdir, fname), 1, false, -1);
		var allText = "";
		var counter =0;
		codeText = "";
		beginCodeText = false;
		var ignoreBlock = false;
		var targetKeys = ["    PrtDevMode = Begin", "    PrtDevNames = Begin", "    PrtDevModeW = Begin", "    PrtDevNamesW = Begin"];
		var keyFound = null;

		while (!ts.AtEndOfStream) {
		    var lineText = ts.ReadLine();
		    
		    // Checksum の置換
		    if (lineText.indexOf("Checksum =") !== -1) {
		        allText += "Checksum =123456\n";
		        continue;
		    }

    //     Right = の置換
    if (lineText.indexOf("    Right =") !== -1) {
        allText += "    Right =123456\n";
        continue;
    }
            
    //     Bottom = の置換
    if (lineText.indexOf("    Bottom =") !== -1) {
        allText += "    Bottom =123456\n";
        continue;
    }

		    // End までのブロックを削除
		    if (ignoreBlock) {
		        if (lineText === "    End") {
		            allText += "    End\n"; // End は残す
		            ignoreBlock = false;
		        }
		        continue; // End が来るまでスキップ
		    }

		    for (var i = 0; i < targetKeys.length; i++) {
		        if (lineText.indexOf(targetKeys[i]) !== -1) {
		            keyFound = targetKeys[i];
		            allText += keyFound + "\n        0x0000000000000000000000000000000000000000000000000000000000000000\n";
		            ignoreBlock = true; // End が来るまでの間、スキップ
		            break;
		        }
		    }

		    if (!ignoreBlock) {
		        allText += lineText + "\n"; // スキップしていない場合、行を保存
		    }
		}
		ts.Close();
		var nts = fso.CreateTextFile(fso.BuildPath(expdir, fname), true, true);
		nts.WriteLine(allText);
		nts.Close();
		break;
	
	default:
		acApp.SaveAsText(obj.Type, bname, fso.BuildPath(expdir, fname));		
		break;
	}
	println("- Export: " + fname);
    });
};
Access.prototype.compact = function(dbPath, acApp) {
    var engine = (!!acApp) ? acApp.DBEngine : (function() {
        var egname = "DAO.DBEngine";
        var egvers = ["120", "36", "35"];
        for (var i = 0; i < egvers.length; i++) {
            try { return new ActiveXObject(egname + "." + egvers[i]); }
            catch (e) {}
        }
        return undefined;
    })();
    
    var tempPath = (function(dbPath) {
        var d = fso.GetParentFolderName(dbPath);
        var b = fso.GetBaseName(dbPath);
        var x = fso.GetExtensionName(dbPath);
        var tempExt  = "tempdb";
        var tempPath = fso.BuildPath(d, [b, x, tempExt].join("."));
        var delim = "#";
        var i = 0;
        while (fso.FileExists(tempPath))
            tempPath = fso.BuildPath(d, [b+delim+(++i), x, tempExt].join("."));
        return tempPath;
    })(dbPath);
    
    engine.CompactDatabase(dbPath, tempPath);
    fso.DeleteFile(dbPath);
    fso.MoveFile(tempPath, dbPath);
    println("- Compact: " + fso.GetFileName(dbPath));
};
Access.prototype.combine = function(tsrc, tbin) {
    println("> Target: " + fso.GetFileName(tbin));
    
    var self = this;
    this.loanOfAcProj(tbin, true, function(acProj) {
        self.cleanupBinary(acProj);
        if (self.cmdParam.vbaproj) {
            var is64BitOffice = self.is64Bit(acProj.Application);
            self.importProject(tsrc, acProj.Application.VBE.ActiveVBProject, is64BitOffice);
        }
        self.importSource(tsrc, acProj);
    });
    this.loanOfAcProj(undefined, false, function(acProj) {
        if (self.cmdParam.dbCompact) self.compact(tbin, acProj.Application);
    });
    
    println();
};
Access.prototype.decombine = function(tbin, tsrc) {
    println("> Target: " + fso.GetFileName(tbin));

	//フォルダを削除
	var oFS = WScript.CreateObject("Scripting.FileSystemObject");
    if (oFS.FolderExists(tsrc)) {
	    oFS.DeleteFolder(tsrc, true);
	    
	    // オブジェクトを解放
		oFS = null;
	    println("> Deleted: " + tsrc); // 削除したフォルダの名前を出力
	}

    var self = this;
    this.loanOfAcProj(tbin, true, function(acProj) {
        self.cleanupSource(tsrc);
        if (self.cmdParam.vbaproj) self.exportProject(acProj.Application.VBE.ActiveVBProject, tsrc);
        self.exportSource(acProj, tsrc);
    });
    this.loanOfAcProj(undefined, false, function(acProj) {
        if (self.cmdParam.dbCompact) self.compact(tbin, acProj.Application);
    });
    
    println();
};
Access.prototype.clear = function(tbin) {
    println("> Target: " + fso.GetFileName(tbin));
    
    var self = this;
    this.loanOfAcProj(tbin, true, function(acProj) {
        self.cleanupBinary(acProj, true);
    });
    
    println();
};

var Command = function(helper) {
    this.__helper = helper;
};
Command.prototype.__helper = null;
Command.prototype.help = function() {
    println(getResource('HelpMessage'));
};
Command.prototype.combine = function() {
    var hlp  = this.__helper;
    var conf = hlp.config;
    if (conf.bak !== undefined && fso.FolderExists(conf.bin)) {
        if (!fso.FolderExists(conf.bak)) fso.CreateFolder(conf.bak);
        
        var bkdir = fso.BuildPath(conf.bak, dateTimeString(new Date()));
        fso.CreateFolder(bkdir);
        fso.CopyFile(fso.BuildPath(conf.bin, "*.*"), bkdir + "\\");
    }
    hlp.combineImpl(conf.src, conf.bin, function() { return conf.getSrcs(); });
};
Command.prototype.decombine = function() {
    var hlp  = this.__helper;
    var conf = hlp.config;
    hlp.combineImpl(conf.bin, conf.src, function() { return conf.getBins(); });
};
Command.prototype.clear = function() {
    var hlp  = this.__helper;
    var conf = hlp.config;
    hlp.iterTarget(
        function() { return conf.getBins(); },
        function(path) {
            var office = hlp.createOffice(path);
            office.clear(path);
        });
};
Command.prototype.acdoc = function() {
    var hlp  = this.__helper;
    var conf = hlp.config;
    
    hlp.iterTarget(
        function() { return conf.getBins(); },
        function(path) {
            if (hlp.getAppName(path) != 'Access') return false;
            
            println("> Target: " + fso.GetFileName(path));
            var docs;
            var acc = hlp.createOffice(path);
            acc.loanOfAcProj(path, true, function(acProj) {
                var acApp = acProj.Application;
                docs = acc.getAcDocs(acApp);
            });
            
            for (var i = 0; i < docs.length; i++) {
                var doc = docs[i];
                println("- " + doc.container + ": " + doc.name);
            }
            println();
        });
};

var CommandHelper = function(param) {
    this.parameter = param;
    this.config  = new Config(param.binary, param.source, param.binbak);
    this.command = new Command(this);
};
CommandHelper.prototype.getAppName = function(fname) {
    switch (fso.GetExtensionName(fname)) {
    case 'doc': case 'dot': case 'docm': case 'dotm':
        return 'Word';
    case 'xls': case 'xlsm': case 'xla': case 'xlam': case 'xlt': case 'xltm': case 'xlsb':
        return 'Excel';
    case 'otm':
        return 'Outlook';
    case 'mdb': case 'accdb':
        return 'Access';
    default:
        return undefined;
    }
};
CommandHelper.prototype.createOffice = function(fname) {
    var office;
    switch (this.getAppName(fname)) {
    case 'Word':
        office = new Word();
        break;
    case 'Excel':
        office = new Excel();
        break;
    case 'Outlook':
        office = new Outlook();
        break;
    case 'Access':
        office = new Access();
        if (this.parameter.incQuery) office.addTargetType(acQuery);
        break;
    default:
        office = new Dummy();
        break;
    }
    office.setCmdParam(this.parameter);
    return office;
};
CommandHelper.prototype.isTempFile = function(fname) {
    return fname.substring(0, 2) == '~$';
};
CommandHelper.prototype.iterTarget = function(getPaths, action) {
    var self = this;
    foreachEnum(getPaths(), function(fl) {
        if (self.isTempFile(fl.Name)) return false;
        return action(fl.Path);
    });
};
CommandHelper.prototype.combineImpl = function(fromDir, toDir, getPaths) {
    if (!fso.FolderExists(fromDir)) {
        println("directory '" + fromDir + "' not exists.");
        return;
    }
    
    if (!fso.FolderExists(toDir)) fso.CreateFolder(toDir);
    
    var self = this;
    this.iterTarget(getPaths, function(fromPath) {
        var toPath  = fso.BuildPath(toDir, fso.GetFileName(fromPath));
        var office  = self.createOffice(fromPath);
        var param   = self.parameter;
        office[param.commandType](fromPath, toPath);
    });
};
CommandHelper.prototype.hasCommand = function(cmdType) {
    return cmdType in this.command
        && this.command[cmdType] != this;
};
CommandHelper.prototype.runCommand = function() {
    var cmd = this.command;
    var cmdType = this.parameter.commandType;
    
    if (!this.hasCommand(cmdType)) {
        println("command '" + cmdType + "' is undefined.");
        return;
    }
    
    var runner = function() { cmd[cmdType].apply(cmd, arguments); };
    if (cmdType == 'help') {
        runner();
    }
    else {
        println("begin " + cmdType + "\n");
        runner();
        println("end");
    }
};

function main(args) {
    var param =
        new CmdParam({
            defaultParameterName: "commandType",
            commandType: "help",
            binary:      "bin",
            source:      "src",
            binbak:      new Conditional("bak"),
            vbaproj:     false,
            incQuery:    false,
            dbCompact:   false
        })
        .parse(args);
    
    // It's guard for internal impl. If necessary, you can comment out to enable this feature.
    //param.binbak.flag = false;
    //param.incQuery = false;
    
    var h = new CommandHelper(param);
    h.runCommand();
}

main(args);
]]>
</script>
    </job>
</package>

このようにすることで、Access固有の差分問題を回避し、安定したバージョン管理が実現できます。

ツールの使い方

前章では vbac.wsf のコードを修正しました。この章では、修正したこのツールの使い方について説明します。

準備

まずは、ターミナルまたはコマンドプロンプトで ariawase フォルダに移動します。

cd ariawase

次に、コードをエクスポートしたい Access ファイル(.accdb)を bin フォルダにコピーしてください。そして今回使用するAccessファイルを開いたら、まず左上の「ファイル」をクリックし、「オプション」を選択してください。

今回使用するAccessファイルを右クリックして「プロパティ」を開いてください。ウィンドウ下部に、「このファイルは他のコンピューターから取得したものです。このコンピューターを保護するため、このファイルへのアクセスはブロックされる可能性があります。」というメッセージが表示されている場合があります。その場合は、「許可する」 にチェックを入れて「OK」をクリックしてください

そして、今回使用するAccessファイルを開いて、左側のメニューから「トラスト センター」をクリックし、「トラスト センターの設定」を開きます。そこで「マクロの設定」を選び、「すべてのマクロを有効にする」にチェックを入れてください。

コードのエクスポート

AccessファイルからVBAコードをエクスポートするには、以下のコマンドを実行します。出力先はsrc フォルダです。

cscript vbac.wsf decombine /incquery

コードのインポート

src フォルダにエクスポートしたVBAコードをAccessファイルにインポートするには、以下のコマンドを使用します。

cscript vbac.wsf combine /incquery

重要な注意点:
インポートする際は、「Accessファイル名」と「エクスポートしたコードのフォルダ名」を同じ名前にしてください。例えば、インポート先のファイル名が test.accdb の場合、エクスポート先のフォルダの中にあるフォルダ名も test.accdb にします。

ディレクトリを明示的に指定したい場合

エクスポート・インポート先のパスを明示的に指定することも可能です。以下のようにパラメータを追加してください。

# コードのエクスポート
cscript vbac.wsf decombine /binary:C:\export_dir /source:import_dir /incquery

# コードのインポート
cscript vbac.wsf combine  /binary:C:\export_dir /source:import_dir /incquery

よくあるエラーと対処法

インポート時にエラーが発生して失敗することがあります。その場合でも、同じコマンドをもう一度実行すると正常に動作することがあるので、あきらめずに再試行してみてください。

注意点:インポートは全上書き

このツールでのインポート処理は、Accessファイルに含まれるすべてのVBAコードを一度削除した上で、再度取り込む形式です。つまり、一部のファイルだけを個別にインポートすることはできません。Accessが正常に動作するために必要なすべてのコードを揃えて一括インポートする必要があります。一見すると複雑に見えるかもしれませんが、慣れれば非常に便利なツールです。

おまけ

前章では、「ariawase」のインポート処理では すべてのVBAコードが一度削除されたあとで再インポートされる という仕様上、「一部のファイルだけを個別にインポートすることはできない」と説明しました。しかし、実際の開発現場では「特定のモジュールだけを移したい」といったケースもあるかと思います。そこでこの章では、「ariawaseを使わずに、特定のVBAモジュールだけを手動でインポートする方法」を紹介します。

1.エクスポート元とインポート先、両方のAccessファイルを開きます。

2.両方のAccessで、キーボードの F11 キーを押して ナビゲーションウィンドウ を表示します。

3.移動させたいVBAモジュール(フォーム、レポート、標準モジュールなど)を、ドラッグ&ドロップで移動させます。

※ エクスポート元のオブジェクトをそのまま、インポート先にドラッグしてドロップすればOKです。

大きなファイル(特にフォームやレポート)はインポートに時間がかかることがあります。その際は焦らず、しばらく待ってみてください(気長に…笑)。この方法では、任意のモジュールだけを柔軟に移動できるため、細かな修正やテストにも便利です。

まとめ

今回は、Microsoft AccessのVBAコードを出力し、GitやGitHubでバージョン管理する方法をご紹介しました。これにより、これまで難しかったAccess開発の履歴管理やチームでのコード共有が、格段にやりやすくなると思います。もちろん今回紹介した内容はあくまで一例であり、プロジェクトの規模や運用体制に応じて柔軟にカスタマイズして活用するのが良いでしょう。また、いくつかの点ではMicrosoftの公式情報が存在せず、推測に基づく説明も含まれていますが、実際に検証を行っており、かなり正確性の高い内容になっていると考えています。

ただし、このツールにも課題があり、特にAccessファイル内に大量のフォーム、モジュール、テーブルがある場合、インポート処理に時間がかかる傾向があります。エクスポートはスムーズでも、インポート時には処理が重くなることがあり注意が必要です。この点については現時点で明確な解決策がなく、もし対処法をご存じの方がいればぜひ教えていただきたいです。私自身も今後引き続き調査を進め、改善策が見つかれば記事として共有したいと考えています。

今回の記事が、少しでも皆さまの力になれば幸いです。

参考文献

いげ太の日記

vbacでエクセルVBAのソースコードをGitバージョン管理する方法 | 隣IT

Microsoft Access をGITでソース管理している内容を共有します。他にもいい方法があれば取り入れたいです。 #Git – Qiita

ACCESS このファイルのソースが信頼できないため、Microsoftによりマクロの実行がブロックされました – たすけてACCESS

このカテゴリの最新記事

関連記事

SHOP LIST

タイガーラック株式会社

〒577-0056
大阪府東大阪市長堂1-3-14 TOKUYASU Bld.