構造体配列内の配列のRedim

解決


マルオ  2005-11-15 08:36:45  No: 127701

マルオです。
いつも勉強させてもらっています。

またまた、教えていただきたいのですが、構造体配列の中に配列を用意して、
それぞれでRedim(Preserve)が実行できるかをテストしようと思いました。

そしたら、下記のコンパイルエラーが発生して、原因がわからずに困っています。

「パブリック オブジェクト モジュールで定義されたユーザー定義型に限り、変数に割り当てることができ、実行時バインディングの関数に渡すことができます。」

多分、何か私のコードに問題があると思うのですが、ぜひ教えていただけないでしょうか?

ちなみに、UboundSpを用いずに、Uboundを用いると、正常に動作します。

'---以下の内容をForm1に記述---
Private Sub Form_Load()

    ReDim structText(0) As structArray
    ReDim structText(0).testA(2) As String
    ReDim structText(0).testB(2) As String
    
    structText(0).testA(0) = "testA0-0"
    structText(0).testA(1) = "testA0-1"
    structText(0).testA(2) = "testA0-2"
    
    structText(0).testB(0) = "testB0-0"
    structText(0).testB(1) = "testB0-1"
    structText(0).testB(2) = "testB0-2"
    
    ReDim Preserve structText(1) As structArray
    ReDim Preserve structText(1).testA(1) As String
    ReDim Preserve structText(1).testB(1) As String
    
    structText(1).testA(0) = "testA1-0"
    structText(1).testA(1) = "testA1-1"
    
    structText(1).testB(0) = "testB1-0"
    structText(1).testB(1) = "testB1-1"

    Debug.Print UboundSp(structText)  '★ここの引数でコンパイルエラー
    Debug.Print UboundSp(structText(0).testA)
    Debug.Print UboundSp(structText(0).testB)
    
    Debug.Print UboundSp(structText(1).testA)
    Debug.Print UboundSp(structText(1).testB)
    
End Sub

'---以下の内容をModule1に記述---
Public structText() As structArray
Public Type structArray
    testA() As String
    testB() As String
End Type

Public Function UboundSp(v As Variant) As Long

    On Error Resume Next
    UboundSp = UBound(v)
    If Err <> 0 Then UboundSp = -1
    On Error GoTo 0
  
End Function

開発環境は,VB6です。


030  2005-11-15 09:31:04  No: 127702

エラーメッセージの通りではないかと思います。
つまり
Public Type structArray
    testA() As String
    testB() As String
End Type
がパブリックオブジェクトモジュールで定義されていないからです。
これはVB6の仕様みたいで
http://www.int21.co.jp/pcdn/vb/noriolib/vbmag/9812/vb6/
にヒントがあります。

結論から言うと
上記構造体を外部コンポーネント(ActiveX.Dll,OCX)などでパブリック宣言してやれば
エラー無くあなたのコードは動作します。


マルオ  2005-11-15 10:09:25  No: 127703

030さん、回答ありがとうございます。
外部コンポーネントを作成するのは、初めてなので、
ちょっとヒントいただきたいのですが、
下記の手順についてコメントいただけるとうれしいです。

まず、ActiveXコントロールに構造体をPublic宣言するために、以下の作業を行う。
-----------
① VB6を起動して、「ActiveXコントロール」を選択して、追加する。
② デフォルトで、「UserControl1」というのがあるので、それには触れずに、標準モジュール(Module1)を追加する。
③ 下記のユーザー定義型を定義する。

Public Type structArray
    testA() As String
    testB() As String
End Type

④ コンパイルして「testtest.ocx」を作成する。
-----------

次に、コンパイルエラーが発生しているプロジェクトに対して「testtest.ocx」を参照設定してあげる。

-----------
① コンパイルエラーが発生しているプロジェクトを開いて、
   [プロジェクト]-[参照設定]の順に選択して、「testtest.ocx」を選択する。
② そして、コンパイル!
-----------

うーん。何か間違っていると思うのですが、どこが間違っているかわかりますでしょうか?

よろしくお願いします。


マルオ  2005-11-15 10:11:10  No: 127704

いい忘れました。

>② そして、コンパイル!

ってところで、同じエラーが発生して、コンパイルができません。(>_<)

ご指摘ください。m(_ _)m


ガッ  2005-11-15 16:37:52  No: 127705

パブリックオブジェクトモジュールは、
GlobalMultiUseに設定されたクラス…だったかな。(ほかにもあるかも)

※でも、わざわざ構造体を使いたいと言うだけで
  外部モジュールにするのは抵抗ありませんか?
  構造体ではなく素直にクラスを作った方がいいと思うのですが…


abu  2005-11-15 19:31:26  No: 127706

ActiveX.ocxは参照設定じゃなくてコンポーネントの追加じゃない?
>※でも、わざわざ構造体を使いたいと言うだけで
>  外部モジュールにするのは抵抗ありませんか?
>  構造体ではなく素直にクラスを作った方がいいと思うのですが…
激しく同意


030  2005-11-15 20:02:43  No: 127707

外部から参照できるのはUserControlモジュールに記述したパブリックメンバだけだったかと。

よって構造体の宣言を標準モジュールではなくUserControlモジュールでパブリック宣言して
ActiveXコントロールを参照設定してやればよいかと。


マルオ  2005-11-15 21:56:51  No: 127708

ガッさん。abuさん。030さん。
回答ありがとうございます。

ほんと初心者ですいません。
クラスに追加しても、同じことが出来るのであれば、そうしたいです。

また家に帰ったら、試して、結果報告させてください。

宜しくお願いします。


マルオ  2005-11-16 09:25:08  No: 127709

こんばんわ。マルオです。
教えてもらったクラスを作成する方法で、早速試してみたのですが、
うまくできませんでした。

うーん。うーん。(-_-;)
クラスってほとんど追加したことなくて、
よく分からないのですが、下記のやり方だとコンパイルエラーが発生してしまいます。

「プライベートオブジェクトモジュール内ではパブリックのユーザー定義型は定義できません。」

どっか間違いがあると思うのですが、ご指摘してもらえないでしょうか?

'---以下の内容をForm1に記述---
Private Sub Form_Load()

    ReDim structText(0) As structArray
    ReDim structText(0).testA(2) As String
    ReDim structText(0).testB(2) As String
    
    structText(0).testA(0) = "testA0-0"
    structText(0).testA(1) = "testA0-1"
    structText(0).testA(2) = "testA0-2"
    
    structText(0).testB(0) = "testB0-0"
    structText(0).testB(1) = "testB0-1"
    structText(0).testB(2) = "testB0-2"
    
    ReDim Preserve structText(1) As structArray
    ReDim Preserve structText(1).testA(1) As String
    ReDim Preserve structText(1).testB(1) As String
    
    structText(1).testA(0) = "testA1-0"
    structText(1).testA(1) = "testA1-1"
    
    structText(1).testB(0) = "testB1-0"
    structText(1).testB(1) = "testB1-1"

    Debug.Print UboundSp(structText)
    Debug.Print UboundSp(structText(0).testA)
    Debug.Print UboundSp(structText(0).testB)
    
    Debug.Print UboundSp(structText(1).testA)
    Debug.Print UboundSp(structText(1).testB)
    
End Sub

'---------以下の内容をModule1に記述-------
Public structText() As structArray

Public Function UboundSp(v As Variant) As Long

    On Error Resume Next
    UboundSp = UBound(v)
    If Err <> 0 Then UboundSp = -1
    On Error GoTo 0
  
End Function

'---------以下の内容をClass1に記述-------
Public Type structArray
    testA() As String
    testB() As String
End Type

よろしくお願いします。


030  2005-11-16 09:40:49  No: 127710

なんか勘違いが多いですね。
ガッさんがおっしゃっているのは
「構造体を使わずにクラスを作成して」
という意味ですよ。
「クラスモジュールで構造体を宣言」とは一言もいっていません。

それと
>よって構造体の宣言を標準モジュールではなくUserControlモジュールでパブリック宣言して
>ActiveXコントロールを参照設定してやればよいかと。
はためしたんですか?


マルオ  2005-11-16 11:01:59  No: 127711

>なんか勘違いが多いですね。
すいません。確かにお叱りごもっともですね。

ガッさんの言いたいことは下記のようなことでよいでしょうか?
他にいい方法ありますでしょうか?

'---以下の内容をForm1に記述---
Private Sub Form_Load()

    ReDim structText(0) As New structArray
'---------------------------------------
' structArrayをクラス定義したので、不要
'    ReDim structText(0).testA(2) As String
'    ReDim structText(0).testB(2) As String
'---------------------------------------
    
    structText(0).testA(0) = "testA0-0"
    structText(0).testA(1) = "testA0-1"
    structText(0).testA(2) = "testA0-2"
    
    structText(0).testB(0) = "testB0-0"
    structText(0).testB(1) = "testB0-1"
    structText(0).testB(2) = "testB0-2"
    
    ReDim Preserve structText(1) As New structArray
'---------------------------------------
' structArrayをクラス定義したので、不要
'    ReDim Preserve structText(1).testA(1) As String
'    ReDim Preserve structText(1).testB(1) As String
'---------------------------------------
    
    structText(1).testA(0) = "testA1-0"
    structText(1).testA(1) = "testA1-1"
    
    structText(1).testB(0) = "testB1-0"
    structText(1).testB(1) = "testB1-1"

    Debug.Print UboundSp(structText)
    Debug.Print structText(0).count_testA
    Debug.Print structText(0).count_testB
'---------------------------------------
' structArrayをクラス定義したので、不要
'    Debug.Print UboundSp(structText(0).testA)
'    Debug.Print UboundSp(structText(0).testB)
'
'    Debug.Print UboundSp(structText(1).testA)
'    Debug.Print UboundSp(structText(1).testB)
'---------------------------------------

End Sub

'---以下の内容をstructArrayクラスに記述---

Option Explicit

Private m_testA() As String
Private m_testB() As String

Public Function count_testA() As Integer
    
    count_testA = UboundSp(m_testA)

End Function

Public Function count_testB() As Integer
    
    count_testB = UboundSp(m_testB)

End Function

Public Property Get testA(Index As Integer) As String
    
    If UboundSp(m_testA) < Index Then
        ReDim Preserve m_testA(Index) As String  '配列の確保
        m_testA(Index) = ""  '初期化
    End If

    testA = m_testA(Index)

End Property

Public Property Let testA(Index As Integer, ByVal NewValue As String)
    
    If UboundSp(m_testA) < Index Then
        ReDim Preserve m_testA(Index) As String '配列の確保
    End If

    m_testA(Index) = NewValue

End Property

Public Property Get testB(Index As Integer) As String
    
    If UboundSp(m_testB) < Index Then
        ReDim Preserve m_testB(Index) As String  '配列の確保
        m_testB(Index) = ""  '初期化
    End If

    testB = m_testB(Index)

End Property

Public Property Let testB(Index As Integer, ByVal NewValue As String)
    
    If UboundSp(m_testB) < Index Then
        ReDim Preserve m_testB(Index) As String '配列の確保
    End If

    m_testB(Index) = NewValue

End Property

'---以下の内容をModule1に記述---

Public structText() As New structArray

Public Function UboundSp(v As Variant) As Long

    On Error Resume Next
    UboundSp = UBound(v)
    If Err <> 0 Then UboundSp = -1
    On Error GoTo 0
  
End Function


K.J.K.  2005-11-16 17:51:31  No: 127712

単純に、

> Public Function UboundSp(v As Variant) As Long

Public Function UboundSp(v() As structArray) As Long

とするわけには行かないのでしょうか? 一般化する必要は
ないように思えます。


我龍院忠太  2005-11-16 18:33:58  No: 127713

実行時バインディングがだめだと言ってるんだから、ちょっと工夫して
事前バインディングにしたらいいのでは、たとえば

*****Module1側******
Public Const intStruct = 0
Public Const intString = 1
Public Type structArray
    testA() As String
    testB() As String
End Type
Public structText() As structArray

Public Function UboundSp(v() As structArray, dt() As String, dimNo As Integer) As Long
    On Error Resume Next
    If dimNo = intStruct Then
        UboundSp = UBound(v)   'ユーザー定義型
    Else
        UboundSp = UBound(dt)  '配列
    End If
    If Err <> 0 Then UboundSp = -1
    On Error GoTo 0
End Function

***Form1側****
Debug.Print UboundSp(structText, structText(0).testA, intStruct)
Debug.Print UboundSp(structText, structText(0).testA, intString)
Debug.Print UboundSp(structText, structText(1).testA, intString)
Debug.Print UboundSp(structText, structText(1).testB, intString)


マルオ  2005-11-16 21:55:05  No: 127714

K.J.K.さん。我龍院忠太さん。
回答ありがとうございます。

事前バインディングにしてあげればうまく動くんですか?
その発想は、全然ありませんでした。

我龍院忠太さんの方法で実施すれば、シンプルなコードで汎用性があるので、使いまわせますよね!(゜〇゜)

家帰ったら、早速試してみます。
また、報告させてください。

宜しくお願いします。


我龍院忠太  2005-11-16 22:11:05  No: 127715

投稿する前に確かめなかったので被ってしまいましたが、
K.J.K.さんの方法も基本的には私と同じに事前バインディング
を勧めています。


マルオ  2005-11-20 12:32:41  No: 127716

回答遅くなってすいません。

事前バインディングで試してみたところ、うまく行きましたので、
その方法にて、対処いたします。

みなさん、ほんとに回答ありがとうございました。
素人の私の質問に付き合っていただいて、非常に感謝です。
m(_ _)m


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加