I'm allocating memory for several PWideChar on my main executable file
var
pwcValor: PWideChar;
begin
pwcValor := AllocMem( sizeof(WideChar) * Succ(Length(pValor)));
StringToWideChar(pValor, pwcValor, Succ(Length(pValor)));
pMetodo(pCodigo, pCodigoParametro, pwcValor);
All of these variables are sent over to an external DLL using late binding. I have some questions about this situation to avoid memory leaks.
Where (on my exe or my dll) should I call the FreeMem on these variables?
Do I need to call FreeMem on these variables?
When can I (or should I) call FreeMem on these variables?
If I call them inside the external DLL (which is also mine), I get Access Violations when I try to Unload from memory the DLL library.
Tks
EDIT
Something I forgot to ask. And the other way around? I have so return parameters from my DLL to the EXE, so the PWideChars are allocated on the DLL. So, I would have to free them on the DLL, right? But I'll probably still be using them on the EXE. Must I pre-allocate on the EXE, send to the DLL the pointer, and have it filled in the DLL in these cases? Or just make a copy on the EXE of the returned parameter, so I can free it safely on the DLL?
Ultimately that depends on the design of the DLLs you use. However, I would say that if not documented otherwise it is safe to free the resources as soon as the DLL function returns. I would even suggest that you should do it. Anyway you must do it to avoid memory leaks.
Regarding the last sentence, a DLL and the invoking EXE eventhough they both be Delphi code, they use different memory managers, so you cannot in a DLL free memory allocated in the EXE.
Regarding to freeing:
There are different possible solutions here:
Your exe can allocate buffer, which then would be filled by dll;
Your dll can export one more function, say FreeString. Exe should call it every time it has finished with the string;
You can use simple WideString type. This type of strings use system memory manager which is the same for exe and dll.
Personally I recommend you the last option.
If you have a Delphi string variable and are using D2009 or later so that PChar maps to PWideChar then you can just call your function with pMetodo(pCodigo, pCodigoParametro, PChar(pValor)). Then inside your DLL, you take a copy of the string by declaring a string variable and simply assigning to the string. For example the DLL code would look like this:
procedure pMetodo(pwcValor: PChar);
var
pValor: string;
begin
pValor := pwcValor;
...
end;
The code as you have it is quite unnecessarily complex. Doing it the way I suggest avoids having to use any explicit memory allocation routines. If you want to write C code then why use Delphi!! ;-)
If you wanted to be more explicit then you could write PWideChar rather than PChar.
Related
I'm working with an old and complex system that shares memory between dozens (sometimes hundreds) of Win32 processes. The code is mostly very old Pascal that has been ported to Delphi a few years ago.
(Almost) all of the code is in a single DLL, which all of the processes load. At the moment, we have forced a fixed loading address of that DLL. Image base is defined and ASLR is disabled in linker settings. Each process checks the DLL loading addresses at startup and the entire system refuses to work if the DLL cannot be loaded at the exact same address in all of the processes. This is of course a problematic solution. Sometimes customers have all sorts of 3rd party gadgets which affect the address space and prevents our product from having the address it wants for the DLL.
The reason for the fixed DLL loading address is below. I'm wondering if there is a way to work around this problem.
I've been trying to introduce object-oriented programming. The problem is, if I instantiate a Delphi class in the shared memory, that instance now seems to be dependent on the DLL loading address. For example, if another process tries to destroy that object, it will crash, unless the two processes happen to have the same DLL address. Delphi runtime seems to save function addresses in the object instance, assuming they will stay fixed for the lifetime of the object.
One possible solution might be to copy the DLL contents to the shared memory, then do some sort of magic trickery on DLL_PROCESS_ATTACH to make the process run that copy of the code instead of the loaded DLL address. The shared memory we have is always mapped at the same addresses. (Yes, this is also a problem sometimes but very rarely since the shared memory can be mapped at high (above 2 GB) addresses which are easily available.)
Or is there a way to tell Delphi compiler "please do not assume that the addresses of the functions related to this class are fixed"? I'm using Delphi 11.1.
I was able to figure out a a solution that seems to work well, so let me answer my own question.
The issue is that in order for dynamic dispatch to work, the object instance must be 'tagged' with type information. In the case of Delphi in Win32, this tag is in the first 32 bits of the object instance, and it is a memory address into the DLL where the the code of the class in question is.
If you shift this address to match the variable (process-specific) address of the DLL, the dynamically dispatched methods work fine. In order to do this, you need to compare this address to the loading address of the DLL (or any other reference address inside the DLL) and save the offset, when creating the object.
Then, before calling the object's methods in another process, "localize" the object by taking the actual address of the DLL, adding the offset, and writing this sum to the first 32 bits of the object.
Now you can use the object in any process, as long as you localize it first:
Obj.Localize;
Obj.Do_Something;
This can be neatly wrapped in a class.
Offset is simply a private 32-bit UInt32.
constructor Global_Object.Create;
begin
Self.Offset := PUInt32(Self)^ - Self.Reference_Address;
end;
procedure Global_Object.Localize;
begin
PUInt32(Self)^ := Self.Reference_Address + Self.Offset;
end;
destructor Global_Object.Destroy;
begin
inherited Destroy;
end;
function Global_Object.Reference_Address
: Cardinal;
begin
// Anything in the DLL can be used as a reference,
// such as the address of this function.
Result := Cardinal(#Global_Object.Reference_Address);
end;
I´m migrating my Delphi 5 source code to Delphi 10 Berlin. I´ve got many DLLs in my project which export functions. These functions are called from other DLLs. There are two DLLs which I can not migrate to Delphi 10 but I still want to use them in my program.
Here an example:
function DoSomething( aList: TStringList ): Boolean; external 'Delphi5.dll';
I want to call "DoSomething" from my Delphi 10 Project. But the problem is, that TStringList in Delphi 5 is not compatible to TStringList in Delphi 10 Berlin (unicode). It would work, when DoSomething would have a parameter like "aString: AnsiString" because AnsiString is compatible to "string" in Delphi 5.
Is there a way to send a List between these two Delphi-Versions? Perhaps a TList or something else? Of course I could send a AnsiString with a separator between the strings to simulate a list, but I want a clean solution, because I´ve got many of these export-functions.
Thanks!
One should NEVER pass an object reference from an EXE to a DLL if it is meant to be used inside the DLL, or vice versa. An object reference can safely be passed to a DLL only if all the DLL does is pass the object back to the EXE (ro vice versa), such as through a callback function.
As you experienced, an object reference is not valid if the EXE and DLL aren't compiled with the same version of Delphi. Even if they are compiled with the same version, I suspect some compiler options could make them incompatible ({$Align} comes to mind, though I have never verified it). And even then, some incompatibilities might still occur (such as "Cannot assign TStringList to TStringList" errors due to RTTI mismatches).
Something that could fix your issue with minimal changes to your code would be to change the declaration of your functions to pass an interface to the DLL, and create a wrapper around TStringList that supports that interface. Said interface would need to support all the functionality you need from TStringList.
function DoSomething( aList: IStringList ): Boolean
Interfaces can be passed between DLL/EXE without most of the problems related to the object reference (as long as they use the exact same interface definition when they are compiled). (Edit: You still need to ensure the data passed to the interface's method are safe to pass to/from a DLL.)
That said, the interface should explicitly use AnsiString use a null-terminated PAnsiChar, or even a WideString (which can safely be sent to/from DLL - Reference).
function DoSomething( aListText: PAnsiChar ): Boolean
function DoSomething( aListText: WideString ): Boolean
Do not use String, which is AnsiString in Delphi 5 but is UnicodeString is Delphi 10. And don't use AnsiString, as it is not compatible between Delphi 5 and Delphi 10 due to internal structure differences.
When I want to export a class within a DLL, is it the right approach to derive it from an interface and return that interface by an exported function?
//exported dll function, which is used in the exe.
function MyClass_Create: IMyClass;
begin
result := TMyClass.Create;
end;
What about the memory management? Can I than pass in/out different interfaces and strings without worries and crashes?
IMyClass = interface
procedure SetString(aMsg: string);
function GetString: string;
procedure SetClass(aClass: ITestClass);
function GetClass: ITestClass;
end;
Interface references are orthogonal to memory management. Usually you export a function that returns interface reference from dll, and don't care about memory management. With reference counting interfaces you can be sure that object instance that implements interface will be freed in dll too.
Strings are different. It does not matter whether you export interface or export flat functions - the same restrictions applies.
BTW your question title is incorrect, there are no 'interface instances' in Delphi.
Using interfaces like this will ensure that the object implementing the interface will be created and freed on the same heap.
However, this will not solve the problem of dynamic string types being allocated and deallocated on different heaps. There are many possible solutions to this, but in my view the best approach is to use WideString across the module boundary.
The WideString type is a wrapper around the COM BSTR and is allocated on the shared COM heap. You only need to use WideString for the interface. The internals of the implementing classes can use native Delphi strings.
Just as strings present problems so do dynamic arrays. Attempting to pas dynamic arrays across module boundaries is not safe. There is no solution analagous to WideString that is as convenient. You can use variant arrays but that's pretty clunky in comparison to WideString.
I have a DLL file from which I need the memory address of a class procedure. I am getting the handle to the DLL file, but when I use GetProcAddress, I can't get the address of the procedure. I have tried the following strings for the process name parameter:
"ProcName"
"ProcClass.ProcName"
"ProcClass::ProcName"
"ProcInterface::ProcName"
"ProcInterface.ProcName"
In none of the cases have I gotten the memory address of the procedure. I am mostly certain that the procedure is public.
What is the string format for doing this? Would it be easier to declare a function pointing to the external procedure and get the address later? Like this:
procedure ProcName(); stdcall; far; external 'Example.DLL';
ProcPointer := #ProcName;
GetProcAddress only gives you the address for exported functions. Your DLL surely doesn't export the methods of a class!
Use an PE explorer to look for the exported names. For example, use the PE explorer available in GExperts. I've got a "PE Information" menu entry under the GExperts menu.
You are into reverse engineering territory here.
I think that if I were you I would just step through in the CPU view of the debugger, following a call to the method of interest, and find the entry point address. I'd subtract it from the base address of the DLL and that would be the offset. Then to calculate the address at runtime you just add the offset it to the base address of the DLL in memory at that time. You can find out the base address with calls to LoadLibrary or GetModuleHandle.
Why hard code the offset? Well, since you can't modify your DLL it doesn't seem to be too limiting. If hard coding the offset is not viable then there are other means of locating entry points, but I must admit I'm not the world's greatest expert on that.
Finally, when you implement the replacement method, you will need to replace it with a global function/procedure with an extra parameter, the first parameter, which takes the place of Self.
I might be reading this wrong. But it seems to me you wrote the DLL.
You should write a function that is NOT a member of any class, and export it from your DLL. Inside that function, call your class method.
If you didn't write the DLL, you still need to find out what functions it exports, and it is very unlikely any of them were class methods, at least not in Pascal.
If someone wrote a dll in C++ and exported its methods, then you would have to investigate C++ name mangling rules.
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.