I want to create a DLL plugins to use with Delphi and other languages (mostly C++).
How can I pass bitmaps in a C++ and Delphi-friendly way? Can it just be a handle to the Delphi TBitmap? C++ program should be able to decode it using WinApi, right?
You cannot pass a Delphi TBitmap object since that is only meaningful to Delphi code. What you need to pass is an HBITMAP, a handle to a Windows bitmap.
The Delphi TBitmap class is just a wrapper around the Windows bitmap and can provide HBITMAP handles. The thing you need to watch out for is the ownership of those handles.
If you have a Delphi TBitmap you can get an HBITMAP by calling the ReleaseHandle method of a TBitmap. The handle returned by ReleaseHandle is no longer owned and managed by the TBitmap object which is exactly what you want. You pass that handle to the C++ code and let it become the owner. It is responsible for disposing of that handle.
The documentation for ReleaseHandle says:
Returns the handle to the bitmap so that the TBitmap object no longer
knows about the handle.
Use ReleaseHandle to disassociate the bitmap from the bitmap handle.
Use it when you need to give a bitmap handle to a routine or object
that will assume ownership (or destroy) the bitmap handle.
In the other direction your Delphi code would receive an HBITMAP from the C++ code and take on ownership. Do that by assigning to the Handle property of a TBitmap instance.
The details will vary from language to language, but no matter what, all will be able to deal with an HBITMAP.
Related
I want to create a general purpose function that loads 'any' type of image (gif, jpg, bmp, png, etc) from disk and returns a bitmap.
function LoadGraph(FileName: string): TBitmap; {pseudocode}
begin
if FileIsJpeg then
jpeg.LoadFromFile;
Bitmap.Exif.Assign(Jpeg.Exif);
end;
The thing is that I need to have access to the Exif data when the input type is Jpeg.
So, I wanted to create a class helper like this:
TYPE
TBitmapHelper = class helper for TBitmap
public
FExifData: TExif;
end;
However, it seems that the Delphi compiler does not have this capability (yet?) as I get this compile error:
E2599 Field definition not allowed in helper type
How to achieve this?
There is quite complex hierarchy of graphic objects in Delphi intended to work with various image formats: TGraphic is abstract class for some image you can load from file, save to file and draw on canvas. TPicture is container for TGraphic which allows you to write just one line of code:
Picture.LoadFromFile(Filename);
and it will look up correct graphic class, create it and load your image.
So one of solutions for you would be to use TPicture instead of TBitmap. For TBitmap and its descendants TPicture will contain Bitmap property, while for the others you can draw on Canvas or assign Picture.Graphic to your TBitmap (works for TJPEGImage, TPNGImage, but still fails with TIcon).
If it's too bulky, for example, you need not just show image on screen but modify it in some way and not think each time how it's actually represented, I'd recommend to create descendant from TBitmap, not just helper, it's quite possible, you can pass such an object everywhere in program where TBitmap is expected, but now you can extend it with new fields. I'm going to do similar thing to add ICC profiles capability (supported in BitmapHeaderV5).
How can I call from delphi a DWScript procedure which takes a class as param?
ex:
DWScript side:
procedure Proc1(AParam: TObject);
begin
// do something useful
end;
Delphi side:
var
Obj: TObject;
Exec.Invoke('Proc1', [obj]);
You need to wrap your Delphi-side object into a Script-side object, and create one if needs be.
For exposure, you can use TdwsUnit and manually expose (which can allow to safeguard the Delphi-side class from script mis-manipulations), or use RTTI exposure (but in that case, bugs on the script side will be able to directly affect the Delphi, and thus could crash the host, so only use RTTI if you know your classes are already safe, or if you don't care about sand-boxing).
The simpler approach if all you need is a simple exposure is to use the RTTI Environment (cf. TRTTIExposeTests.EnvironmentTest in URTTIExposeTests), but keep in mind the above notes about RTTI and safety/sandboxing.
The more complex approach is to create a script-side object with code like
scriptObj := Info.Vars['TScriptSideClassName'].Method['Create'].Call([param1, param2]);
And then manually adjust its ExternalObject property.
You may also have to worry about creating the script-side object only once (if you want script-side object comparisons to work as usual), as well as cleanup (ie. what happens if the Delphi-side object gets freed while the script is still running, or who should be responsible for freeing the Delphi-side object when the script-side object is freed), as well as other subtlety (which will get simpler when/if Delphi gets some form of automated memory management).
I need to manage OLE drag-drop operations with Delphi. Now I got a problem - I need to put some data from TStream into global TStgMedium (to send it through OLE methods using IDataObject).
I guess there should be some commonly used pattern to do this? Can you provide some sample code?
It's probably easiest to use TStreamAdapter to adapt a TStream instance into an IStream.
The other approach that leaps out is to pass an HGLOBAL to IDataObject.SetData but that would be somewhat inefficient for large streams.
I'm trying to put together some components for using OpenGL in RAD Studio 2009. I want to have multiple Rendering Contexts.
My idea is to have something like a "main OpenGL component", called GLMaster. It's a TFrame descendant so it should provide me with a DC. Also there's a component for a GLMonitor, acting as camera.
I create an OpenGL RC in the overriden GLMaster.Loaded inside an "if not (csDesigning in ComponentState) then"-clause. The DC for this I do not keep, it's stated in the RAD Studio help that you shouldn't:
"TWinControl.Handle
Do not refer to the Handle property during component creation or streaming. The underlying window does not exist until the first time the Handle property is referenced. When this occurs, the HandleNeeded method is called automatically."
I handle this by function pointers in components using GLMaster pointing to GLMaster.GetCurrentDC (returns a HDC).
During destruction GLMonitor wants to clean up some render-to textures and other OpenGL resources. When retrieving the DC for a call to wglMakeActive the function pointer is followed and execution jumps to GLMaster.GetCurrentDC. Pointer(Self) tells me that it's the same GLMaster that we created the "master RC" in during the streaming of the components. The property [GLMaster.]Handle is now invalid! If I however free the GLMonitor in the app form's OnClose (using simply GLMonitor_1.Free;) the Handle inside GLMaster.GetCurrentDC is valid end everything works.
I managed to see that the handle used in GLMaster.Loaded is not the same (!?!?) as after the initialization is done and the app form is showed to the user. When I found this I googled my problem and added an overriden CreateParams to add CS_OWNDC to the GLMaster component. A breakpoint there tells me that it's being executed.
Why does the VCL/Delphi silently provides the newly created components with other Handles, and thereby indirectly other DCs, even if I provide the OWNDC flag? Is there someway to get an "ALL components are now loaded and their properties are read from the .dfm file and"-message so I can run GLMaster.InitEverything inside the component?
Right now I'm thinking the fastest way onward is to put this InitEverything in the main form's OnShow handler. And then a call to GLMatser.CleanAll in the main form's OnClose. But I don't want any code in the form, that's the reason I created the components in the first place!
P.S.
Not that I think it matters for this particular problem but I use OpenGL 3.2 Core Profile.
I'm answering the "Why does the VCL/Delphi silently provides the newly created components with other Handles".
Why an control's Window Handle might change at run-time
Your TFrame (an TWinControl descendant) is placed on an other TWinControl, let's say a TForm. The parent container provides property wrappers around many settings, allowing us to easily make changes; Some of those changes can't be implemented without re-creating the windhow handle. You can get a long list of properties that cause this to happen by doing a search for RecreateWnd in Forms.pas, Controls.pas etc.
Examples of properties that call RecreateWnd to implement a change at run-time:
TScollBox.BorderStyle
TForm.BorderIcons (for MDI childs)
When your TFrame's parent needs to change Window Handle, your TFrame is going to have to change Window Handle as well. Your Delphi Control, no matter the descendense, needs to be handle this, and other controls have it worst: Controls that are implemented as wrappers around Windows controls need to save state and reload state (TEdit, TComboBox, ...)
What I think is going on
While streaming (loading your form) some control does something that needs a Window Handle before loading finishes. It's very likely your own code! After everything finishes loading the Form might need to re-create it's Handle, and this in turn causes your Frame's handle to be changed.
What you might want to override
Given the way the VCL works, you need to be prepared for your Window Handle to change. You should do a search in Controls.pas for the words Handle, HDC. If your control is so intimately linked to it's Window Handle and HDC, it's in your best interest to read up on this stuff.
Take a look at those routintes. Maybe you can sopot a better place to hook:
CreateHandle
CreateWnd
DestroyHandle
DestroyWnd
GetDeviceContext
If the title is the question, then no, Delphi does not ignore CS_OWNDC when creating a frame. Just tested with the below procedure with a frame on a form. When the flag is not specified, the line is drawn on itself again and again, resulting with a line from (0,0) to (10,10). When the DC is owned, the line extends from (0,0) to (50,50), no need to tell but the retrieved DC is same at all times.
procedure TFrame2.WmPaint(var Msg: TWMPaint);
var
i: Integer;
DC: HDC;
Pt: TPoint;
begin
inherited;
for i := 1 to 5 do begin
DC := GetDC(Handle);
GetCurrentPositionEx(DC, #Pt);
LineTo(DC, Pt.X + 10, Pt.Y + 10);
ReleaseDC(Handle, DC);
end;
end;
if your question is this:
ALL components are now loaded and their properties are read from the .dfm file and
The answer is:
Yes, just override the Loaded method of your component. It is called when all the references are valid (after dfm reading is finished).
I have a DLL compiled with D2007 that has functions that return AnsiStrings.
My application is compiled in D2009. When it calls the AnsiString functions, it gets back garbage.
I created a little test app/dll to experiment and discovered that if both app and dll are compiled with the same version of Delphi (either 2007 or 2009), there is no problem. But when one is compiled in 2009 and the other 2007, I get garbage.
I've tried including the latest version of FastMM in both projects, but even then the 2009 app cannot read AnsiStrings from the 2007 dll.
Any ideas of what is going wrong here? Is there a way to work around this?
The internal structure of AnsiStrings changed between Delphi 2007 and Delphi 2009. (Don't get upset; that possibility has been present since day 1.) A Delphi 2009 string maintains a number indicating what code page its data is in.
I recommend you do what every other DLL on Earth does and pass character buffers that the function can fill. The caller should pass a buffer pointer and a number indicating the size of the buffer. (Make sure you're clear about whether you're measuring the size in bytes or characters.) The DLL function fills the buffer, writing no more than the given size, counting the terminating null character.
If the caller doesn't know how many bytes the buffer should be, then you have two options:
Make the DLL behave specially when the input buffer pointer is null. In that case, have it return the required size so that the caller can allocate that much space and call the function a second time.
Have the DLL allocate space for itself, with a predetermined method available for the caller to free the buffer later. The DLL can either export a function for freeing buffers that it has allocated, or you can specify some mutually available API function for the caller to use, such as GlobalFree. Your DLL must use the corresponding allocation API, such as GlobalAlloc. (Don't use Delphi's built-in memory-allocation functions like GetMem or New; there's no guarantee that the caller's memory manager will know how to call Free or Dispose, even if it's written in the same language, even if it's written with the same Delphi version.)
Besides, it's selfish to write a DLL that can only be used by a single language. Write your DLLs in the same style as the Windows API, and you can't go wrong.
OK, so haven't tried it, so a big fat disclaimer slapped on this one.
In the help viewer, look at the topic (Unicode in RAD Stufio) ms-help://embarcadero.rs2009/devcommon/unicodeinide_xml.html
Returning the Delphi 2007 string to Delphi 2009, you should get two problems.
First, the code page mentioned by Rob. You can set this by declaring another AnsiString and calling StringCodePage on the new AnsiString. Then assign that to the old AnsiString by calling SetCodePage. That should work, but if it doesn't there is hope still.
The second problem is the element size which will be something completely mad. It should be 1, so make it 1. The issue here is that there is no SetElementSize function to lean on.
Try this:
var
ElemSizeAddr: PWord; // Need a two-byte type
BrokenAnsiString: AnsiString; // The patient we are trying to cure
...
ElemSizeAddr := Pointer(PAnsiChar(BrokenAnsiString) - 10);
ElemSizeAddr^ := 1; // The size of the element
That should do it!
Now if the StringCodePage/SetCodePage thing didn't work, you can do the same as above, changing the line where we get the address to deduct 12, instead of 10.
It has hack scribbled all over it, that's why I love it.
You are going to need to port those DLLs eventually, but this makes the port more manageable.
One final word - depending on how you return the AnsiString (function result, output parameter, etc) you may need to first assign the string to a different AnsiString variable just to make sure there is no trouble with memory being overwritten.
You'll likely just need to convert the DLL to 2009. According to Embarcadero, the conversion to 2009 is 'easy' and should take you no time at all.
Your DLL should not be returning AnsiString values to begin with. The only way that would work correctly in the first place is if both DLL and EXE were compiled with the ShareMem unit, and even then only if they are compiled with the same Delphi version. D2007's memory manager is not compatible with D2009's memory manager (or any other cross-version use of memory managers), AFAIK.
I agree with Rob and Remy here: common Dlls should return PAnsiChar instead
of AnsiStrings.
If the DLL works OK compiled with D2009, why simply doesn't stop compiling it
with D2007 and start compiling it with D2009 once and for all?
Just as a quick solution here: if your actual data that you pass back from dll in the string does not exceed 255 chars, you can change both the in-dll and interface declerations to use ShortString, which will work regardless of 2007/2009 version. Since you're using AnsiString already on 2007 without a codepage identifier, unicode wont give you any trouble.
if you go this way, all you need to do is change the declarations like:
function MyStringReturningFunction : ShortString ; external 'MyLibrary.dll';
(and in dll: function MyStringReturningFunction : ShortString; respectively)
Same goes for input/output parameters of course:
procedure MyStringTakingAndReturningFunction(s1:ShortString; var s2:ShortString); external 'MyLibrary.dll';
Should be easier than changing a lot of code. But be careful, as I said, your data must not exceed 255 characters since that is the maximum size a ShortString can hold.