Search code examples
delphimemory-leaksdelphi-xe7pchar

delphi call delphi dll memory leak caused by pchar


So lets take about dll.

If you want to pass a string to a dll call, you must make the procedure input PChar. Else you get data curroption.

so we say that our dll has

  procedure LookPchar(pfff:Pchar);stdCall;External 'OutDll.dll';

which is nice. Now lets look what we declare in the dll dpr:

  procedure LookPchar(pfff:Pchar);
  begin
    with TForm1.Create(nil) do
    try
      show;
      FireDacConnection.ConnectionName := (Copy(pfff,1,100));
    finally
      free;
    end;

  end;
  exports LookPchar;

well, in the Dll we have a Form, with a FireDacConnection in it, but any component or object in it will do the work.

the problem is that this PChar is released twice and cause memory leaks. i can't find a way to pass the PChar without cause memory leaks.

you may use fastmm, i use eurukalog, which writes

|+Leak #2: Type=UnicodeString: Ref count - 1, Content: "\r\n"; Total size=18; Count=1 |

Why is the Unicode String gets Ref count of -1? how to prevent it? how to pass the Unicode string correctly?

What I tried: pass it as const. copy it (as in example and with strpcopy and strcopy) use local variable to hold the copy of PChar.

edit: adding the calling code :

var
  ConnectionName:WideString;
begin
  ConnectionName := 'This Is My String';
  LookPChar(PChar(ConnectionName));
end;

adding leak log dump

|+Leak #2: Type=UnicodeString: Ref count - 1, Content: "\r\n"; Total size=18; Count=1 | |-----------------------------------------------------------------------------------------------------------------------------------------| |00000002|04 |00000000|01D79D9C|outDll.dll|00009D9C|System
| |_NewUnicodeString |23897[6] | |00000002|04 |00000000|008A11BC|myapp.exe |004A11BC|Caller
|TForm2 |Button4Click |66[2] | |00000002|04 |00000000|00641C13|myapp.exe |00241C13|Vcl.Controls |TControl |Click |7348[9] | |00000002|04 |00000000|00646172|myapp.exe |00246172|Vcl.Controls |TWinControl |WndProc |10038[153] | |00000002|04 |00000000|0065B71C|myapp.exe |0025B71C|Vcl.StdCtrls |TButtonControl|WndProc |5163[13] | |00000002|04 |00000000|006462D7|myapp.exe |002462D7|Vcl.Controls | |DoControlMsg |10107[12] | |00000002|04 |00000000|00646172|myapp.exe |00246172|Vcl.Controls |TWinControl |WndProc |10038[153] | |00000002|04 |00000000|0070B240|myapp.exe |0030B240|Vcl.Forms
|TCustomForm |WndProc |4427[206] | |00000002|04 |00000000|006457AC|myapp.exe |002457AC|Vcl.Controls |TWinControl |MainWndProc |9750[3] | |00000002|04 |00000000|004F7614|myapp.exe |000F7614|System.Classes| |StdWndProc
|16600[8] | |00000002|03 |00000000|768162F7|user32.dll
|000162F7|USER32 | | (possible gapfnScSendMessage+815)| | |00000002|03
|00000000|76816D35|user32.dll |00016D35|USER32 |
| (possible GetThreadDesktop+210) | | |00000002|03
|00000000|76816DE8|user32.dll |00016DE8|USER32 |
| (possible GetThreadDesktop+389) | | |00000002|03
|00000000|76816E49|user32.dll |00016E49|USER32 |
| (possible GetThreadDesktop+486) | | |00000002|03
|00000000|77420107|ntdll.dll |00010107|ntdll |
|KiUserCallbackDispatcher | | |00000002|03
|00000000|768196D0|user32.dll |000196D0|USER32 |
|SendMessageW | | |00000002|03
|00000000|71AB459B|comctl32.dll |000A459B|comctl32 |
|LoadIconMetric | | |00000002|03
|00000000|71AB45FE|comctl32.dll |000A45FE|comctl32 |
|LoadIconMetric | | |00000002|03
|00000000|71AB4488|comctl32.dll |000A4488|comctl32 |
|LoadIconMetric | | |00000002|03
|00000000|768162F7|user32.dll |000162F7|USER32 |
| (possible gapfnScSendMessage+815)| | |00000002|03
|00000000|76816D35|user32.dll |00016D35|USER32 |
| (possible GetThreadDesktop+210) | | |00000002|03
|00000000|76820D32|user32.dll |00020D32|USER32 |
| (possible GetClientRect+192) | | |00000002|03
|00000000|76820D56|user32.dll |00020D56|USER32 |
|CallWindowProcW | | |00000002|04
|00000000|00646282|myapp.exe |00246282|Vcl.Controls |TWinControl
|DefaultHandler |10079[30] | |00000002|04
|00000000|00646172|myapp.exe |00246172|Vcl.Controls |TWinControl
|WndProc |10038[153] | |00000002|04
|00000000|0065B71C|myapp.exe |0025B71C|Vcl.StdCtrls |TButtonControl|WndProc |5163[13] | |00000002|04 |00000000|004F7614|myapp.exe |000F7614|System.Classes| |StdWndProc
|16600[8] | |00000002|03 |00000000|768162F7|user32.dll
|000162F7|USER32 | | (possible gapfnScSendMessage+815)| | |00000002|03
|00000000|76816D35|user32.dll |00016D35|USER32 |
| (possible GetThreadDesktop+210) | | |00000002|03
|00000000|768177CE|user32.dll |000177CE|USER32 |
| (possible CharPrevW+314) | | |00000002|03
|00000000|76817893|user32.dll |00017893|USER32 |

|DispatchMessageW | |

sorry its unclear, i do not know how to keep tabs in stackoverflow editor.


Solution

  • Copy(pfff,1,100) is rather odd. You can use pfff directly and have the compiler automatically convert from pointer to null terminated character array to string.

    FireDacConnection.ConnectionName := pfff;
    

    It would surely make sense to do that before calling Show. It certainly seems pretty weird that you show a form modeless, then set the connection name, and then free the form. Indeed, even showing a form in a DLL looks odd.

    That said, this isn't the cause of your problem. The only explanation for a leak in you code is a calling convention mismatch, or an error at the call site. Passing a PChar, and taking a copy, as you do, won't leak.

    The calling convention in the implementation appears to be register. The declaration in your DLL should be:

    procedure LookPchar(pfff:Pchar); stdcall;
    

    Or did you not show the stdcall in the DLL code?

    You might have made a mistake at the call site. Perhaps the leak is there. We cannot see that code.

    Looking at your various edits, FastMM is reporting a leak that is not produced by any of the code in the question. You will need to isolate the issue before you can solve it. That's your next step.

    Using PChar is fine for input. In the other direction, from callee to caller, there are many options, but you have not asked about that here. And there are many many questions on that topic.