Access VBAで自作クラスを引数型に指定した瞬間に「ユーザー定義型が定義されていません(User-type not defined)」が出ると、つい「参照設定(ツール > 参照設定)の追加が必要?」と考えがちです。ですが、As clsMold のように“クラス名っぽい型”で起きる場合、原因はもっと単純で、プロジェクト内にそのクラスが無い・名前が違う・外部プロジェクトとして見えていない、のどれかであることがほとんどです。
よくある再現例:「As clsMold」でコンパイルが止まる
たとえば次のように、独自クラス型を引数にした関数を作るとします。
Function BuildSQLInsertNewMold(objCurrMold As clsMold) As String
' ここでINSERT文を組み立てる想定
End Function
このときVBAが clsMold という型を解決できないと、コンパイル時点で「User-type not defined(ユーザー定義型が定義されていません)」になります。結論から言うと、ここで必要なのは参照設定の追加ではなく、まず clsMold という“クラス”が存在して、正しい名前で認識されている状態 を作ることです。
まず押さえる:VBAの「型」はどこから来るのか
VBAで型が解決されるルートは大きく3つです。
| 型の出どころ | 例 | Access VBAでの扱い |
|---|---|---|
| 標準ライブラリ/参照ライブラリ | DAO.Database / ADODB.Connection / Excel.Application | 参照設定で解決されることが多い |
| 自作クラス(クラスモジュール) | clsMold / clsLogger | 同一プロジェクト内にクラスモジュールが必要 |
| ユーザー定義型(Type…End Type) | Public Type TMold … End Type | 標準モジュール内で定義する(クラスとは別物) |
今回の clsMold は、一般的には 「自作のクラスモジュール名」 を指します。つまり、そのクラスモジュールが無いなら必ず落ちます。
原因の最優先チェック:clsMold というクラスモジュールが“存在しない”
クラスモジュールを作っていない(コードだけコピーした)
どこかのサンプルコードから関数だけコピーした場合、クラス側(clsMold)がプロジェクトに入っていないケースが頻発します。AccessのVBE(VBAエディター)で次を確認してください。
- VBEを開く(Alt + F11)
- プロジェクトエクスプローラーで「クラス モジュール」に clsMold が存在するか確認
- 無ければ「挿入」→「クラス モジュール」で作成
この時点で As clsMold のエラーが消えるなら、原因はほぼ確定です。
“モジュール名” と “(Name)プロパティ” が一致していない
クラスモジュールを作っていても、プロパティウィンドウの (Name) が clsMold になっていない と同じエラーになります。ここが最大の落とし穴です。
- クラスモジュールをクリック
- プロパティウィンドウで (Name) を確認
- 厳密に clsMold になっているかチェック(綴り違いがNG)
大文字小文字は通常気にしなくてOKですが、clsMord / clsModl のような綴り違いは一発で詰みます。
次に多い:参照設定の「MISSING」で別の型解決が崩れている
「clsMoldがあるのにエラーが出る」場合は、参照設定の問題が“間接的に”影響している可能性があります。特に、VBAはコンパイルが通っていないと型情報が正しく更新されず、結果として関係ない場所で「ユーザー定義型が定義されていません」に見えることがあります。
チェック手順
- VBEの「ツール」→「参照設定」を開く
- リスト上部に MISSING: 〜 がないか確認
- あればチェックを外す、または正しいライブラリに差し替える
- 「デバッグ」→「コンパイル(VBAProjectのコンパイル)」を実行
この「コンパイル」は地味に重要です。Accessはオブジェクト(フォーム/レポート)側にもモジュールがぶら下がり、どこか1か所でも壊れていると全体が不安定になりがちです。
“同名”トラップ:標準モジュール名が clsMold になっている
意外にやりがちなのが、標準モジュール(Module)を clsMold という名前にしている パターンです。標準モジュール名は型になりません(Type定義をしていない限り)。
| やりたいこと | 必要なもの | モジュール種類 |
|---|---|---|
| プロパティやメソッドを持つ“オブジェクト” | clsMold クラス | クラスモジュール |
| 構造体っぽい入れ物(メソッド無し) | Public Type TMold | 標準モジュール |
もし標準モジュールが clsMold という名前なら、クラスモジュール側を別名にするか、標準モジュールの方を modMold などに改名して衝突を避けるのが安全です。
外部のAccessファイルに clsMold がある場合:参照追加だけでは足りないことがある
質問文の「参照設定で追加すれば解決する?」は、clsMold が別のACCDB/MDB側にある 場合に成立します。ただし、ここには追加の条件があります。
外部プロジェクト参照の基本
- 参照元と参照先の両方を開いた状態で、参照元のVBEで「ツール → 参照設定」
- 一覧に VBAProject(参照先ファイル名) のような項目が出る場合があるのでチェック
- 参照先のクラスが外部から見える状態である必要がある
外部から見える状態とは(よく詰まるポイント)
参照先が「自分の中だけで使う設計」になっていると、参照を付けても型として認識できないことがあります。環境により表示項目は異なりますが、少なくとも次は意識してください。
- クラスモジュールやメンバーが Private 扱いになっていないか
- 参照先をACCDE化している場合、参照元から利用できる設計か
- 参照先を開いていないと参照できない構成になっていないか(開き方・信頼済みか)
外部参照は便利ですが、運用面(配布・更新・信頼設定・バージョン差)で詰まりやすいので、まずは同一プロジェクトにクラスを入れて解決するのが最短ルートです。
最短で切り分ける:即席テストコード
「本当に clsMold が認識されているか」を切り分けるには、イミディエイトウィンドウ(Ctrl + G)や小さなテストプロシージャが有効です。
Public Sub TestClsMold()
Dim m As clsMold
Set m = New clsMold
Debug.Print TypeName(m)
End Sub
これがコンパイル/実行できれば、少なくとも clsMold は型として解決できています。逆に、この段階で止まるなら、クラスモジュールの存在・名前・参照・MISSING を上から順に疑うのが正攻法です。
「クラス」と「ユーザー定義型(Type)」は別物:混同を防ぐ
エラーメッセージが「ユーザー定義型」と言っているせいで、Type 文の話に引っ張られがちですが、今回の主役は“クラス”です。とはいえ、設計によっては Type の方が適していることもあります。
クラスが向いているケース
- 入力値の検証(例:型番のルールチェック)を持たせたい
- SQL生成・整形・ログ出力などの振る舞いをまとめたい
- プロパティ変更時に関連値を更新したい
Typeが向いているケース
- 単純なデータの入れ物で、処理は別モジュールに置く
- 大量に生成して高速に扱いたい(オブジェクト生成コストを減らす)
- API宣言などで構造体が必要
「SQLを組み立てる」目的なら、clsMoldに“SQL用の整形メソッド”を持たせる など、クラス設計のメリットが出やすいです。
実用例:clsMold を引数にSQLを作るときの安全な書き方
ここからは、エラー解消後に「実際どう書くと事故りにくいか」を具体化します。AccessのSQL生成で多い事故は、シングルクォート、Null、日付(#) の扱いです。SQLインジェクション対策という意味でも、文字列を安易に連結しない工夫が必要です(最優先はクエリパラメータですが、どうしても文字列生成が必要な場面もあります)。
clsMold(例)
クラスモジュール clsMold を作成し、最低限のプロパティを置いた例です。
'=== Class Module: clsMold ===
Option Explicit
Private mMoldId As Long
Private mMoldName As String
Private mCreatedAt As Date
Private mIsActive As Boolean
Public Property Get MoldId() As Long
MoldId = mMoldId
End Property
Public Property Let MoldId(ByVal v As Long)
mMoldId = v
End Property
Public Property Get MoldName() As String
MoldName = mMoldName
End Property
Public Property Let MoldName(ByVal v As String)
mMoldName = v
End Property
Public Property Get CreatedAt() As Date
CreatedAt = mCreatedAt
End Property
Public Property Let CreatedAt(ByVal v As Date)
mCreatedAt = v
End Property
Public Property Get IsActive() As Boolean
IsActive = mIsActive
End Property
Public Property Let IsActive(ByVal v As Boolean)
mIsActive = v
End Property
SQL整形用のヘルパー(標準モジュール)
SQL文字列生成をするなら、整形はヘルパーに寄せて“同じ地雷を何度も踏まない”のがコツです。
'=== Standard Module: modSqlHelper ===
Option Explicit
Public Function SqlText(ByVal v As Variant) As String
If IsNull(v) Then
SqlText = "NULL"
Else
SqlText = "'" & Replace(CStr(v), "'", "''") & "'"
End If
End Function
Public Function SqlNumber(ByVal v As Variant) As String
If IsNull(v) Or (Len(Trim$(v & "")) = 0) Then
SqlNumber = "NULL"
Else
SqlNumber = CStr(v)
End If
End Function
Public Function SqlDate(ByVal v As Variant) As String
If IsNull(v) Then
SqlDate = "NULL"
Else
' Access SQL: #yyyy-mm-dd hh:nn:ss#
SqlDate = "#" & Format$(CDate(v), "yyyy-mm-dd hh:nn:ss") & "#"
End If
End Function
Public Function SqlBool(ByVal v As Variant) As String
If IsNull(v) Then
SqlBool = "NULL"
Else
SqlBool = IIf(CBool(v), "True", "False")
End If
End Function
本題:clsMold を引数にした関数(完成形)
Public Function BuildSQLInsertNewMold(ByVal objCurrMold As clsMold) As String
Dim sql As String
sql = "INSERT INTO T_Mold (MoldId, MoldName, CreatedAt, IsActive) VALUES ("
sql = sql & SqlNumber(objCurrMold.MoldId) & ", "
sql = sql & SqlText(objCurrMold.MoldName) & ", "
sql = sql & SqlDate(objCurrMold.CreatedAt) & ", "
sql = sql & SqlBool(objCurrMold.IsActive)
sql = sql & ");"
BuildSQLInsertNewMold = sql
End Function
これで、clsMold が正しく定義されている限り、As clsMold で落ちることはありません。さらに、SQL文字列の整形も一貫するため、現場でありがちな「ある画面は動くが別画面でコケる」系の事故が減ります。
まだ直らないときのチェックリスト(上から順に潰す)
最後に、現場での“詰まりポイント”を短い手順にまとめます。
| 症状 | 最優先で見る場所 | 対処 |
|---|---|---|
| As clsMold で即エラー | クラスモジュールの有無 | クラスモジュールを作成/インポート |
| クラスはあるのにエラー | (Name) プロパティ | (Name)=clsMold に合わせる |
| 環境によって出たり消えたり | 参照設定のMISSING | MISSINGを解消してコンパイル |
| 別DBのclsMoldを使いたい | 外部参照の可視性 | 参照追加+外部公開できる設計にする |
| 同名のモジュールがある | 標準モジュール名 | modMold などに改名して衝突回避 |
まとめ:参照設定より先に“クラスの存在と名前”を疑う
Function ... (objCurrMold As clsMold) で「ユーザー定義型が定義されていません」が出たとき、最初にやるべきことは参照設定の追加ではなく、clsMold というクラスモジュールがプロジェクト内に存在し、(Name) が一致しているか を確認することです。そこを通過した上で、MISSING参照や外部プロジェクト参照など“次の層”を疑うと、最短で原因に到達できます。
もし今後、同様のSQL生成やデータ受け渡しが増えるなら、クラス+SQLヘルパー の組み合わせは保守性が上がりやすい構成です。まずは TestClsMold のような小さな検証から始めて、型解決ができている状態を作っていきましょう。

コメント