有谁能解决 Delphi 的这个 Bug?(200分)

  • 主题发起人 主题发起人 andin
  • 开始时间 开始时间
to andin:
>虽然 Delphi 已经创建了Canvas 对象,但是,还没有创建内存设备句柄,如果一个
>Canvas 对象不拥有内存设备句柄,这个 Canvas 对象毫无用处的,Delphi 的解决办法
>是:需要时(例如调用Canvas.TextOut),创建内存设备句柄
这是对的。

>虽然你的程序没有错误,但是执行效率很低,原因就在:内存设备句柄反复的创建、释
>放、创建、释放。。。,
呵呵,好象不太对,Canvas对象在调用实际的GDI函数时,会检查设备句柄是否可用,如不可用则会创建一个,但并不会反复的创建、释放的。
if csHandleValid in NeededState then
begin
CreateHandle; //即使不包含FCurrentBitmap.Canvas.Handle这里也只会进来一次
 
虽然我也不大懂,可是终于又在大富翁看到这样热烈的气氛,这样的贴子感到高兴,希望这样的贴子越来越多,重新回到曾经的大富翁时代!
 
to andin:
确实如你所说,bitmap需要每次创建内存设备句柄。
但你的代码调用了gethandle,它通过FreeContext调用了TBitmapCanvas.FreeContext,
代码如下:
procedure TBitmapCanvas.FreeContext;
var
H: HBITMAP;
begin
if FHandle <> 0 then
begin
Lock;
try
if FOldBitmap <> 0 then SelectObject(FHandle, FOldBitmap);
if FOldPalette <> 0 then SelectPalette(FHandle, FOldPalette, True);
H := FHandle;
Handle := 0;
DeleteDC(H);
BitmapCanvasList.Remove(Self);
finally
Unlock;
end;
end;
end;
你的写法好像达不到目的吧?
我觉得调用HandleNeeded说不定行!我对图像变成不熟,说的不一定对。
另外,Traveller说得对啊!其实这不能算bug
 
看到这么多朋友在讨论这个问题,真是让我很高兴,下面,我来一一回复:

TO:wr960204
千万不要生气,我根本就没有与你斗气,只是在与 Delphi 斗气,因为 Delphi 的三个问题:TBitmap 问题 + 从 Delphi5 升级到 Delphi6 问题 + DAX 错误问题,搞得损失挺大的,TBitmap 问题到这里算是彻底解决了;第二个问题,我只好花时间去重新调试我的程序;第三个问题,据说 D7 已经解决了这个 Bug,但是,我还没有测试过;你说,我能不怀疑 Delphi 吗?
至于你所说的美国航天局项目,具体内容是什么样的?我们根本就不知晓。但是,如果是 MIS 方面的项目,这我相信 Delphi 完全有这个能力。
 
To:Traveller
用 Delphi 开发了这两个游戏???这让我很震惊,我没有玩过这几个游戏,不过,根据我目前对 Graphics 单元的分析,效率奇低,不过,如果用的是 DirectX ,那就另外一回事情了,不过,为了用 DirectX ,需要翻译出 DirectX 头文件,这太没有必要了,为什么不直接用 VC 呢?
我对你的说法表示怀疑。
 
据说地球帝国是用delphi开发的,不过是听别人说的。
 
To:Traveller
Delphi 确实说过这一段话,但是,给人的感觉是:如果有多个线程同时往 Canvas 上写东西,那么必须用 Lock。我相信大家都是这么理解的,所以,我作了这么一个简单的测试程序;你怎么还认定 TBitmap 是线程安全的呢?打开 Delphi 的 VCL 源程序,你就知道了,随便访问一个 TBitmap 的方法或属性,都不是线程安全的。
 
TO:特尔斐
关于效率低(反复创建、释放。。。)的说法,是正确的,更具体的说是这样的:
当工作线程执行完 Unlock 时,如果时间片切换到主线程执行,主线程会自作主张得调用FreeMemoryContexts 函数将内存设备句柄给删除掉,当时间片切换到工作线程时,VCL 又得创建内存设备句柄,如此反复。。。,你说效率高不高?
 
To:wfzha
我确实达到了目的,成功的回避了 Delphi 的怪异处理办法;
你将两个不同含义的句柄:TBitmap.NeedHandled 只是取得 Bitmap 对象句柄,也就是 DDB 或 DIB 句柄,而 TCanvas 需要的是内存设备句柄,当用 Canvas 对象在 Bitmap 对象上操作时,必须同时需要这两个句柄,而 Delphi 的毛病就在:自作主张的删除内存设备句柄,根本就不告诉开发人员!
 
你看看FreeMemoryContexts的代码:
procedure FreeMemoryContexts;
var
I: Integer;
begin
with BitmapCanvasList.LockList do
try
for I := Count-1 downto 0 do
with TBitmapCanvas(Items) do
if TryLock then
try
FreeContext;
finally
Unlock;
end;
finally
BitmapCanvasList.UnlockList;
end;
end;
它就是调用FreeContext;
你访问handle属性时会调用我前面说的那一串,最后也是调用了FreeContext,有什么不同?
 
这两个游戏确实是delphi开发的,尤其是奇迹时代,是个delphi开发游戏的经典之作。
vcl是无法胜任游戏开发的,但是你见过mfc开发的游戏吗(不包括桌面小游戏)?游戏当然DirectX,这个没说的,呵呵。
 
to andin:
我又看了一边代码,却是我说的不行,但好像没有方法可行。
canvas执行画图的时候,无论如何都先调用
RequiredState([csHandleValid, csPenValid, csBrushValid]);
只要有csHandleValid,canvas就会调用createhandle,
createhandle一定会调用 H := CreateCompatibleDC(0);
没有方法跳过。我觉得,好像这样不会影响效率吧?否则为什么borland
会这么写呢?
另外,开发游戏只需要简单的编译器和directx就行,据说borland刚推出
delphi时没有简化版,写游戏的公司不会白掏那么多钱买delphi的,后来
borland意识到了这个问题,推出了一个只有几十美元的版本,就有公司采用
delphi写游戏了。
delphi的优势主要在vcl上,写游戏用不着vcl的。
 
继续看代码!又有新的感悟:
其实,你说的也对也不对,主线程确实调用了FreeMemoryContexts,但你的代码中
加上lock后,FreeMemoryContexts并不会释放DC,从前面帖子中可以看到,delphi调用
FreeContext;前,现判断trylock地返回值,如果返回true,才释放dc,什么时候返回
true呢?从下面trylock的源码中可以看出,只有FLockCount = 0;时才会返回true。
所以,你的代码中起作用的是lock, FCurrentBitmap.Canvas.Handle;没用。完全可以
去掉。不会造成dc被释放。
function TCanvas.TryLock: Boolean;
begin
EnterCriticalSection(CounterLock);
try
Result := FLockCount = 0;
if Result then Lock;
finally
LeaveCriticalSection(CounterLock);
end;
end;
 
To:wfzha
在我的线程构造函数里,在调用 Lock 方法之前,必须调用FCurrentBitmap.Canvas.Handle,如果不这样作,只有一个效果:工作线程直接进入死锁状态,为什么呢?因为:在构造函数里调用了Lock,实际上就是在主线程里调用了Lock,然后,工作线程在执行Canvas.TextOut时,发现Canvas没有内存设备句柄,必然要调用 Lock 锁定Canvas,准备创建内存设备句柄,这不就形成了死锁吗?

相反,如果在构造函数里,在 Lock 之前,执行 Canvas.Handle,就会申请到内存设备句柄,调用Lock后,主线程再也不会释放这个句柄,在工作线程里,执行TextOut时,因为已经存在内存设备句柄,所以,VCL 不会调用 Lock。
附带的补充一点,如果在工作线程里,调用 LoadFromFile 之类的函数,必然会引起死锁的问题
 
客观得说,Delphi 的编译器是非常不错的,效率比 VC 要好;
但是,在计算机速度足够用的今天,这个对于我们来说,没有多大的意义。

Delphi 的优势在:以丰富的 VCL来实现快速软件开发。可是,VCL有这样或那样的问题,而且不断的升级,升级后,相互不兼容,最让人不可理解的是,Delphi 还专门升级帮助文档,这反映出了 Borland 公司对 SDK 文档的重视程度不够,或者说,Borland 公司的 SDK 文档小组水平一般,所有的这些,让我不再迷信 Delhpi,不知道大家的看法如何?
 
to andin:
>关于效率低(反复创建、释放。。。)的说法,是正确的,更具体的说是这样的:
>当工作线程执行完 Unlock 时,如果时间片切换到主线程执行,主线程会自作主张得调用
>FreeMemoryContexts 函数将内存设备句柄给删除掉,当时间片切换到工作线程时,VCL 又>得创建内存设备句柄,如此反复。。。,你说效率高不高?
经过测试发现你的说法有误,在进入工作线程的循环之前已经调用了CurrentBitmap.Canvas.TryLock,当主线程自作主张得调用FreeMemoryContexts 函数时发现Canvas的锁计数(FLockCount)不为0时不会删除内存设备句柄。所以,在该工作线程的生命周期内Canvas的内存设备句柄只会在调用FCurrentBitmap.Canvas.TextOut(0,0,IntToStr(number))时创建一次,直到线程结束运行才会被释放,不知你以为然否?
 
to andin
对不起,我前面关于gethandle的代码看错了,看成tbitmap.gethandle的了。[:D]
转了一大圈又回到了原来的问题。你说的对,是要调用一下,我试了一下,只要是调用
dc的都行,不用就不行!
to 特尔斐
如果没有canvas.handle的话是会删掉的。因为加不上lock,加上线程死掉
 
今天看了不少代码,收获不小啊!
 
用VC和Delphi开发游戏都是一样的。MFC的图形处理效率更低。
没有哪款游戏使用MFC的,失去了类库用SDK开发VC和Delphi只有语法上的差别

上面我说的美国航天局的项目确实是真的。但你不要把哥伦比亚号事件和Delphi开发拉上关系系呀。
先不说那个,我问你VC开发过的大型项目你知道几个?一般开发商都不会说开发工具是什么的。你怎么就知道没有用Delphi开发的?
介绍几款Delphi开发的几款经典游戏给你看看
http://turbo.gamedev.net/
说明:专业的Borland编译器游戏开发网站,站长水平很高,并且用Delphi开发了《疯狂保龄球》、
《过山车大亨》《迷你高尔夫》等游戏!几乎每日更新!
http://www.digital-tome.com/
说明:《突袭阿瓦隆》一个用Delphi开发的商业游戏,国内的盗版竟然说他是《暗黑破坏神》的资料片!
http://www.ageofwonders.com/aow.html
http://aow2.godgames.com/welcome.html
说明:《奇迹时代2》,Delphi开发的角色扮演策略游戏,获得E3最佳游戏大奖!1代中文版已经发行!
http://groups.yahoo.com/group/JEDI-JWars/
说明:《侏罗纪之战》雅虎俱乐部,开放源码的Delphi即时战略游戏和地图编辑器!推荐加入!
http://c-evo.org/
说明:拥有13个民族、标准人工智能、基于《文明》的策略游戏《文明进化史》!
Borland Delphi 源码开放!推荐!
http://www.frogcity.com/index.html
说明:《贸易帝国》策略游戏,使用Delphi作为开发工具。
http://www.kharne.net/
http://sourceforge.net/projects/kharne
说明:开放源码的《龙与地下城》RPG游戏,全部Delphi源码!
还有8月30日即将发布的金山WPS Office 2003 就是用Delphi/Kylix开发的,跨Win/Lin平台的。以前的WPS Office可是VC/MFC的。现在全改Delphi了。嘿嘿!
 
To:特尔斐
你的说法完全正确,在线程的循环体内,内存设备句柄不会被释放,只是在线程一个循环执行完毕后,如果内存设备句柄极有可能被释放;
针对这种情况,我的作法就是:在工作线程开始运行之前,申请到内存设备句柄,然后锁定这个句柄,直到工作线程退出为止。
 
后退
顶部