Delphi's Sharemem - When it is not needed - delphi

I know that when I share strings between a Delphi APP and a Delphi DLL I need to add Sharemem in both app and dll project source as the first unit in uses clause.
But, if the dll exports function that accept only Pchars but inside some of the dll methods I use strings, should I use sharemem as well? Let me shown a sample code:
procedure ShowMyCustomMessage(aMessage : Pchar);
var
vUselessString : string;
begin
vUselessString := aMessage;
ShowMessage(vUselessString);
end;
exports
ShowMyCustomMessage;
In that simple and useless case, the dll is accepting a Pchar but inside the exported method the dll creates a string var. Should I add ShareMem as well?
What about WideString? Does passing WideString parameters require the use of Sharemem?

You need to use Sharemem if and only if memory is allocated in one module (i.e. DLL/EXE) and deallocated in a different module. This commonly happens when you are working passing string between modules.
In the example you give, there is no need to use Sharemem. The memory for the PChar is allocated by the called and is not deallocated by the callee. The string in the callee is allocated and deallocated in the callee.
Here's an example where you would need Sharemem:
function GetString: string;
begin
Result := 'hello';
end;
Here the memory for the string is allocated in the callee but will be deallocated by the caller.
The case of a WideString is quite special. The WideString is a wrapper around the COM BSTR type. It allocates and deallocates using the shared COM allocator. So it does not use the Delphi allocator and you are safe to pass WideString between modules without using Sharemem.

Related

Use ReallocMem in dll for pointer first allocated in host app (Delphi XE)

I have a record which i want to pass to dll, where it can be extended with more fields.
Here is what i have in main app:
type
THostInterface = record
name: string;
x1: single;
end;
PHostInterface = ^THostInterface;
var
pHI: PHostInterface;
begin
GetMem(pHI, SizeOf(THostInterface));
dll_func(pHI);
dll_func2(pHI);
end;
Here is what i want to have in dll: extend given record with some fields, main app doesnt care about its contents, but should be able to pass pointer to new extended record to another dll functions.
type
THostInterfaceNew = record
name: string;
x1: single;
x2,x3: single;
end;
end;
procedure dll_func (var pHI: PHostInterface);
begin
ReallocMem(pHI,sizeOf(THostInterfaceNew)); // Access Violation!
pHI^.x2:=1;
end;
procedure dll_func2 (var pHI: PHostInterfaceNew);
begin
Writeln(pHI^.x2); // here i want to have value "1"
end;
Using ReallocMem in dll leads to Access Violation, could u plz explain why?
The memory manager that allocates memory must be the same memory manager that reallocates/frees it. By default, an app and a DLL would be compiled with separate memory managers. You cannot allocate memory in the app using one memory manager and then reallocate/free it in the DLL using a different memory manager.
To make this work, the app and DLL must share a single memory manager, at least for data that passes over the DLL boundary.
If you compile both app and DLL with runtime packages enabled, they can share a single instance of the RTL, and thus a single instance of Delphi's native memory manager. If runtime packages are not enabled, you can still share a single instance of Delphi's native memory manager by using the ShareMem or SimpleShareMem unit in both projects. See Sharing Memory on Embarcadero's DocWiki.
Note that these approaches only work in Delphi/C++Builder projects, and require the app and DLL to be compiled in the same version of Delphi/C++Builder.
If the app is not written in Delphi/C++Builder, then you will have to resort to using an OS-provided memory management API for any memory that passes over the DLL boundary, such as:
LocalAlloc() / LocalReAlloc() / LocalFree()
CoTaskMemAlloc() / CoTaskMemRealloc() / CoTaskMemFree()
IMalloc interface
etc
Either way, this is just a bad design all around, IMHO. It is safe to pass memory across the DLL boundary for the purpose of reading from it or filling it, but do not allocate the memory in one module and then realloc/free it in another module - Period. Whoever allocates the memory should be the only one to realloc/free it. This gives the allocator the freedom to decide how it wants to (re)allocate it (on the stack? on the heap? in a memory pool?).
If you want the DLL to reallocate the memory, then the DLL should be the one to allocate and free it as well. Otherwise, make the app allocate, reallocate, and free the memory, and the DLL can simply fill the reallocated memory with values as needed.
Now, with that said, even if you did manage to reallocate the memory safely, the code you have shown would still fail to compile, since dll_func2() expects a var reference (why?) to a PHostInterfaceNew but you are passing it a PHostInterface instead. That will cause a "Types of actual and formal var parameters must be identical" error. You cannot mismatch types like that. You would need to remove the var reference and use a type-cast, eg:
procedure dll_func2 (pHI: PHostInterfaceNew);
...
var
pHI: PHostInterface;
begin
...
dll_func2(PHostInterfaceNew(pHI));
end;

Why can Delphi DLLs use WideString without using ShareMem?

David's answer to another question shows a Delphi DLL function returning a WideString. I never thought that was possible without the use of ShareMem.
My test DLL:
function SomeFunction1: Widestring; stdcall;
begin
Result := 'Hello';
end;
function SomeFunction2(var OutVar: Widestring): BOOL; stdcall;
begin
OutVar := 'Hello';
Result := True;
end;
My caller program:
function SomeFunction1: WideString; stdcall; external 'Test.dll';
function SomeFunction2(var OutVar: Widestring): BOOL; stdcall; external 'Test.dll';
procedure TForm1.Button1Click(Sender: TObject);
var
W: WideString;
begin
ShowMessage(SomeFunction1);
SomeFunction2(W);
ShowMessage(W);
end;
It works, and I don't understand how. The convention I know of is the one used by the Windows API, for example Windows GetClassNameW:
function GetClassNameW(hWnd: HWND; lpClassName: PWideChar; nMaxCount: Integer): Integer; stdcall;
Meaning the caller provides the buffer, and the maximum length. The Windows DLL writes to that buffer with the length limitation. The caller is allocates and deallocates the memory.
Another option is that the DLL allocate the memory for example by using LocalAlloc, and the Caller deallocates the memory by calling LocalFree.
How does the memory allocation and deallocation work with my DLL example? Does the "magic" happen because the result is WideString(BSTR)? And why aren't Windows APIs declared with such convenient convention? (Are there any known Win32 APIs that uses such convention?)
EDIT:
I Tested the DLL with C#.
Calling SomeFunction1 causes an AV (Attempted to read or write protected memory).
SomeFunction2 works fine.
[DllImport(#"Test.dll")]
[return: MarshalAs(UnmanagedType.BStr)]
static extern string SomeFunction1();
[DllImport(#"Test.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SomeFunction2([MarshalAs(UnmanagedType.BStr)] out string res);
...
string s;
SomeFunction2(out s);
MessageBox.Show(s); // works ok
MessageBox.Show(SomeFunction1()); // fails with AV!
Here is a followup.
A WideString is the same as a BSTR, it's just the Delphi name for it. The memory allocation is handled by the shared COM allocator, CoTaskMemAlloc. Because all parties use the same allocator you can safely allocate in one module and deallocate in another.
So, the reason you don't need to use Sharemem is that the Delphi heap is not being used. Instead the COM heap is used. And that is shared between all modules in a process.
If you look at the Delphi implementation of WideString you will see calls to the following APIs: SysAllocStringLen, SysFreeString and SysReAllocStringLen. These are the system provided BSTR API functions.
Many of the Windows APIs you refer to pre-date the invention of COM. What's more, there are performance benefits to using a fixed length buffer, allocated by the caller. Namely that it can be allocated on the stack rather than a heap. I also can imagine that the Windows designers don't want to force every process to have to link to OleAut32.dll and pay the price of maintaining the COM heap. Remember that when most of the Windows API was designed, the performance characteristics of the typical hardware was very different from now.
Another possible reason for not using BSTR more widely is that the Windows API is targeted at C. And managing the lifetime of BSTR from C is very much more tricky than from higher level languages like C++, C#, Delphi etc.
There is an extra complication however. The Delphi ABI for WideString return values is not compatible with Microsoft tools. You should not use WideString as a return type, instead return it via an out parameter. For more details see Why can a WideString not be used as a function return value for interop?

Passing parameters from Delphi 5 to Delphi DLL XE

I have a Delphi 5 application in the application code calls a function in the DLL, passing integer and string parameters, this works well when the DLL is called in a static way, when I try to dynamically change does not work.
which is the correct way to pass parameters to function dynamically?
the code is as follows
main application
function Modulo_Pptos_Operacion(No_Orden : Integer; pathBD : string; PathBDConf : String) : Integer ; stdcall;
external 'LIB_Pptos_Oper.dll';
Modulo_Pptos_Operacion(DmDatos.OrdenesNO_Orden.AsInteger,
DmDatos.CiasPATHA.AsString, 'Alguna String');
DLL
Modulo_Pptos_Operacion function (No_Orden: Integer; PathDB: AnsiString; PathDBConfig: AnsiString): Integer; StdCall;
DYNAMIC CRASH
main application
type
TDLLPpto = function(No_Orden : Integer; PathDB : AnsiString; PathDBConfig : AnsiString) : Integer;
var
DLLHandle: THandle;
: TDLLPpto;
PROCEDURE CALL
DLLHandle := LoadLibrary('LIB_Pptos_Oper.dll');
DLLHandle <> 0 then
begin
#DLLPpto := GetProcAddress(DLLHandle, 'Modulo_Pptos_Operacion');
end;
;
which is the right way?
The problem is probably that you are mixing different runtimes and probably different heaps. Delphi strings are not valid interop types because their implementations vary from version to version.
In this case you can simply switch to using null-terminated strings, PAnsiChar.
In the case of dynamically loaded dll you omitted stdcall; calling convention directive in the declaration of TDLLPpto. Still it is advisable to use PAnsiChar type to pass strings across executable boundaries.
The layout of ansistring has changed with Delphi XE: now there is also a codepage field at negative offset and D5 does not have that. EG: strings from D5 and DXE are utterly incompatible. Thus you should use PAnsiChar or PWideChar in your interface, either zero terminated (Delphi strings are always zero terminated) of introduce an extra parameter with the length if the string might contain #$00 bytes.
Also: the different Delphi versions both have different memory managers. If a string is allocated by the main app and freed by the DLL (strings are reference counted) the pointer get's passed to the wrong memory manager which usually results in corrupted memory and thus nasty Access Violations etc.
Another solution is to use WideString; this is both in D5 en DXE equal to a COM BSTR stringtype and managed by the OS and not the Delphi memory manager. They are compatible. The only problem is: they are slow compared to the Delphi strings and are not ref counted.
In all: when using DLL interfaces, try to avoid string, use PAnsiChar or PWideChar, or WideString

How can I pass a Delphi string to a Prism DLL?

We try to pass a string from a native Delphi program to a Delphi Prism DLL.
We have no problem passing integers, but strings are mismatched in the DLL.
We saw Robert Love's code snippet in response to another question, but there is no code for the native Delphi program.
How can we pass strings from Delphi to a Delphi Prism DLL?
The best way would be to use WideString.
For several reasons.
It is Unicode and works before D2009
It's memory is managed in ole32.dll, so no dependency on either Delphi's memory manager or the CLR GC.
You do not have to directly deal with pointers
In Oxygene, you could write it like so:
type
Sample = static class
private
[UnmanagedExport]
method StringTest([MarshalAs(UnmanagedType.BStr)]input : String;
[MarshalAs(UnmanagedType.BStr)]out output : String);
end;
implementation
method Sample.StringTest(input : String; out output : String);
begin
output := input + "ä ~ î 暗";
end;
"MarshalAs" tells the CLR how to marshal strings back and forth. Without it, strings are passed as Ansi (PAnsiChar), which is probably NOT what you would want to do.
This is how to use it from Delphi:
procedure StringTest(const input : WideString; out output : WideString);
stdcall; external 'OxygeneLib';
var
input, output : WideString;
begin
input := 'A b c';
StringTest(input, output);
Writeln(output);
end.
Also, never ever use types, that are not clearly defined, for external interfaces.
You must not use PChar for DLL imports or exports. Because if you do, you will run into exceptions when you compile it with D7 or D2009 (depending on what the original dev system was)
Strings in Delphi Win32 are managed differently from strings in .Net, so you can not pass a .Net string to Delphi Win32 or vice versa.
To exchange strings values you'd better use PChar type which is supported by both compilers. That is the same way you send string values to Windows API functions.
Regards
P.S. I am NOT Robert ;-)

How can I return a PChar from a DLL function to a VB6 application without risking crashes or memory leaks?

I have to create a DLL which is used by a VB6 application. This DLL has to provide several functions, some of them must return strings.
This is the VB6 declaration:
Declare Function MyProc Lib "mylib.dll" (ByVal Param As String) As String
And this the Delphi implementation stub in mylib.dll:
function MyProc(AParam: PChar): PChar; stdcall;
var
ReturnValue: string;
begin
ReturnValue := GetReturnValue(AParam);
Result := ???;
end;
What do I have to return here? Who will free the memory of the returnd PChar string?
EDIT: I'm asking about Delphi 2005 (PChar = PAnsiChar)
You need to craft a BSTR instead. VB6 strings are actually BSTRs. Call SysAllocString() on the Delphi side and return the BSTR to the VB6 side. The VB6 side will have to call SysFreeString() to free the string - it will do it automatically.
If PChar corresponds to an ANSI string (your case) you have to manually convert it to Unicode - use MultiByteToWideChar() for that. See this answer for how to better use SysAllocStringLen() and MultiByteToWideChar() together.
If you don't want to risk crashes or memory leaks, then craft your API using the Windows API as a model. There, the API functions generally don't allocate their own memory. Instead, the caller passes a buffer and tells the API how big the buffer is. The API fills the buffer up to that limit. See the GetWindowText function, for example. Functions don't return pointers, unless they're pointers to things the caller already provided. Instead, the caller provides everything itself, and the function just uses whatever it's given. You almost never see an output buffer parameter that isn't accompanied by another parameter telling the buffer's size.
A further enhancement you can make to that technique is to allow the function to tell the caller how big the buffer needs to be. When the input pointer is a null pointer, then the function can return how many bytes the caller needs to provide. The caller will call the function twice.
You don't need to derive your API from scratch. Use already-working APIs as examples for how to expose your own.
Combining Sharptooth and Lars D's answer; aren't widestrings already allocated via windows and BSTR?
I'm not familiar with Dephi, but here are the two main options when using strings with a non-COM DLL and VB6.
Option 1. Use "ANSI" strings.
'DLL routine expecting to be passed pointers to ANSI strings '
'VB6 will allocate and deallocate the strings '
'Its vital that VB6 allocates sufficient space for the return string '
Declare Sub MyProc Lib "mylib.dll" (ByVal Param As String, _
ByVal OutVal As String)
Function DoMyProc(ByVal Param As String) As String
Dim sResult As String
sResult = Space$(255) ' create 255 bytes of space for the return string '
Call MyProc(Param, sResult)
DoMyProc = sResult
End Function
Option two. Use BSTRs.
'DLL routine expecting to be passed two BSTRs. It will modify the second one. '
'VB6 "owns" both BSTRs and will deallocate them when it has finished with them. '
Declare Sub MyProc(ByVal lpParam As Long, ByVal lpOutVal As Long)
Function DoMyProc(ByVal Param As String) As String
Dim sResult As String
Call MyProc(StrPtr(Param), StrPtr(sResult))
DoMyProc = sResult
End Function
I'd also suggest looking at the Microsoft advice on writing C DLLs to be called from VB. Originally released with VB5 but still relevant to VB6.
Use the Windows API to allocate the memory that the PChar pointer points into. Then, the VB app can deallocate the memory after use, using the Windows API, too.
I would say that whoever allocates the memory must also free it in this case. You will run into problems with other scenarios. So the most safe and clean way would be:
The DLL allocates memory (because it knows how much) and returns the PChar to caller
After the caller is done with it, it calls FreePointer back to the DLL
DLL frees the memory in the FreePointer exported function
The setup would be like this:
unit DLL;
interface
uses
SysUtils;
function Execute(const Params: PChar): PChar; stdcall;
procedure FreePointer(const P: PChar); stdcall;
exports Execute;
exports FreePointer;
implementation
function Execute(const Params: PChar): PChar; stdcall;
var
Size: Cardinal;
begin
Size := Calculate the size;
GetMem(Result, Size);
...do something to fill the buffer
end;
procedure FreePointer(const P: PChar); stdcall;
begin
FreeMem(P);
end;
end.
You cannot return a PChar as a function result, but you can pass an additional PChar parameter and copy the string you want to return to this PChar. Note, that VB must allocate that string to the required size before passing it to the dll. Also in VB that parameter must be declared as byval param as string AND it must be passed with byval:
param = "aaaaaaaaaaaaaaaaaaaa" ' reserve 20 characters
call myproc(byval param)
The additional byval in the call will do the compiler magic of converting a VB string to a PChar and back.
(I hope I remember this is correctly, it has been quite a while since I was forced to use VB.)

Resources