转:诡异的VB多线程和回调函数(给VB程序做进度条真是烦人啊)

原帖出至:http://storespace0930.spaces.live.com/blog/cns!6BA56F0E11D65292!469.entry

2008/6/16
诡异的VB多线程和回调函数(给VB程序做进度条真是烦人啊)

最近做一个项目。项目流程中需要调用一个vc写的dll来做大运算量的工作。但是由于需要显示进度条,就把进度条窗口在vb中实现,打算让vc回调vb中的函数,用来实现进度条的滚动。试了一下效果不好,原因是vc的dll在处理是,vb的界面失去了响应,因此进度条也就失去了意义。解决的方法很明显,就是要为vc中的运算另开一个线程。

思路很简单,但做起来就异常麻烦。vb6根本就不具备多线程功能,没办法,借助API的 CreateThread函数吧。本来以为就这样结束了,但麻烦远远没有结束。线程创建了,但是dll中的函数却根本就不运行,弄了半天,程序还是不行。一调试,vb6环境就经常崩溃,弄得无比郁闷。上网一查才知道vb中的多线程是极其不稳定的,甚至你的IDE中调试运行可能没有任何问题,但是生成exe 文件后运行就出问题了。

那怎么办呢?那只能把创建线程移到VC的dll中实现了呗,相比起来这个就容易多了。但是在vb中调用的时候还是有问题,dll中的函数居然还是不运行。这个就搞笑了,试了半天才发现,在调用dll后,vb中的过程不能立即结束,如果立即结束的话,那么dll中的函数就不会运行。但只要在vb调用dll的函数中添加一句   Sleep 100   语句,则dll中的线程就可以建立了。然后我们就可以通过一个Timer控件过一段时间查询一下线程是否结束。

程序到此,多线程的路子基本通了。还差回调函数,就可以显示进度了。

回调函数看起来很简单啊,vb中用 AddressOf  把位于标准模块中的某个函数的地址传给dll,dll中再在适当的时候用这个地址回调vb中的函数。但一开始就不顺。发现vb传过去的地址和vc中得到的地址相比老是不对,折腾了半天,才发现在传递地址给dll中的某个函数时,应该把该地址按照 ByVal *** As Long  ,这样dll中就可以得到正确的地址了。

地址传对了,那vc中就按照如下方式声明函数即可:

    typedef void (__stdcall *FUNCPTR)(int i,BSTR FormTitle,BSTR currBlockName,int currNum,int TotleCount,int ProcessedFaces,int TotalFacesCount);//参数列表要与vb中的对应int→Long,BSTR→String
    FUNCPTR vbFunc;//定义一个函数,呆会就用这个函数进行回调
    vbFunc = (FUNCPTR)CallBackAddress; //CallBackAddress就是我们传递进来的VB函数地址

到此VC中的声明就成功了

记住吧VC中的string传递给VB前,一定要转成BSTR类型。具体做法:

    BSTR  temp;
    temp=A2BSTR(string.c_str());

然后把 temp 返回给VB前,再用SysAllocString ,即:

    vbFunc(0,SysAllocString(string1) ,SysAllocString(string2) ,0,0,0,1);

到此,你是不是以为进度条可以滚动了?恭喜,在VB6的IDE中进行调试运行时没有任何问题。但是!!!很遗憾,生成EXE后就不能运行了,一到回调的地方就会开始报错,这就让人崩溃了。调试时候好,exe报错,就是说你连错在什么地方都不好找,只能在堆栈中看到说某个系统dll出错。

就这个问题,我卡了一天,上网找了各种各样的资料后,才找到这么几句话(出自:http://bbs.pfan.cn/post-248211.html)

    1:“你是否中使用了AddressOf来获取函数地址,并把AddressOf获取的地址传递给多线程来使用?如果是的话那恭喜你!
        前些天,偶也是遇到了类似的问题,最后查遍MSDN,在微软的公告里说AddressOf是一个线程安全类型,也就是说AddressOf取得的地址只能由VB创建的线程进行使用,不能传递给由CreateThread”函数或其它的创建线程的函数进行使用。
    附上MSDN中的解释:http://support.microsoft.com/kb/198607/zh-cn”

    2:“没有为什么,因为VB6对多线程的支持不好。
    我也试过很多类似的莫名其妙的东西。
    一点经验:在多线程里面,尽量少操作非线程安全对象(例如COM部件,VB控件),多用些纯API。”

虽然对线程安全还不懂是什么意思,但是明白了回调函数中不应该对控件进行操作。我的错误就在于在回调中对进度条和标签控件进行了操作。知道了错在什么地方,也就很好解决了。再定义一堆全局变量呗,在回调时的操作只是把回传的值赋给这些变量,然后再由vb本身的程序按这些全局变量进行操作。到这里,滚动条应该可以滚动了吧^_^

但现实很无情,这下vb报溢出错误,一查,回调是回传的进度参数出了问题,dll中定义成 float ,vb中相应的定义成 single ,按理应该没有问题啊。一查看回传的值也就是   4.34919177519511E-03 这个数肯定在single的表达范围内啊,怎么会溢出呢?找了一下  刘炳文老师的《VB 6.0 win32 API 程序设计.pdf》在 P61找到这么一段话:

“……要保证所使用的DLL在浮点调用规范上与VB保持兼容。这里所说的兼容包括两个方面,一是编译程序使用的必须是IEEE浮点标准,二是编译程序必须遵守MicroSoft公司的浮点数调用规范”

我想大概是在调用规范上出了问题,但懒得解决了,既然这个浮点数是在VC中用两个整数除出来的,那我把整数直接传过来在VB中除不就可以了^_^

不过,虽然这里偷懒把这个问题解决了。但还有个现象我还是要提一下,就是同样是在VB中除,但是如果吧除法的表达式写在让DLL回调的的函数中,即DLL中的线程除完后返回,那么还是会溢出。而如果回调函数中只把这两个整数赋给全局变量。在其他的VB过程中用这两个全局变量相除得出浮点数的话,就没有问题了。看来线程的问题还不是那么简单的。但我实在是筋疲力尽了,赶紧吧这个项目做完,没时间理会这些东西了=。=

我的论文啊,你什么时候才能诞生呢???我的实习offer啊,你什么时候才能到我手中啊???

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个简单的示例程序,展示了如何在多线程中实现函数访问共享资源和数据同步: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> typedef struct { int value; pthread_mutex_t mutex; pthread_cond_t cond; } shared_data_t; void* thread_func(void* arg); void callback_func(void* arg) { shared_data_t* shared_data = (shared_data_t*) arg; pthread_mutex_lock(&shared_data->mutex); shared_data->value++; pthread_cond_signal(&shared_data->cond); pthread_mutex_unlock(&shared_data->mutex); } int main() { shared_data_t shared_data = {0, PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER}; pthread_t thread; if (pthread_create(&thread, NULL, thread_func, (void*) &shared_data) != 0) { fprintf(stderr, "Error creating thread.\n"); exit(1); } pthread_mutex_lock(&shared_data.mutex); while (shared_data.value == 0) { pthread_cond_wait(&shared_data.cond, &shared_data.mutex); } printf("Value updated by callback: %d\n", shared_data.value); pthread_mutex_unlock(&shared_data.mutex); pthread_join(thread, NULL); return 0; } void* thread_func(void* arg) { shared_data_t* shared_data = (shared_data_t*) arg; // Do some work... callback_func(shared_data); return NULL; } ``` 在这个程序中,我们定义了一个 `shared_data_t` 结构体,其中包含一个整数值 `value`,一个互斥锁 `mutex` 和一个条件变量 `cond`。我们使用互斥锁来保护共享资源 `value` 的访问,使用条件变量来实现线程之间的同步。 在 `thread_func` 函数中,我们用了函数 `callback_func`,并将共享数据结构体的指针作为参数传递给它。在 `callback_func` 中,我们先使用互斥锁锁定共享数据结构体,然后对 `value` 进行更新,并使用条件变量通知其他等待线程。最后,我们释放互斥锁。 在 `main` 函数中,我们启动一个新的线程,并等待函数更新共享数据。我们使用条件变量来等待线程通知,如果 `value` 的值仍然为 0,则线程将等待条件变量的信号。一旦条件变量被通知,我们再次锁定互斥锁来读取更新后的值,并释放互斥锁。 这个示例程序展示了如何在多线程中实现函数访问共享资源和数据同步的基本原理。当然,实际情况可能更加复杂,需要更多的同步机制来确保线程安全。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值