VBGood网站全文搜索 Google

搜索VBGood全站网页(全文搜索)

VB爱好者乐园(VBGood)

 找回密码
 立即注册
搜索
楼主: tgy

[讨论] *再谈VB多线程和标准DLL问题*

  [复制链接]
发表于 2011-10-31 10:56:51 | 显示全部楼层
本帖最后由 sexfio 于 2011-10-31 11:21 编辑
tgy 发表于 2011-10-31 10:44
显示窗体只是我写的例子,当然也可以不显示窗体,根据自己需要.
关于参数,只能通过线程参数传进去,如果需传 ...


哦,麻烦帮我看下,我改了下,我想实现的是
创立3个线程,0,1,2
每个线程导入一个参数,分别是0,1,2,然后每秒这个数+1
然后在主窗体创立一个MSFlexGrid1用于显示
每行MSFlexGrid1的第2列显示每秒+1后的结果,我这样改貌似不对,应该怎么改呢?最好帮我改成结构体的,我想导入多个参数,谢谢了~~~

'窗体
Private Sub Command1_Click()
    Dim i&
    MSFlexGrid1.Cols = 4: MSFlexGrid1.Rows = 1
    For i = 0 To Val(Text1) - 1    'Text为创立的线程数
        MSFlexGrid1.AddItem i: j = i
        CreateThread 0&, 0&, AddressOf XXX, ByVal App.hInstance, 0&, 0
    Next
End Sub

'模块

Public j&

Private Declare Function GetTickCount Lib "kernel32" () As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Private Function Delay(sSeconds As Long)        '延时函数 Delay(毫秒)
    Dim Begin As Long
    Begin = GetTickCount
    Do: DoEvents: Sleep 1
    Loop Until GetTickCount - Begin >= sSeconds
End Function

Public Sub Main()
    Form1.Show
End Sub

Public Sub XXX(ByVal df As Long)    '线程函数
    init df
    '=====================================================================
    j1 = j: j2 = j
    Do
        Form1.MSFlexGrid1.TextMatrix(j1 + 1, 1) = j2
        j2 = j2 + 1
        Delay 1000
    Loop
    '=====================================================================
    CoUninitialize
End Sub

////////////////////////////////////////////////////////////////////////////////////////////////////////////
刚用了结构体
Public Type fio
    index As Long
    hInstance As Long
End Type

Private Sub Command1_Click()
    Dim i&, ff As fio
    MSFlexGrid1.Cols = 4: MSFlexGrid1.Rows = 1
    For i = 0 To Val(Text1) - 1
        MSFlexGrid1.AddItem i: j = i
        ff.index = i: ff.hInstance = App.hInstance
        CreateThread 0&, 0&, AddressOf XXX, ByVal ff, 0&, 0
    Next
End Sub

结果提示类型不正确,CreateThread 的第4个参数是导入参数吧?类型不是any吗?为什么会不正确?

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

点评

tgy
用结构体把j传进去  发表于 2011-10-31 11:20
回复 支持 反对

使用道具 举报

发表于 2011-10-31 11:32:05 | 显示全部楼层
本帖最后由 sexfio 于 2011-10-31 11:33 编辑

Private Sub Command1_Click()
    Dim i&, ff As fio
    MSFlexGrid1.Cols = 4: MSFlexGrid1.Rows = 1
    For i = 0 To Val(Text1) - 1
        MSFlexGrid1.AddItem i
        ff.index = i: ff.hInstance = App.hInstance
        CreateThread 0&, 0&, AddressOf XXX, ByVal ff, 0&, 0
    Next
End Sub

'模块

Public Type fio
    index As Long
    hInstance As Long
End Type

Private Declare Function GetTickCount Lib "kernel32" () As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Private Function Delay(sSeconds As Long)        '延时函数 Delay(毫秒)
    Dim Begin As Long
    Begin = GetTickCount
    Do: DoEvents: Sleep 1
    Loop Until GetTickCount - Begin >= sSeconds
End Function

Public Sub Main()
    Form1.Show
End Sub

Public Sub XXX(ByVal df As fio)    '线程函数
    init df.hInstance
    '=====================================================================
    j1 = df.index: j2 = j1
    Do
        Form1.MSFlexGrid1.TextMatrix(j1 + 1, 1) = j2
        j2 = j2 + 1
        Delay 1000
    Loop
    '=====================================================================
    CoUninitialize
End Sub

还是不行,哪错了?提示用户类型不能用
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-10-31 13:40:48 | 显示全部楼层
本帖最后由 tgy 于 2011-10-31 19:50 编辑

结构体不能用byval   
ff至少要为窗体级变量,或全局变量,不能是局部变量,道理很简单,局部变量的话,可能线程函数还没启动,这个变量已经被销毁了。
CreateThread 0&, 0&, AddressOf XXX,byval varptr(ff), 0&, 0


然后线程函数这样试下
Public Sub XXX( byref df As fio)    '线程函数

回复 支持 反对

使用道具 举报

发表于 2011-10-31 14:58:08 | 显示全部楼层
本帖最后由 sexfio 于 2011-10-31 14:59 编辑
tgy 发表于 2011-10-31 13:40
结构体不能用byval   
ff至少要为窗体级变量,或全局变量,不能是局部变量
CreateThread 0&, 0&, Addres ...


搞定了,代码如下
'窗体

Dim ff(2) As fio

Private Sub Command1_Click()
    Dim i&
    MSFlexGrid1.Cols = 4: MSFlexGrid1.Rows = 1
    For i = 0 To Val(Text1) - 1
        MSFlexGrid1.AddItem i
        ff(i).index = i: ff(i).hInstance = App.hInstance
        CreateThread 0&, 0&, AddressOf XXX, ByVal VarPtr(ff(i)), 0&, 0
    Next
End Sub

'模块

Public Type fio
    index As Long
    hInstance As Long
End Type

Private Declare Function GetTickCount Lib "kernel32" () As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Private Function Delay(sSeconds As Long)        '延时函数 Delay(毫秒)
    Dim Begin As Long
    Begin = GetTickCount
    Do: DoEvents: Sleep 1
    Loop Until GetTickCount - Begin >= sSeconds
End Function

Public Sub Main()
    Form1.Show
End Sub

Public Sub XXX(ByRef df As fio)    '线程函数
    init df.hInstance
    '=====================================================================
    Dim j1&, j2&
    j1 = df.index: j2 = j1
    Do
        Form1.MSFlexGrid1.TextMatrix(j1 + 1, 1) = j2
        j2 = j2 + 1
        Delay 1000
    Loop
    '=====================================================================
    CoUninitialize
End Sub


还有2个问题,一个是那个结构体ff如果不用数组貌似不行啊,为什么啊?????我的意思是这样CreateThread 0&, 0&, AddressOf XXX, ByVal VarPtr(ff), 0&, 0,这样不行呀
还有一个是直接关闭窗体,结束程序会弹出错误,应该怎么退出呢?CoUninitialize是干什么的?
求指教~~~~~~~~~~~~

点评

tgy
创建的线程还没有结束时,就退出了主窗体,肯定会出错的,因为子线程还在试图访问已经被卸载的主窗体,当然出错了,结束所有子线程,最后关闭窗体就不会出错了。记住,多线程编程要处处小心,呵呵 。  发表于 2011-10-31 19:45
CoUninitialize是干嘛用的?  发表于 2011-10-31 16:59
哦,那这样用数组满麻烦的呀,对了,退出那个问题怎么搞?现在循环中我关程序就弹错误窗口,怎么样退出一个线程呢?  发表于 2011-10-31 16:58
AddressOf XXX, ByVal VarPtr(ff), 0&, 0,这样肯定不行 因为你ff是几个线程公用的但是没做线程同步 主线程在改 ff的同时其他线程也在访问 ff 那肯定就出问题了,改成数组 每个线程都有一个单独的ff就没有问题了  发表于 2011-10-31 15:31
回复 支持 反对

使用道具 举报

发表于 2011-10-31 15:45:12 | 显示全部楼层
谢谢楼主
回复 支持 反对

使用道具 举报

发表于 2011-10-31 17:11:40 | 显示全部楼层
又遇到问题了,ff不能设置为动态数组,怎么搞的?
Dim ff() As fio

Private Sub Command1_Click()
    Dim i&
    MSFlexGrid1.Cols = 4: MSFlexGrid1.Rows = 1
    For i = 0 To Val(Text1) - 1
        ReDim Preserve ff(i)
        ff(i).index = i: ff(i).hInstance = App.hInstance
        MSFlexGrid1.AddItem i
        CreateThread 0&, 0&, AddressOf XXX, VarPtr(ff(i)), 0&, 0
    Next
End Sub

动态数组不能VarPtr吗?byval varptr(ff(i))也不行,是不是有其他办法?

点评

呵呵,是的,我刚才自己也研究出来了,每次Preserve 就把地址搞变了  发表于 2011-10-31 17:50
晕倒,你CreateThread 完了之后新的循环 ReDim Preserve ff(i)然后旧的VarPtr(ff(i))就变成无效内存地址了……  发表于 2011-10-31 17:25
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-10-31 17:37:18 | 显示全部楼层
sexfio 发表于 2011-10-31 14:58
搞定了,代码如下
'窗体

不用数组是可以的.仔细检查下,要保证线程函数访问此结构体时变量还在有效,不然肯定会有问题的.

点评

不用数组怎么改呢???  发表于 2011-11-1 01:03
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-10-31 17:49:11 | 显示全部楼层
本帖最后由 tgy 于 2011-10-31 17:50 编辑

修改下init函数的话,可以不带参数调用init函数的.
init函数修改如下:


Public Sub init(Optional ByVal hinstdll As Long)      '线程或DLL调用初始化VB环境函数 ,dll调用时hinstdll为DLL基地址,EXE多线程调用时为EXE基地址
    Dim fake As Long
    Dim lp As Long, lvb As Long
    Dim riid As UUID
    Dim aiid As UUID
    Dim ofac As Object
    Dim f0 As Long
    Dim fakehead As Long
    Dim ll As Long
    hh = hinstdll
    CreateIExprSrvObj 0, 4, 0
    Call CoInitialize(0)
    If hinstdll = 0 Then hinstdll = GetModuleHandle(0)
    With riid
        .data1 = 1
        .data4(0) = &HC0
        .data4(7) = &H46
    End With
    f0 = GetFakeH(GetModuleHandle(0))
    fakehead = GetFakeH(hinstdll)
    If f0 = 0 Then
        Call VBDllGetClassObject(GetModuleHandle(0), lvb, ByVal fakehead, aiid, riid, ofac)
    Else
        Call VBDllGetClassObject(hinstdll, lvb, ByVal fakehead, aiid, riid, ofac)
    End If
    App.Title = App.Title
    hh = hinstdll
End Sub

点评

tgy
设计这个参数是写DLL时用的,因为这个模块是写标准DLL和多线程通用的,所以弄了个参数。  发表于 2011-10-31 18:43
哈哈,你的init df.hInstance这句即使在生成的是exe里,带不带参数结果也没啥区别啊  发表于 2011-10-31 17:59
回复 支持 反对

使用道具 举报

发表于 2011-10-31 18:01:16 | 显示全部楼层
tgy 发表于 2011-10-31 17:49
修改下init函数的话,可以不带参数调用init函数的.
init函数修改如下:

满好的,开了100个线程没出啥问题,对了,怎么样能把那个模块里的XXX调用搞到主窗体里去?

点评

tgy
这也就是VB的弱点之:不支持(函数)指针,如果有函数指针这个问题就不是问题了  发表于 2011-11-1 14:17
tgy
这个可能就有点难度了,因为addressof只能取模块中的函数地址,不过可以变通一上,设计一个通用的模块函数来调用窗体中的函数,可以用VB内置函数callbyname 来完成,但同样增加调用的复杂度,甚至会得不偿失。  发表于 2011-10-31 18:49
回复 支持 反对

使用道具 举报

发表于 2011-11-7 09:41:31 | 显示全部楼层
弄个双线程,至少解决了,程序假死的问题,do events  实在是boring
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

文字版|手机版|小黑屋|VBGood  

GMT+8, 2022-6-29 02:42

VB爱好者乐园(VBGood)
快速回复 返回顶部 返回列表