如何在主程序和DLL间传递TBitmap对象?(200分)

  • 主题发起人 主题发起人 xifengge
  • 开始时间 开始时间
X

xifengge

Unregistered / Unconfirmed
GUEST, unregistred user!
本人自己写了一个图像处理控件,为了方便追加功能,所以部分处理放到DLL中进行。
处理内容可能包括:改变BITMAP的widht,height;修改Palette;以及其他绘图操作。
但是如何在DLL中将BITMAP对象修改后传回将主程序,一直找不到好的办法。
哪位大侠有好的办法请赐教,本人只有288分了,如果能圆满解决必倾囊相送。如果嫌少待本人挣了分再补送,多谢!
【注:文件映射的办法在处理大尺寸位图时效率太低,所以不予考虑】
 
这个问题主要DLL能返回一个对象,有了对象就好办了。
不过在返回对象时就需要DELPHI里PBL同时发布,这样就可行。
 
传入时用pointer(BitMap),接收的时候直接就是BitMap:=接收的值
delphi的对像都是指针,传递指针过去,只要接收的类型和传入的类型相同就可以了
 
正确的做法是在exe和dll之间传递windows标准数据类型。
直接传送对象的做法受限于同一版本编译器生成的二进制映像(exe和dll),如果一定要这样做,编码时还要考虑是否用bpl,不用bpl的话,还考虑类的register--具体原因参考组件编写方法。
我看了Graphics单元中关于TBitmap的说明和部分代码,我个人观点是,有两个方法可以实现你的要求:
1.传递Handle属性进行处理,主程序生成TBitMap实例,并传Handle入dll,dll中另外生成一个TBitMap实例,并使用同一个Handle。。。。。。
2.用TMemoryStream传递Data属性(指针),通过流机制进行处理...
 
to: bbscom & boy2002cn,你说的太笼统了,Tbitmap是个复合对象,属性里还有其他对象,不能简单的用指针传递吧,你试试看就知道了,肯定会出现地址错误。

to szf: 你说的方法貌似可行,我试试看先。我用笨办法也可以实现,就是分别传递BITMAP、BRUSH、PEN的Handle,还没试过调色板。用Tmemorystream传递也许是个不错的办法,缺点同样是大位图的时候太吃内存了。
不知道还有没有效率更高一些的办法?
 
不太明白为什么要处理的这么复杂,做一个临时文件,主程序也用,动态库也用不就行了。共享的不过是一个字符串(文件名),最多再加一个正在修改的标识就行了。不用这么复杂吧?
 
你要在大位图吃不吃内存,那只有通过文件交换了。
 
TO娃娃:用文件映射的话处理起来比较慢啊,效率太低。我通常处理的位图都在5000×5000以上的。
 
其实通过文件交换是很好,这样TBitmap就不会吃大内存了。
思路是:在DLL里将TBitmap保存一个文件,将这个文件名在DLL返回给主程序,
然后主程序根据DLL里返回的文件名,再重新OPEN就不会吃大内存了。
 
再次说明一下,不用内存映射和文件交换是因为那样做处理大位图的时候效率太低,呵呵。
TO szf: 你说的传递BITMAP 的 Handle 的办法可否给一个简单的例子【如在位图上用红色画一个矩形】?
我用你的办法在DLL中创建一个BITMAP实例【假定BMP】,赋上了主程序传递过来的BITMAP.HANDLE, 然后进行绘图操作。但是仍然不能使用 BMP.CANVAS.BRUSH.COLOR := clRed 。这样做的后果在释放DLL时会导致地址错误。
 
在DLL中将BitMap对象通过Base64编码进行转换,得到字符串,主程序中调DLL得到字符串,再在主程序中使用Base64将字符串反转为BitMap对象,不过这样效率可能会比较低的,我没有测试过
 
delphi可以通过dll,传送对象的,例如窗体什么的
 
to feizaihz: 这样做效率肯定会更低,呵呵。不过还是感谢帮顶[:)]
 
采用Runtime Package

Project -> Option -> Packages 选中 Build with runtime packages
下面输入框里保留RTL,VCL编译就可以了。

发布时别忘了把RTL100.BPL和VCL100.BPL带着(在System32中,我的是Delphi2006,
别的版本数字不同)。

DLL文件

library Project33;

uses
SysUtils,
Classes,
Graphics;

{$R *.res}

procedure DrawIt(const bmp: TBitmap);
begin
bmp.Canvas.Pen.Color := clRed;
bmp.Canvas.Ellipse(10, 10, bmp.Width-10, bmp.Height-10);
end;

exports DrawIt;

begin
end.

主程序:

program Project32;

uses
Forms,
Unit31 in 'Unit31.pas' {Form31};

{$R *.res}

begin
Application.Initialize;
Application.CreateForm(TForm31, Form31);
Application.Run;
end.

unit Unit31;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls;

type
TForm31 = class(TForm)
Button1: TButton;
Image1: TImage;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form31: TForm31;

implementation

{$R *.dfm}

procedure DrawIt(bmp: TBitmap); external 'project33.dll';

procedure TForm31.Button1Click(Sender: TObject);
begin
DrawIt(Image1.Picture.Bitmap);
end;

end.
 
TO tseug: 你一定没有测试过吧,关闭程序的时候没有地址错误?
另外,你给的例子貌似和 runtime package 没有关系嘛,还是用的DLL,没有用 bpl .
不过还是感谢你的回答[:D]
 
如果你觉得我没测试就随便回答尽可不信,我已经说明我是用的D2006。
多说一句,类似的思路我在2000年D6的时候就用在工程上了,不过当时传递的是
保存在DLL中的Form而已。
 
to szf: 你说的用bpl的方法我也试过了,如果不用unloadpackage释放就可以,关闭程序也不会出错,但是一执行unloadpackage,关闭程序的时候就出runtime error.
不知道如果不手动是否会不会造成内存泄漏,我跟踪了一下内存好像是不会的.
 
to tseug: 别误会啊,我只是想确认一下你那边是否已经通过,因为我用D6编译运行了一遍还是地址错啊。也许在D2006上是可以通过的吧[:(]
焦虑中... 大哥,救救我啊。
 
1、Build with runtime packages 选中时编译程序不会把那些BPL(比如上述的RTL、VCL)
中的一些代码编译到最终可执行文件或者动态库中,因此,当程序执行时调用GetClass之
类的函数获取的都是保存在BPL中的,换句话说是共享的地址(当然,你自定义的类不算,
共享他们需要另外的办法)。
2、我提供这个思路在D2006下可以正常运行,虽然D2006在内存管理上与D6、D7相比有改
进,但同样思路D6下用过,没有发现问题。
3、检查一下是不是你的DLL和EXE都选中了Build with runtime packages后编译的。
 
tseug:
你例子中描述的代码,只要在 Unit31 的 TForm31 中多增加一个方法就可以避开内存管理问题的,不需要带包,添加的方法如下:
procedure TForm1.EllipseToBitmap(bmp: TBitmap);
var
B:TBitmap;
begin
B:=TBitmap.Create;
try
B.Assign(bmp);
DrawIt(B);
bmp.Assign(B);
finally
B.Free;
end;
end;

Click 时就调用这个新加的方法:
procedure TForm31.Button1Click(Sender: TObject);
begin
EllipseToBitmap(Image1.Picture.Bitmap);
end;

以下是没有增加方法导致异常时的栈调用,大概可以做注解:
:7c812a5b kernel32.RaiseException + 0x52
:00403e38 NotifyNonDelphiException + $1C
:7c92378b ntdll.RtlConvertUlongToLargeInteger + 0x46
:7c92eafa ntdll.KiUserExceptionDispatcher + 0xe
:0044d38b TScrollingWinControl.CalcAutoRange + $17
:0043a05a TControl.WndProc + $2BE
:0043e014 TWinControl.WndProc + $500
:0044f26c TCustomForm.WndProc + $558
:0043d73b TWinControl.MainWndProc + $2F
:0041b24a StdWndProc + $16
:77d18734 USER32.GetDC + 0x6d
:77d18816 ; C:/WINDOWS/system32/USER32.dll
:77d1d17f ; C:/WINDOWS/system32/USER32.dll
:77d1d598 USER32.DefWindowProcA + 0xaa
:77d1d545 USER32.DefWindowProcA + 0x57
:77d18734 USER32.GetDC + 0x6d
:77d18816 ; C:/WINDOWS/system32/USER32.dll
:77d1c63f USER32.IsWindowUnicode + 0xa1
:77d1e905 USER32.CallWindowProcA + 0x1b
:0043e110 TWinControl.DefaultHandler + $DC
:0043e014 TWinControl.WndProc + $500
:0044f26c TCustomForm.WndProc + $558
:0043d73b TWinControl.MainWndProc + $2F
:0041b24a StdWndProc + $16
:77d18734 USER32.GetDC + 0x6d
:77d18816 ; C:/WINDOWS/system32/USER32.dll
:77d1b4c0 ; C:/WINDOWS/system32/USER32.dll
:77d1d0a5 ; C:/WINDOWS/system32/USER32.dll
:7c92eae3 ntdll.KiUserCallbackDispatcher + 0x13
:0045019b TCustomForm.SetMenu + $197
:00451f00 TCustomForm.WMNCCreate + $20
:0043a05a TControl.WndProc + $2BE
:0043e014 TWinControl.WndProc + $500
:0044f26c TCustomForm.WndProc + $558
:0043d73b TWinControl.MainWndProc + $2F
:0041b24a StdWndProc + $16
:77d18734 USER32.GetDC + 0x6d
:77d18816 ; C:/WINDOWS/system32/USER32.dll
:77d1b4c0 ; C:/WINDOWS/system32/USER32.dll
:77d1fd29 ; C:/WINDOWS/system32/USER32.dll
:7c92eae3 ntdll.KiUserCallbackDispatcher + 0x13
:77d201f7 ; C:/WINDOWS/system32/USER32.dll
:77d20291 USER32.CreateWindowExA + 0x33
:00407108 CreateWindowEx + $44
:0043ce69 TWinControl.CreateWindowHandle + $35
:0043cd8b TWinControl.CreateWnd + $127
:0044d1e6 TScrollingWinControl.CreateWnd + $A
:0041b24a StdWndProc + $16
:77d18734 USER32.GetDC + 0x6d
:77d18816 ; C:/WINDOWS/system32/USER32.dll
:77d189cd ; C:/WINDOWS/system32/USER32.dll
:77d18a10 USER32.DispatchMessageW + 0xf
:77d2e2b9 ; C:/WINDOWS/system32/USER32.dll
:77d261c6 ; C:/WINDOWS/system32/USER32.dll
:77d3a92e ; C:/WINDOWS/system32/USER32.dll
:77d3a294 ; C:/WINDOWS/system32/USER32.dll
:77d65fbb USER32.MessageBoxTimeoutW + 0x7a
:77d66060 USER32.MessageBoxTimeoutA + 0x9c
:77d50577 USER32.MessageBoxExA + 0x1b
:77d5052f USER32.MessageBoxA + 0x45
:0040bd2e ShowException + $B2
:7c92378b ntdll.RtlConvertUlongToLargeInteger + 0x46
:7c92eafa ntdll.KiUserExceptionDispatcher + 0xe
:0041fc58 TPen.Destroy + $18
 
后退
顶部