类模块中不能用自定义结构么?
类模块中不能用自定义结构么?我在标准模块中声明了如下结构:
'用于类间消息传递,指明涉及变动的细节ID
Public Type DETAIL
lngDetailId As Long
strDetailTable As String
End Type
然后在类模块中这样用:
Public Event ValueChangedD(udtDetail As DETAIL, sngIncreased As Single, udtCT As ChangeType) '由CDetailList发出
'由CDetailList发出
Public Sub SendMessage_ValueChangedD(udtDetail As DETAIL, sngIncreased As Single, udtCT As ChangeType)
RaiseEvent ValueChangedD(udtDetail, sngIncreased, udtCT)
End Sub
编译后出错,提示:只有在公共对象模块中定义的用户定义类型才可以与变体类型强制转换 或传递给后期绑定 late-bound 的函数。
出错在下面这句:
RaiseEvent ValueChangedD(udtDetail, sngIncreased, udtCT)
“udtDetail”这个词高亮显示。这个raiseevent是后期绑定的函数么?如果是,像现在这种情况,我有什么办法传结构出去? --------------------编程问答-------------------- http://topic.csdn.net/u/20080507/12/DEE6229E-85EC-4D72-A95D-9A87530F9CB0.html
发现这里有个类似的问题,看看先。 --------------------编程问答-------------------- http://www.itzhe.cn/news/20080508/133277.html
唉,盗版CSDN的网站太多太多了。 --------------------编程问答-------------------- 看完了,还是有点不明白。在1楼那个帖子里tiger_zhao说:
Public的Class按照COM标准,是“工程外”可见的;定义在模块中的结构仅仅是工程内可见,当然不能作为公共类的参数类型。
但是,我试验的情况是:可以定义在模块中的结构作为公共类的参数类型,但就是不能用在raiseevent里。看下面的代码就可以通过编译:
Public Sub SendMessage_ValueChangedD(udtDetail As DETAIL, sngIncreased As Single, udtCT As ChangeType)--------------------编程问答-------------------- 呵呵。发现那个帖子最底下一兄弟居然用copymemory来解决这问题。 --------------------编程问答-------------------- MSDN------
udtDetail.lngDetailId = 0
' RaiseEvent ValueChangedD(udtDetail, sngIncreased, udtCT)
End Sub
Type 语句只能在模块级使用。使用 Type 语句声明了一个用户自定义类型后,就可以在该声明范围内的任何位置声明该类型的变量。可以使用 Dim、Private、Public、ReDim 或 Static 来声明用户自定义类型的变量。
在标准模块中,用户自定义类型按缺省设置是公用的。可以使用 Private 关键字来改变其可见性。而在类模块中,用户自定义类型只能是私有的,且使用 Public 关键字也不能改变其可见性。
--------------------编程问答-------------------- 真的非常奇怪。要知道我在类模块中用模块中定义的结构快1个月了,嘛问题都没出过啊?!
看我在basTree模块里定义的结构如下:
'用于DetailList维护细节数据
Public Type NODE_DETAIL
lngDetailId As Long
strName As String
sngValue As Single
bytRef As Byte
End Type
在CDetailList类模块里用这个结构,既没有编译错误过也没有运行错误过:
Private m_udtDetails() As NODE_DETAIL
Public Property Get Item(lngIndex As Long) As NODE_DETAIL
Item = m_udtDetails(lngIndex)
End Property
Public Sub Initialize(strDetailTable As String)
On Error GoTo Err_Initialize
Dim rstDetail As DAO.Recordset
Dim i As Long
Set m_cmm = GetCmm
Me.DetailTable = strDetailTable
'loadData
Set rstDetail = CurrentDb.OpenRecordset(m_strDetailTable)
With rstDetail
ReDim m_udtDetails(.RecordCount - 1)
i = 0
While Not .EOF
m_udtDetails(i).lngDetailId = .Fields("lngId")
m_udtDetails(i).strName = Nz(.Fields("strName"))
m_udtDetails(i).bytRef = .Fields("bytRef")
If m_blnBasic Then
m_udtDetails(i).sngValue = Nz(.Fields("sngValue"))
Else
m_udtDetails(i).sngValue = -1
End If
i = i + 1
.MoveNext
Wend
.Close
End With
Set rstDetail = Nothing
Exit Sub
Err_Initialize:
Stop
Debug.Print ERR.Description
Resume
End Sub
……
Private Sub m_cmm_ValueChangedS(udtDetail As DETAIL, sngNewValue As Single, udtCT As ChangeType)
Dim sngIncreased As Single
If udtDetail.strDetailTable <> m_strDetailTable Then Exit Sub
#If DEBUG_VERSION Then
gstrLog = gstrLog & "CDetailList:m_cmm_ValueChangedS "
#End If
'unfinished: sngIncreased尚未得到正确的值
Call m_cmm.SendMessage_ValueChangedD(udtDetail, sngIncreased, udtCT)
End Sub
--------------------编程问答-------------------- MSDN-----
对象模块中的用户定义类型不能为 Public。这个错误的起因与解决方法如下所示:
试图在对象模块中定义 Public 用户自定义类型。
将用户自定义类型的定义移到标准模块中,然后在对象模块或其它模块中声明该类型的变量。若只是要在它出现的模块中使用该类型,可以在对象模块中放入 Type...End Type 定义,并在定义之前加上 Private 关键字。
--------------------编程问答--------------------
谢谢老张。
俺现在是要在类模块中使用定义在标准模块中公用自定义类型,你的明白? --------------------编程问答-------------------- 俺现在是要在类模块中使用定义在标准模块中公用自定义类型。但有时能行(看0楼的例子),有时不能行(看3楼和6楼的例子)。需要对这一现象的科学解释。 --------------------编程问答-------------------- 这个帖子里有这样一段话:Only public user defined types defined in public object modules can be used as parameters or return types for public procedures of class modules or as fields of public user defined types.
我在6楼的代码就把自定义类型作为类模块函数的返回类型了,Public Property Get Item(lngIndex As Long) As NODE_DETAIL
用的好好的,没出错啊。
我在3楼的代码倒还没运行过,但是可以通过编译。这回是把自定义类型作为类模块函数的参数了。
貌似与这段英文的说法都矛盾。
--------------------编程问答-------------------- 可行的解决办法似乎是这样:使用ODL语言或EditTLB软件,生成一个tlb类型库,然后在工程里引用它。那么这个类型就可以在全局使用了,并且也可以作为函数的返回值. 这里有myjian写的例子:http://topic.csdn.net/u/20070601/22/CFD75329-1625-4133-829C-8AFB9AB40787.html
不过,我还是不知道为何有9楼那样的现象? --------------------编程问答-------------------- 在标准模块中用public定义,在类模块中要用到该自定义结构作为对外的参数时,用friend定义。
标准模块中
Public Type MyType
x As Single
y As Single
End Type
类模块中
Friend Sub setValue(v As MyType)
End Sub
--------------------编程问答--------------------
试了,编译错误,现象仍然同0楼。 --------------------编程问答--------------------
我这里没有通过编译.
只有过程或属性存在于标准模块中时,才可以通过.
在窗体及类模块中,无法通过. --------------------编程问答-------------------- TO MYJIAN:我在VBA里试的。不知是不是这个差别导致的结果不同?在类模块中用标准模块里定义的结构,这种用法,我用了很久了。直到昨天用了raiseevent才出错。
另外,弱弱的问一哈,那个VB6ApiTypeLibMaker.exe怎么用啊?我把下面的结构声明拷到API声明里:
'用于类间消息传递,指明涉及变动的细节ID
Public Type DETAIL
lngDetailId As Long
strDetailTable As String
End Type
然后点“转换”,它怎么没反应啊? --------------------编程问答-------------------- 就这错误当初害我倒写了不少代码呀..
其实有个简单的方案,不需要改什么代码
把Type改写成类模块
Public Type DETAIL
lngDetailId As Long
strDetailTable As String
End Type
'
'新建一个类模块,名为DETAIL
定义两个公共变量
Public lngDetailId As Long
Public strDetailTable As String
'把原来的Type删除
这时,只要在每个Dim X As DETAIL 后面加上一句Set X=New DETAIL
不考虑资源问题就用这个吧,LS很多方法都可以的,不过还是这个最方便..:) --------------------编程问答--------------------
也被编译器拒绝过...类似楼上的绕过去了,只不过我的变量是私有的.. --------------------编程问答-------------------- 帮顶,值得探讨 --------------------编程问答-------------------- 私有类的属性、方法始终在工程内调用,所以可以用私有 UDT。
而 RaiseEvent 走标准流程,无法保证事件接收者肯定是工程内对象,所以不能用私有 UDT。 --------------------编程问答--------------------
谢谢。请问如何知道我定义的类是私有类还是公有类? --------------------编程问答-------------------- Public Type DETAIL
lngDetailId As Long
strDetailTable As String
End Type
用一个结构模拟类代替结构。
一、结构模拟类
类名:DETAIL。
代码:
public lngDetailId As Long
public strDetailTable As String
二、用户类模块:
Public Event ValueChangedD(udtDetail As DETAIL, sngIncreased As Single, udtCT As ChangeType) '由CDetailList发出
dim m_udtCT as ChangeType
Private Sub Class_Initialize()
set m_udtCT=New ChangeType
End Sub
Private Sub Class_Terminate()
set m_udtCT=Nothing
End Sub
Public Sub SendMessage_ValueChangedD(udtDetail As DETAIL, sngIncreased As Single, udtCT As ChangeType)
RaiseEvent ValueChangedD(udtDetail, sngIncreased, m_udtCT)
End Sub
三、用户类实例代码:
Dim WithEvents objUserClass As UserClass
Private Sub Form_Load()
Set objUserClass = New UserClass
End Sub
Private Sub objUserClass_ValueChangedD(udtDetail As Single, sngIncreased As Single, udtCT As ChangeType)
MsgBox udtCT.lngDetailId
End Sub --------------------编程问答--------------------
Standard Exe 工程内的 Class 必定是私有的。
其它工程内的 Class 通过属性 Instancing 指定。 --------------------编程问答--------------------
类模块内自定义数据类型不能直接在vb中以公用接口方法输出
除非将那个类型放到模块中,并标示成 global 或 public 状态
原因是com中类型是严谨的,你输出了一个只有自己才知道的数据类型,
而对于外面引用你的代码,他们无法得到关于这个数据类型的任何说明
对于vb,就强制的设置成不允许这样做
对于类,他是一个封闭的代码堆,是COM的一个特性
--------------------编程问答--------------------
谢谢指点。
不过,,打击你一哈,看帖不仔细。
俺要用的自定义类型本来就没定义在类模块中。呵呵。 --------------------编程问答--------------------
是谁看的不仔细.......
我说结构放模块中是告诉你解决方法..............
一般情况下,在工程内想解决这个问题的最简单的办法就是写到模块中设置成全局类型
其实,好多vb本身的枚举,结构,常数什么的也都是放到了模块中,所以你才能直接调用
--------------------编程问答--------------------
呵呵。看9楼。 --------------------编程问答--------------------
我晕,回错贴了。。。。。。。。
我要回的是一个问的和这个问题相似的帖子,ff开多个标签,看了那个回了这个。。。
我也汗一回。。。 --------------------编程问答-------------------- 建议尽量用类来代替结构体 -> 就像Java的世界里一切皆是对象 --------------------编程问答-------------------- XUEXI YIXIA --------------------编程问答-------------------- 能啊。。。。。。。。。。。。。。。。 --------------------编程问答-------------------- 建议尽量用类来代替结构体 -> 就像Java的世界里一切皆是对象 --------------------编程问答--------------------
呵呵,精力充沛的人通常都同时做好几件事。 --------------------编程问答-------------------- VB的语法是有点怪,在VC中没有此现象 --------------------编程问答-------------------- up --------------------编程问答--------------------
C 可以编写非 COM 标准的程序,结构A 的指针可以直接当作结构B 的指针用。
灵活的相对面就是不安全。 --------------------编程问答-------------------- mark --------------------编程问答-------------------- 长见识了, --------------------编程问答-------------------- To slowgrace:
解决方法就是在工程外(tlb、dll)定义结构:
1)可以用 VC 的 idl 编译成 tlb。
2)也可以用《高级 Visual Basic 编程》附带的 EditTlb.exe 直接做成 tlb。
3)纯 VB 的方式就是在一个 ActiveX Dll 工程中定义结构。 --------------------编程问答-------------------- 学习! --------------------编程问答-------------------- 语法感觉有点怪怪的…… --------------------编程问答-------------------- 讨论这么激烈,参观一下! --------------------编程问答-------------------- 首先声明一下:VB6写出来的COM不是真正的COM,原因在这里不讨论.
那么来看看VB6对于COM是怎么支持的,想在VB6里面创建"COM"那么必须新建一个类模块,然后把属性,方法,事件通通写在
这个类模块中,想要对外暴露的接口,在VB6里面被申请为PUBLIC类型,Private,Friend都被视为保护类型对外不暴露
现在LZ的意思是,想在标准模块中,定义一个PUBLIC类型的结构体,然后在外部进程中对其进行使用,那么,回答是肯定的,
所有在标准模块中定义的PUBLIC类型,都被视为只能在进程内使用,不能被使用在外部进程中,这就是为什么同样是模块,
同样是定义了PUBLIC类型,一个可以在外部被使用,一个只能被进程内的类模块使用!
至于说到C/C++,,那是个标准COM,那么在定义时,如果是公共类型那么不管是进程内还是时程外,只要将其内存地址复制出来
(也就是常说的用指针指到那个内存地址的开头位置),那么你就能用,但是在C/C++里面还有二种类型是不能被外部进程使用
的,一个是私有类型,还有一个好像叫友员吧(我记不大清楚了名字:) 报歉),其中私有类型和VB6一样只能在相同的子进程中
使用,而友员那就有点和VB6中在标准模块中PUBLIC一样,可以在进程内使用.
呵呵,,乱说的,说的不好指出一下下! --------------------编程问答-------------------- 友元是个特例,似乎VB没有与之匹配的东西,要说有大概只能说是friend了,区别较大. --------------------编程问答-------------------- --------------------编程问答-------------------- VB 不是 COM 还有谁是 COM? --------------------编程问答-------------------- 真的COM?假的COM?
似乎使用"并不完全支持COM的所有特性"来说明比较合适吧?
我知道又要说继承了......... --------------------编程问答-------------------- vb 是的对象是标准对象,只是他不支持象c++那样的那么多的对象操作功能,如果熟悉对象原理,
也是可以通过copymemory 来实现大多数的c++功能的
只不过对象编程也有很多特性,对象虽然方便共享,但就对对象的实现方式来讲,
不同语言在实现对象的过程中,也有自己的方法或特殊的COM属性,所以并不一定能实现不同语言之间的共享
--------------------编程问答--------------------
感觉这个说法很新颖 --------------------编程问答--------------------
实现出来的东西做为技术研究还可以,应用价值不高,因为这需要编译器支持,
而用copymemory这类东西动态修改,编译器不一定能准确识别
导致函数不能正常工作或程序崩溃
其实,一直以来用copymemory动态绑定对象实例上,我一直有所怀疑,vb好像识别出了用copymemory实现的动态绑定,只是一种感觉,没有深入研究过。 --------------------编程问答--------------------
想用事件(event)传自定义类型,恐怕很难了,学学微软,常常在定义API时,传入(出)一个指针,还有一个长度,几乎可以代替一切,当然得用copymemory了--------------------编程问答-------------------- 学习学习。。。帮顶
'class1
Option Explicit
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal ByteLen As Long)
Public Event ValueChangedD(ByVal udtDetail As Long, ByVal nCount As Long, ByVal sngIncreased As Single)
Friend Sub SendMessage_ValueChangedD(udtDetail As DETAIL, sngIncreased As Single)
Dim bytArr() As Byte
ReDim bytArr(LenB(udtDetail) - 1)
udtDetail.lngDetailId = 111
udtDetail.strDetailTable = "xxxxxxxxxxxxx"
CopyMemory bytArr(0), udtDetail, LenB(udtDetail)
RaiseEvent ValueChangedD(VarPtr(bytArr(0)), LenB(udtDetail), sngIncreased)
End Sub
'
'Module1
Option Explicit
Public Type DETAIL
lngDetailId As Long
strDetailTable As String * 255 '需要用定长字符串,不定长得不到正确结果
End Type
'Form
Option Explicit
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal ByteLen As Long)
Dim WithEvents c As Class1
Private Sub c_ValueChangedD(ByVal udtDetail As Long, ByVal nCount As Long, ByVal sngIncreased As Single)
Dim d As DETAIL
CopyMemory d, ByVal udtDetail, nCount
Debug.Print d.lngDetailId, ":", d.strDetailTable
End Sub
Private Sub Command1_Click()
Dim d As DETAIL, s As Single
Set c = New Class1
d.lngDetailId = 12
d.strDetailTable = "asdf"
c.SendMessage_ValueChangedD d, s
End Sub
--------------------编程问答--------------------
VB6ApiTypeLibMaker貌似只能转API声明.
要做类型库,推荐使用EditTLB.EXE:
一,新建个ActiveX DLL工程,在那里面声明你要的自定义结构.
为了方便,添加个模块,再声明在里面吧.
二,编译出DLL,然后用EditTLB.exe打开,把类删除,只留你要的那个结构,再另存为TLB文件;
三,打开这个TLB文件,改一下CLSID(不能与DLL冲突了,或者你反注册那DLL也行),可以使用了...... --------------------编程问答-------------------- 模块里的似乎类型不能导出,,,写在公共类里试试,,我没试过 --------------------编程问答--------------------
先问句溜边儿滴:你干嘛声明两次copymemory啊?在标准模块里声明一个public的CopyMemory不好么? --------------------编程问答-------------------- 路过.............. --------------------编程问答-------------------- 正好想请教myjian问题呢。
刚才看到MSDN里提到MkTypLib这个工具,是用来做type library的。你用过么?貌似这个和你及TZ推荐的那个功能相似?不过的话,这个MkTypLib是正儿八经VB程序员指南里推荐的。 --------------------编程问答-------------------- 我给你那个工具只是一个语法转换器,实际还是调用的这个MKTYPLIB.EXE来生成的.
因为ODL语言掌握的人不多......... --------------------编程问答--------------------
谢谢。 --------------------编程问答--------------------
谢谢指教。你说的很好,VC俺不懂,你说的VB部分的内容和我最近学习到的知识相符。不过的话,我感觉你并没有看到俺的症结所在啊,呵呵。:
其实我的问题的根源不在自定义结构在哪里定义,而是在哪里使用。我的所有自定义结构都是在标准模块里以public声明的,所以,其实它们都是不能被外部使用的,但是可被进程内的类模块使用,所以我以前在类模块里用自定义结构都没问题。但是这回我在进程内使用了raiseevent函数,由于这个函数的接受对象可能来自外部进程,所以它不能使用私有的自定义结构,所以这里编译不能通过。具体可见Tiger_Zhao在19楼和22楼的解释。
--------------------编程问答--------------------
我不喜欢public --------------------编程问答--------------------
这个解释当时似乎懂了,现在再来看感觉还是懵懂。 --------------------编程问答-------------------- 哎。许多事不能深想啊。 --------------------编程问答--------------------
也就是我得在我的工程里引用这个定义了UDT的部件(.tlb 或 .dll),是吧? --------------------编程问答-------------------- 没错 --------------------编程问答--------------------
学习一把
补充:VB , 非技术类