自作ツール(ExcelVBA)のセルの保護
2016年09月吉日
株式会社ワールドイッツマイン
代表取締役副社長
川上智弘
●エクセルの問題点及び本書の目的。
エクセルVBAを用いてツールを作成する人の殆どが、シートまたはセルの保護を施しているとおもいます。
純粋にボタンを押してマクロを実行するだけのものは、すべてのセルをロックしますし、
単票や一覧票の類は、入力可能項目のセルのロックを外し、
計算に依って算出される項目やラベル項目他のセルはロックするでしょう。
その上でシートの保護を掛ける事で、編集してはいけないエリアの不可侵処理を行います。
しかしながら、ロックが掛かっているセルは何をしても崩される事はありませんが、
ロックが掛かっていないセルは、[切り取り][貼り付け][フィル][クリア]処理で
仮にシートの保護が掛かっている場合でもセルの型が崩される場合があります。
[フィル][クリア]は悪意のある人間しかやらないので心配はほぼありませんが、
残りの二つは、単票入力等の場合にいくらでも行う事があり得るので
(誤って本来記入する必要がある項目の右隣に記入してしまい、切り取って本来のエリアに貼り付ける…等)
防止出来るならば防止したほうが宜しいと筆者は考えます。
早い話が、自作ツールを破壊から防ぐ手段です。
以下は筆者が考える最良の手段故、他に良い方法がいくらでもあるとおもいます。
あくまで参考程度に参照して頂けたら幸いです。
●手段その1
使用者全員に[切り取り][貼り付け]を絶対に行わない様に釘を刺す方法です。
上記の行為を行ってセルまたはツールが破壊されても一切責任を取らない旨を記入しておけば
万が一苦情が来ても、その苦情を突っ撥ねる事が可能です。
●手段その2
フォームを使用する方法です。
フォームの項目は絶対に破壊される事が無い為、新規にツールを作成する場合に非常に有効な手段です。
●手段その3
自作ツールに破壊を未然に防ぐマクロを組み込む方法です。
筆者がVBAの技術が未熟故、最良の方法では無い可能性が高いですが、
既存のツールがエクセルでありフォームに鞍替えする事が困難な場合や
どんなに釘を刺しても苦情だけは一人前のバカもいますので(現に何人もいました)、
その場合に参考にして頂けたらとおもいます。コードはVBAで記入してあります。
JavaやPerl等の最先端の言語を用いている方からしたら
VBAなど河原の小石の如く取るに足らない存在でしょうが、
進捗管理や品質管理はVBAで記述してある場合が多いので、そこは御容赦願います。
方法は大きく分けて二つ。
一つ目が[切り取り]、[コピー]、[貼り付け]自体を不可能にする方法。
二つ目がセルが破壊された瞬間に、そのセルの状態を元に戻す方法。
しかしながら一つ目は業務に支障が出るケースもありますので、二つ目の方法を以下に記載します。
[Alt]+[F11]にてVBAを起動して、[Microsoft Excel Objects]配下の
対象シートのコードを追加していく形になります。
Private Sub Worksheet_Changeにて
@一旦、セルを全選択して色を(254,254,254)にする。
Aヘッダー項目と入力可能欄のみ色を塗り潰し直し、Range.NumberFormatLocalを"@"にする。
B全ての入力可能欄に名前定義を施す。
C入力可能欄の文字列以外の項目のRange.NumberFormatLocalを適切なものに変える。
DWorksheet_Changeにてフラグを設けて、
 A.・入力可能項目の色が白(255,255,255)以外。
      ・Range.NumberFormatLocalが不正値(例えば数値項目なのに文字列("@")等の数値以外が入っている)。
      ・名前定義が無くなっている入力可能項目がある。
      ・入力可能かつ結合セルの結合が解除されているセルが存在する。
      ・エクセルがコピー状態。
      の内、一つ以上を満たす。
 B.アンドゥ可能な状態。
 AB共に満たした場合は、フラグを建てる。
Eフラグが立っている場合はフラグを戻し、アンドゥする。
事で防止を図ります。
実際に筆者が即興で作成した単票ツールでコードの中身を見て行きます。
必要に応じて、ここからサンプル単票を墜として下さい。
コードは全て以下の単票マクロに最適化してありますのでご了承願います。
画面
画面項目説明
個人単票
名前⇒ @ 部門⇒ A 社員番号⇒ 川上智弘
生年月日⇒ B 会社Mail⇒ C 血液型⇒ D
趣味⇒ E 好物⇒ F 将来の夢⇒ G
ラッキーaヒ H 好きな色⇒ I 好きな天候⇒ J http://img.atwikiimg.com/www9.atwiki.jp/gensouv/attach/346/21/B05.png
描き足りない事があったら何でも好きに描いて下さい。。⇒ K
赤い項目は必須入力、青い項目は任意入力です。 保存 初期化 終了 全て
終了
画面項目説明
項目名称(名前定義) 種類 属性
(IME)
桁数
byte

位置
Tab
Idx
I/O 必須 画面L 初期値 チェック内容・
編集内容
項目説明・備考
1 名前記入欄 TXT 全角 左詰 1 I @
2 部門記入欄 TXT 全角 左詰 2 I A
3 社員番号記入欄 TXT
(R)
IME
無効
左詰 O ログインID
4 生年月日入力欄 DATE IME
無効
8 右詰 3 I B YYYY/MM/DD
5 会社Mail入力欄 TXT IME
無効
左詰 4 I C
6 血液型入力欄 CMB IME
無効
2 左詰 5 I D "O"、"A"、"B"、"AB"から1つ選択。
7 趣味入力欄 TXT 全角 左詰 6 I E
8 好きな物入力欄 TXT 全角 左詰 7 I F
9 将来の夢入力欄 TXT 全角 左詰 8 I G
10 数字入力欄 NUM IME
無効
9,0 右詰 9 I H 0〜100000000の整数を入力可。
11 色入力欄 TXT 全角 左詰 10 I I
12 好きな天候入力欄 CMB IME
無効
左詰 11 I J ""、"快晴"、""、"豪雨"、""、""、""、"吹雪"、"熱波"、"砂嵐"、"暗闇"、"時化"、"上記以外"から1つ選択。
13 備考欄 TXT 全角 中央 12 I K
14 保存 BTN 必須入力チェック。 ブックを保存する。
15 初期化 BTN ブックを初期化する。
16 終了 BTN 必須入力チェック。 ブックを保存終了する。
17 全て終了 BTN 必須入力チェック。 起動中の全てのブックを終了する。
18 BTN
D
上記の単票は@〜Kの入力可能セルはセルのロックが掛かっておらず、
その他のセルにはロックが掛かっており、その上でシートの保護を掛けています。
即ち@〜Kのみ進入及び編集可能となります。
以下は標準モジュール(Main)のコーディング(一部抜粋)です。
Option Explicit
Public Const MST1 = "名前記入欄"
Public Const MST2 = "部門記入欄"
Public Const MST3 = "生年月日入力欄"
Public Const MST4 = "会社Mail入力欄"
Public Const MST5 = "血液型入力欄"
Public Const MST6 = "趣味入力欄"
Public Const MST7 = "好きな物入力欄"
Public Const MST8 = "将来の夢入力欄"
Public Const MST9 = "数字入力欄"
Public Const MST10 = "色入力欄"
Public Const MST11 = "好きな天候入力欄"
Public Const MST12 = "備考欄"
Public ARYY As Variant '名前定義を格納する箱
Public FirstFlg As Boolean '起動時フラグ
Public PFlg As Boolean '他マクロ実行フラグ
Public PSPS As Boolean 'セル破壊判定フラグ。最重要。
'〜〜〜〜〜〜〜〜〜〜(中略)〜〜〜〜〜〜〜〜〜〜
Sub SingleC()  '名前定義破壊判定
Dim APPPP As Long
Dim MyName As Name
On Error GoTo DesTroy '名前定義が破壊されているなら、下のIF文でエラー発生。DesTroy以下に飛ぶ。
ARYY = Array("名前記入欄", "部門記入欄", "生年月日入力欄", "会社Mail入力欄", _
             "血液型入力欄", "趣味入力欄", "好きな物入力欄", "将来の夢入力欄", _
             "数字入力欄", "色入力欄", "好きな天候入力欄", "備考欄") '名前定義の一覧。
For APPPP = 1 To 12 '名前定義は12個あるので1〜12。
If InStr(1, Range(ARYY(APPPP - 1)).Value, Chr(10)) > 0 Then 'この単票は改行禁止にしている為 一石二鳥。
  PSPS = True
  Exit For
End If
Next
Exit Sub '名前定義が無事(かつ未改行)なら、フラグを立てずにSubを抜ける。
DesTroy:
PSPS = True '上のIF文でエラーが発生(名前定義を読み込めない)の場合、ここに飛ぶ。
End Sub
以下はMicrosoft Excel Objectsのコーディングになります。
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim SPSF As Boolean
SPSF = False
PSPS = False '初期状態で必ずフラグを降ろす。
If PFlg <> True And FirstFlg <> True Then '起動時と他マクロ実行時は、この処理は行いません。
 Call CopyPasteCommandControl(False)
  If InStr(1, Cells(Target.Row, Target.Column).Value, Chr(10)) > 0 Then
   Cells(Target.Row, Target.Column).Value = Replace(Cells(Target.Row, Target.Column).Value, Chr(10), "")
  End If
 Call SingleC '名前定義破壊判定(上記参照)
  If PSPS = True Then
  Else '名前定義が無事だった場合、NumberFormatLocalが正しいか、色がALL255(純白)か、セルの結合が解除されていないか判定。
    If Range(MST1).MergeArea.NumberFormatLocal = "@" And Range(MST2).MergeArea.NumberFormatLocal = "@" And _
       Range(MST3).MergeArea.NumberFormatLocal = "yyyy/mm/dd" And Range(MST4).MergeArea.NumberFormatLocal = "@" And _
       Range(MST5).MergeArea.NumberFormatLocal = "@" And _
       Range(MST6).MergeArea.NumberFormatLocal = "@" And Range(MST7).MergeArea.NumberFormatLocal = "@" And _
       Range(MST8).MergeArea.NumberFormatLocal = "@" And Range(MST9).MergeArea.NumberFormatLocal = "0" And _
       Range(MST10).MergeArea.NumberFormatLocal = "@" And _
       Range(MST11).MergeArea.NumberFormatLocal = "@" And Range(MST12).MergeArea.NumberFormatLocal = "@" And _
       Range(MST1).MergeArea.Interior.Color = RGB(255, 255, 255) And Range(MST2).MergeArea.Interior.Color = RGB(255, 255, 255) And _
       Range(MST3).MergeArea.Interior.Color = RGB(255, 255, 255) And Range(MST4).MergeArea.Interior.Color = RGB(255, 255, 255) And _
       Range(MST5).MergeArea.Interior.Color = RGB(255, 255, 255) And Range(MST6).MergeArea.Interior.Color = RGB(255, 255, 255) And _
       Range(MST7).MergeArea.Interior.Color = RGB(255, 255, 255) And Range(MST8).MergeArea.Interior.Color = RGB(255, 255, 255) And _
       Range(MST9).MergeArea.Interior.Color = RGB(255, 255, 255) And Range(MST10).MergeArea.Interior.Color = RGB(255, 255, 255) And _
       Range(MST11).MergeArea.Interior.Color = RGB(255, 255, 255) And Range(MST12).MergeArea.Interior.Color = RGB(255, 255, 255) And _
       Range(MST1).MergeArea.Locked = False And Range(MST2).MergeArea.Locked = False And Range(MST3).MergeArea.Locked = False And _
       Range(MST4).MergeArea.Locked = False And Range(MST5).MergeArea.Locked = False And Range(MST6).MergeArea.Locked = False And _
       Range(MST7).MergeArea.Locked = False And Range(MST8).MergeArea.Locked = False And _
       Range(MST9).MergeArea.Locked = False And Range(MST10).MergeArea.Locked = False And Range(MST11).MergeArea.Locked = False And _
       Range(MST12).MergeArea.Locked = False And _
       Range(MST1).MergeCells And Range(MST2).MergeCells And Range(MST3).MergeCells And Range(MST4).MergeCells And _
       Range(MST5).MergeCells And Range(MST6).MergeCells And Range(MST7).MergeCells And Range(MST8).MergeCells And _
       Range(MST9).MergeCells And Range(MST10).MergeCells And Range(MST11).MergeCells And Range(MST12).MergeCells Then
    Else '上記の内1つでも不正な値が入っていたら、セルが破壊されている証拠になる。
    PSPS = True
    End If
  End If
  If PSPS = True Or Application.CutCopyMode = 1 Then 'コピーモードあるいはセル破壊フラグが立っている場合。
     PSPS = False 'フラグを戻す。
        With Application
            .EnableEvents = False
            On Error GoTo RR: 'アンドゥ不可能の場合は.Undoは飛ばす。
            .Undo '状態を1つ手前に戻す。
RR: 'アンドゥが出来なかった場合はここに飛ぶ。
            .EnableEvents = True
            On Error GoTo 0 'エラー処理を戻す。
        End With
  End If
  If Application.CutCopyMode = 1 Then 'コピー状態強制解除。
    Application.CutCopyMode = 0
  End If
  Call CopyPasteCommandControl(True)
End If
End Sub
一部の抜粋(個々のSubは未省略)ですが、以上になります。
もし、より良いコーディングがありましたら、ご一報下さい。
●おわりに
最後までお付き合い下さり、ありがとうございました。
質問等、何かありましたら、こちらまで。
時間が余っている時は対応します。