首页 VB6多线程

VB6多线程

举报
开通vip

VB6多线程创建多线程的测试应用程序 为了测试和调试进程内部件(.dll 和 .ocx 文件) ,需要一个多线程的客户应用程序。创建 一个简单的多线程应用程序的步骤如下: 打开一个新的 ActiveX EXE 工程,将默认的类模块命名为 MainApp 。把 MainApp 的 Instancing 属性设成为 PublicNotCreatable 。 MainApp 对象将占有这个应用程序的第一个 线程,并显示主用户界面。 在“工程属性”对话框的“通用”选项卡上,在“启动对象”框中选择“Sub Main” ,在“线 程模块...

VB6多线程
创建多线程的测试应用程序 为了测试和调试进程内部件(.dll 和 .ocx 文件) ,需要一个多线程的客户应用程序。创建 一个简单的多线程应用程序的步骤如下: 打开一个新的 ActiveX EXE 工程,将默认的类模块命名为 MainApp 。把 MainApp 的 Instancing 属性设成为 PublicNotCreatable 。 MainApp 对象将占有这个应用程序的第一个 线程,并显示主用户界面。 在“工程属性”对话框的“通用”选项卡上,在“启动对象”框中选择“Sub Main” ,在“线 程模块”框中选择“每个对象对应一个线程” ,并输入一个具有唯一性的工程名称。 (工程名 称决定类型库的名称;如果两个应用程序的类型库名称相同的话将会出现问题。 ) "ThreadDemo" 是下例所用的工程名称。在“部件”选项卡上,在“启动模式”框中选择“独 立方式” 。 增加一个窗体,将它命名为 frmProcess ,并将其 Visible 和 ControlBox 属性设成 False 。 这个窗体将以隐藏窗口的方式运行,其中 Sub Main 用来标识该进程的主线程。这 个窗体不需要代码。 在工程中增加一个标准模块。 把声明、 Sub Main 过程、 以及下面显示的 EnumThreadWndMain 过程放在这个模块中。正如在相应的文字和代码注释中所说明的,在启动应用程序以及每次 创建一个新的线程时,都要执行 Sub Main 。 Sub Main 的示例代码演示了如何标识第一个 线程,这样就能知道何时创建 MainApp。 增加一个窗体,将它命名为 frmMTMain 。这个窗体为这个测试应用程序提供主用户界面。在 这个窗体中增加简单的声明, 并把紧在 “测试应用程序的多个实例” 标头上面的 Form_Unload 事件也加进去。 在 MainApp 的 Class_Initialize 事件过程中增加代码以显示 frmMTMain。详见下面的代 码。 要创建另外的测试线程,则在工程中应该至少有一个 Instancing 属性被设成 MultiUse 的 类。增加一个类模块和一个窗体,插入“创建新线程”标头下的代码。由于这个工程选择了 “每个对象对应一个线程” 因此每个在外部创建的公共对象都会启动一个新的线程。 , 这就是 说,可以通过使用 CreateObject 函数创建带程序标识符( ProgID )的 MultiUse 类的实 例,来创建一个新的线程,见相应文字中的说明。 向 frmMTMain 中增加代码, 通过创建所定义的 MultiUse 类的实例来创建新的线程。 有关代 码在下面这个示例的“创建新线程”标头下。 开发环境不支持多线程。如果按 F5 键运行工程,所有的对象将被创建在同一个线程中。为 了测试多线程的行为,必须编译 ActiveX EXE 工程,然后运行最终的可执行程序。 重点 为了保证每个新的 MultiUse 对象都能启动一个新线程, 必须使用 “每个对象对应一个 线程”选项而不能用“线程缓冲池”选项。 在 Sub Main 中决定主线程 每个新的线程都会执行 Sub Main 。这是因为 Visual Basic 为每个线程(即每个单元)都 维护了一个全局数据的独立副本。为了初始化线程的全局数据,必须执行 Sub Main 。这就 是说如果 Sub Main 加载了一个隐藏的窗口,或者显示了应用程序的主用户界面,那么在创 建每个新线程时都会加载这些窗体的新副本。 下面的代码用来判断 Sub Main 是不是在第一个线程中执行,这样可以只加载一次隐藏的窗 体或者只显示一次测试应用程序的主用户界面。 ' 被隐藏窗口的标题的根值 Public Const PROC_CAPTION = "ApartmentDemoProcessWindow" Public Const ERR_InternalStartup = &H600 Public Const ERR_NoAutomation = &H601 Public Const ENUM_STOP = 0 Public Const ENUM_CONTINUE = 1 Declare Function FindWindow Lib "user32" Alias "FindWindowA" _ (ByVal lpClassName As String, ByVal lpWindowName As String) As Long Declare Function GetWindowThreadProcessId Lib "user32"_ (ByVal hwnd As Long, lpdwProcessId As Long) As Long Declare Function EnumThreadWindows Lib "user32" _ (ByVal dwThreadId As Long, ByVal lpfn As Long, ByVal lParam As Long) _ As Long ' 通过 EnumThreadWindows 取得窗口句柄。 Private mhwndVB As Long ' 用来标识主线程的隐藏窗体。 Private mfrmProcess As New frmProcess ' 进程标识符。 Private mlngProcessID As Long Sub Main() Dim ma As MainApp ' 借用一个窗口句柄来获得进程 ' 标识符(请参阅下面 EnumThreadWndMain 的回调) 。 Call EnumThreadWindows(App.ThreadID, AddressOf EnumThreadWndMain, 0&) If mhwndVB = 0 Then Err.Raise ERR_InternalStartup + vbObjectError, , _ "Internal error starting thread" Else Call GetWindowThreadProcessId(mhwndVB, mlngProcessID) ' 进程标识符使隐藏窗口的标题具有唯一性。 If 0 = FindWindow(vbNullString, PROC_CAPTION & CStr(mlngProcessID)) Then ' 找不到窗口,因此这是第一个线程。 If App.StartMode = vbSModeStandalone Then ' 用唯一的标题创建隐藏窗体。 mfrmProcess.Caption = PROC_CAPTION & CStr(mlngProcessID) ' MainApp 的初始化事件( Instancing = ' PublicNotCreatable )显示主用户界面。 Set ma = New MainApp ' (如果没有对 MainApp 的全局引用,那么 ' 关闭应用程序就更加简单;否则 MainApp 应该 ' 把 Me 传递给主用户窗体,这样 ' 该窗体就能保证 MainApp 不被终止。 ) Else Err.Raise ERR_NoAutomation + vbObjectError, , _ "Application can't be started with Automation" End If End If End If End Sub ' EnumThreadWindows 所使用的回调函数。 Public Function EnumThreadWndMain(ByVal hwnd As Long, ByVal _ lParam As Long) As Long ' 保存窗口句柄。 mhwndVB = hwnd ' 只需要第一个窗口。 ' 一发现窗口就停止迭代。 EnumThreadWndMain = ENUM_STOP End Function ' MainApp 在它的 Terminate 事件中调用这个子程序; ' 否则隐藏窗体将使 ' 应 应用程序免于被关闭。 Public Sub FreeProcessWindow() Unload mfrmProcess Set mfrmProcess = Nothing End Sub 注意 这种以来标识第一个线程的技术在 Visual Basic 将来的版本中可能会有问题。 可以看到 Sub Main 在第一次以后对于任何线程都不再有任何动作。在增加创建 MultiUse 对象的代码(以便启动后继的线程)时,应该确保包含了初始化这些对象的代码。 EnumThreadWindows 和回调函数 EnumThreadWndMain 一起使用,以便能确定 Visual Basic 为其内部使用而创建的一个隐藏窗口的位置。这个隐藏窗口的窗口句柄被传递给 GetWindowThreadProcessId,该函数返回进程标识符。进程标识符将被用来创建由 Sub Main 加载的隐藏窗口 (frmProcess) 的唯一标题。后继线程检测到这个窗口后就能知道它们不需 要再创建 MainApp 对象了。这种转换是必需的,因为 Visual Basic 没有提供识别应用程序 主线程的方法。 MainApp class 在其 Initialize 事件中显示测试应用程序的主窗体。MainApp 应该把它的 Me 引用传递给主窗体,这样该窗体就能保证 MainApp 不被终止。从主用户界面可以创建所 有的后继线程。 MainApp 的 Instancing 属性设成 PublicNotCreatable 能有助于避免显 将 示两个用户主界面的窗体。 下面是 MainApp 类和它的相关窗体(上面步骤 5 和 6)的简单的示例: ' MainApp 类的代码。 Private mfrmMTMain As New frmMTMain Private Sub Class_Initialize() Set mfrmMTMain.MainApp = Me mfrmMTMain.Caption = mfrmMTMain.Caption & " (" & App.ThreadID & ")" mfrmMTMain.Show End Sub Friend Sub Closing() Set mfrmMTMain = Nothing End Sub Private Sub Class_Terminate() ' 清理隐藏窗口。 Call FreeProcessWindow End Sub ' frmMTMain 窗体的代码。 Public MainApp As MainApp Private Sub Form_Unload(Cancel As Integer) Call MainApp.Closing Set MainApp = Nothing End Sub 测试应用程序的多个实例 在隐藏窗口的标题中包含进程标识符能够使测试应用程序的多个实例互不影响地运行。 如果调用了 CreateObject ,那么所创建的公有类的实例将会位于当前应用程序实例的一个 线程上。 这是因为在寻找其它正在运行的能够提供该对象的 Exe 部件之前, CreateObject 总 是试图在当前应用程序中创建对象。 单元的有用属性 把进程标识符作为包含 Sub Main 的模块的只读属性将是有用的: '测试应用程序中不需要这些代码 Public Property Get ProcessID() As Long ProcessID = mlngProcessID End Property 这使得线程上的所有对象都能通过调用非限定的 ProcessID 属性来取得进程标识符。同样, 以这种方式将 Boolean IsMainThread 属性显露出来也是有用的。 创建新线程 “每个对象对应一个线程”选项使每个在外部创建的公有对象——即使用 CreateObject 函 数创建的对象——都会启动一个新的线程。要创建一 个新线程,可以简单地使用一下某个 MultiUse 类的程序标识符 (ProgID): '在测试应用程序中不需要包含这些代码 Dim tw As ThreadedWindow Set tw = CreateObject("ThreadDemo.ThreadedWindow") 这时变量 tw 包含了对一个新线程中的对象的引用。所有使用 tw 对这个对象的属性和方法 的调用都会导致交叉线程调度的额外开销。 注意 用 New 操作符创建的对象不是在新建的线程中创建。 它驻留在执行 New 操作符的同一 个线程中。请参阅“ 设计 领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计 多线程进程外部件”和“在 Visual Basic 部件中对象创建是如何 工作的” 。 为了确保在所有其它线程结束前 MainApp 不终止, 可以给每个公有类设一个 MainApp 属性。 如果对象在新线程中创建了一个 MultiUse 对象,那么作为初始化过程的一部分,它可以把 对 MainApp 对象的引用传递给新对象。 (也还可以向 MainApp 传递一个对新对象的引用, 这 样 MainApp 就能有一个对所有控制该线程的对象的引用集合了; 但是要记住这可能会产生循 环引用。请参阅“处理循环引用”) 。 如果希望控制一个线程的类来显示一个窗体,那么应该向它提供显示窗体的 Initialize 方 法(不要和 Initialize 事件混淆)或 Show 方法。不要在 Class_Initialize 事件过程中 显示窗体, 因为这样会在创建类的实例时产生时序错误。 下面是的代码是关于一个 MultiUse 的 ThreadedWindow 类和它的窗体的一个很简单的实例: ' 一个 MultiUse 的 ThreadedWindow 类的代码。 Private mMainApp As MainApp Private mfrm As New frmThreadedWindow Public Sub Initialize(ByVal ma As MainApp) Set mMainApp = ma Set mfrm.ThreadedWindow = Me mfrm.Caption = mfrm.Caption & " (" & App.ThreadID & ")" mfrm.Show End Sub Friend Sub Closing() Set mfrm = Nothing End Sub ' frmThreadedWindow 窗体的代码。 Public ThreadedWindow As ThreadedWindow Private Sub Form_Unload(Cancel As Integer) Call ThreadedWindow.Closing Set ThreadedWindow = Nothing End Sub 下面的代码段显示了如何初始化 ThreadedWindow 对象: '测试应用程序的主窗体( frmMTMain )代码。 Private Sub mnuFileNewTW_Click() Dim tw As ThreadedWindow Set tw = CreateObject("ThreadDemo.ThreadedWindow") ' 告诉新对象显示它的窗体,并 ' 将一个对主应用程序 ' 对象的引用传递给它。 Call tw.Initialize(Me.MainApp) End Sub 如果有很多可以控制线程的类,那么可以通过定义包含 Initialize 方法的 IApartment 接 口来使代码更通用。 在实现每个类的 IApartment 时, 可以为每个类提供适当的 Initialize 方法。下面是创建线程的代码实例: '测试应用程序中不需要这些代码 Private Sub mnuFileNewObject_Click(Index As Integer) Dim iapt As IApartment Select Case Index Case otThreadedWindow Set iapt = CreateObject("ThreadDemo.ThreadedWindow") ' (其它情况……) End Select ' 初 始化对象的公用代码。 Call iapt.Initialize(MainApp) End Sub 注意 可以通过在独立的类型库中定义接口来产生一个只有多线程应用程序知道的 IXxxxApartment 接口。在 ActiveX Exe 工程中,需要设置对该类型库的引用。 保持对线程对象的引用 为了确保能正确地关闭一个多线程应用程序,对于用来创建和控制线程的所有 MultiUse 对 象都必须仔细保存对它们的引用情况。 应该清楚地定义对象的存活期目标。 举例来说, 考虑一个显示窗体的 MultiUse 对象的情况。 管理对象存活期的最容易办法是让对象向窗体传递一个 Me 引用;这样窗体就能够保持对象 一直存活。如果用户关闭了窗体,窗体的 Unload 事件必然将所有对这个 MultiUse 对象的 引用设成 Nothing ,这样对象就能终止并清理它对窗体的引用了。 (最好为 MultiUse 对象 提供一个友元方法来清理对窗体的引用和所有其它对内部对象引用;窗体的 Unload 事件调 用这个方法。 ) 如果控制线程的对象在线程中使用 New 操作符创建了另外的对象, 那么应确保清理对这些对 象的引用。对于在线程中创建的这些对象,在释放对它们的引用之前线程是无法关闭的。打 开的线程要消耗系统资源。 友元方法不能在线程间使用 由于友元属性和方法不是类的公有接口的一部分,因此不能在其它线程中调用它们。对象之 间交叉线程的调用只限于声明为 Public 的属性和方法。 重入 如果由于调用 DoEvents 、显示模态窗体,或者对其它线程中对象进行辅助调用而使对象的 某个方法被移交控制,那么第二个调用者就可以在第一个调用结束之前进入该方法的代码。 如果这个方法使用或修改了属性值或模块级变量,那么这种调用将导致该对象的一种无效内 部状态。要防止重入,可以: 避免移交。 为每个方法维护一个模块级布尔标志。在开始一个方法时,它测试这个标志来决定此方法是 否正在运行。如果没有,方法就将这个标志设成 True 并继续;否则就产生一个错误。在方 法结束或由于任何原因退出时必须仔细地关闭这个标志。 编写重入方法——即不依赖于模块级数据的方法。 异步任务 Visual Basic 没有提供分支执行的途径——就是在一个线程中用某个方法调用一个新的线 程,然后立刻在原来的线程中继续处理。通过让原来的方法调用打开一个计时器然后立即返 回,可以在测试应用程序中模拟这种行为。当发生计时器事件时,可以将计时器关闭并执行 异步处理。这种技术在 “异步回调和事件”中讨论,同时在 Coffee 示例应用程序中有演示 (请参阅“创建 ActiveX Exe 部件”。 ) 使用多线程测试应用程序 要测试单元线程化的部件,必 编译这个多线程测试应用程序,这是因为 Visual Basic 开 发环境目前不支持执行多线程。如果有 Visual Studio ,那么可以利用它将测试应用程序编 译成带有调试信息的本机代码,这样就可以使用 Visual Studio 的调试程序。
本文档为【VB6多线程】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_941785
暂无简介~
格式:doc
大小:26KB
软件:Word
页数:7
分类:生活休闲
上传时间:2019-04-18
浏览量:410