听起来您的 Socket 变量在表单的代码隐藏中声明为 Public 字段。
表单是带有可视化设计器的类模块。作为类模块,当您使用一个类模块时,您就是在使用类的一个实例。
如果您在任何类模块(即任何ThisWorkbook、Sheet1、UserForm1 或Class1 模块)中声明Public Socked As Object,那么是的,该对象是Public,但这并不成立它全局:公共字段属于它声明的类的实例。
这使得 Socket 标识符在表单的代码隐藏之外的任何地方使用时,实际上是一个未声明的变量 - 使用 Option Explicit 在编译时捕获这个错误的荣誉,否则很可能直到运行时才被注意到,其中未声明的 Socket 局部变量将是一些 Variant/Empty 值,并且某处某处可能不会喜欢它并且会提出了一些错误,可能与问题的实际根源相去甚远!
您有多种选择。
1。将Socket 设为实际的全局变量
Socket 目前属于我称之为UserForm1 的东西。由于其他组件需要这条数据,您可以通过将声明移至 标准模块 (.bas) 来扩展其范围:
'Module1.bas
Option Explicit
Public Socket As Object
现在只需从 UserForm1 中删除声明,一切都应该“正常工作”。
除了,它是一个全局变量:任何地方的每个人都可以随时覆盖这个对象引用。
这并不理想。
2。让其他代码使用它所在的Socket
Socket 属于 UserForm1。根据您如何/是否实例化该表单,您可能可以将其视为全局状态,因为所有UserForm 模块都有一个VB_PredeclaredId 隐藏属性设置为True,这正是您能够做到的这样做:
UserForm1.Show
当你这样做时,UserForm1 对象实例由 VBA 在 . 取消引用运算符处创建,然后调用 Show 方法。 如果实例不自毁,那么当表单被隐藏(未卸载)后执行返回时,你就可以访问所有内容了Public关于它:
Dim theSocket As Object
Set theSocket = UserForm1.Socket
现在这显然带有一个主要警告:您需要采取措施确保 VBA 不会重新生成一个全新的实例(使用全新的 Socket 引用,设置为 Nothing)当. 解引用运算符在该指令中运行。
您可以通过处理QueryClose 事件并显式取消实例的销毁来做到这一点:
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
Cancel = True
Me.Hide
End If
End Sub
如果你有一个OkButton 做这样的事情:
Private Sub OkButton_Click()
Unload Me
End Sub
或者更糟糕的是,像这样:
Private Sub OkButton_Click()
Unload UserForm1
End Sub
然后改成这样:
Private sub OkButton_Click()
Me.Hide
End Sub
这很关键:您必须 Hide 表单,而不是 Unload 它,并且您不能让它在执行返回之前被销毁 - 调用代码需要才能使用与用户交互的实例完全相同。
3。自己控制
UserForm1 是一个类,就像任何其他类一样。 New 自己解决而不是让 VBA 负责:采取与上述完全相同的预防措施,但现在假装没有默认实例:
Public Sub DoSomething()
With New UserForm1
.Show
Dim theSocket As Object
Set theSocket = .Socket
End With
End Sub
请参阅我的UserForm1.Show 文章了解更多信息和详细信息。
4。使用单独的Socket 对象
如果表单的Socket 对象没有在该表单之外使用任何业务,那么其他代码也定义和设置Socket 来使用可能更有意义?
变量的范围应该尽可能的小,尤其是当我们在查看共享资源时,比如一个套接字连接:如果不同的程序Set这个套接字指向不同的东西,那么唯一的目的就是该变量为Public 是为了避免在需要套接字的任何地方声明它的击键,这是滥用全局状态并且会在后端咬你,一天或一天。
从实用的角度讲,表单无论如何都不知道如何连接套接字:应该有一个专门的模块来处理这个问题,每个需要与套接字通信的人都应该通过该模块。