We have a Delphi 6 dll that has a PChar passed to it. The dll function then does a search and returns a PCHar. This works good using Delphi 6 to call and recieve the dll result. We have upgraded our calling program to Delphi 2010 and most of the time, the value is passed back just fine. However, we do have times when we get an access violation, when calling the dll. The dll does process the request. Below is the code. Has anyone had a similar issue using an older version Delphi Dll with Delphi 2010? I am sure that Unicode has something to do with it, but not sure how to fix this. TIA This is the function in the dll. function ContactSearch(aCriteria: PChar):PChar; stdcall; var lCriteria: String; begin lCriteria := aCriteria; lCriteria := Verify(lCriteria, 'Contact'); Result := PChar(lCriteria); end; Delphi 2010 calling code var lRequest: AnsiString; dllResult: String; dllHandle := LoadLibrary('Search.dll') ; if dllHandle <> 0 then begin @FuncContactSearch := GetProcAddress(dllHandle, 'ContactSearch') ; if Assigned (FuncContactSearch) then begin dllResult := PAnsiChar(FuncContactSearch(PChar(lRequest))); <--- This populates dllResult with the string that is returned. This is also where I get a memory error sometimes and it does not populate dllResult // dllResult := FuncContactySearch(PChar(lRequest)) ; //call the function <-- This call would not populate dllResult wi
![]() |
0 |
![]() |
Jan wrote: > We have a Delphi 6 dll that has a PChar passed to it. PChar in D6 was an alias for PAnsiChar, but is now an alias for PWideChar in D2009+. You need to adjust your D2010 code accordingly to use PAnsiChar directly instead of PChar generically. > function ContactSearch(aCriteria: PChar):PChar; stdcall; > var > lCriteria: String; > begin > lCriteria := aCriteria; > lCriteria := Verify(lCriteria, 'Contact'); > Result := PChar(lCriteria); > end; This kind of code is very dangerous, even in D6. You are returning a PAnsiChar to an AnsiString that gets freed when the function exits, thus returning a pointer to invalid memory. The fact that it works at all simply means that the caller is using the pointer before the Memory Manager has a chance to overwrite the freed data with something else. Don't rely on that behavior. If you need the returned data to persist after the function exits, you must either: 1) save the result of the search in a global variable inside of the DLL so it remains in memory, then you can return a pointer to it: {code:delphi} var // if the function gets called by multiple threads, you will have to declare // this variable as 'threadvar' so each thread gets its own copy of the variable... CriteriaResult: String; function ContactSearch(aCriteria: PChar):PChar; stdcall; var lCriteria: String; begin lCriteria := aCriteria; CriteriaResult := Verify(lCriteria, 'Contact'); Result := PChar(CriteriaResult); end; {code} 2) dynamically allocate a memory block to return, and then require the caller to pass it back to the DLL when finished using it so it can be freed correctly: {code:delphi} function ContactSearch(aCriteria: PChar):PChar; stdcall; var lCriteria: String; begin lCriteria := aCriteria; lCriteria := Verify(lCriteria, 'Contact'); Result := StrNew(PChar(lCriteria)); end; procedure FreeContactSearchResult(aResult: PChar); stdcall; begin StrDispose(aResult); end; {code} {code:delphi} var dllResult: PAnsiChar; dllResult := ContactSearch(...); if dllResult <> nil then begin // use dllResult as needed... FreeContactSearchResult(dllResult); end; {code} 3) have the caller pass in a block of memory for the DLL to fill in, that way the caller decides how to manage the memory: {code} function ContactSearch(aCriteria: PChar; aResultBuffer: PChar; aBufferSize: Cardinal): Cardinal; stdcall; var lCriteria: String; lResultLen: Cardinal begin lCriteria := aCriteria; lCriteria := Verify(lCriteria, 'Contact'); lResultLen := Min(aBufferSize, Length(lCriteria)); StrPLCopy(aResultBuffer, lCriteria, aBufferSize); Result := lResultLen; end; {code} {code:delphi} var dllResult: array[0..255] of AnsiChar; dllResultLen: Cardinal; begin dllResultLen := ContactSearch(..., dllResult, 256); // use dllResult up to dllResultLen AnsiChars as needed... {code} {code:delphi} var dllResult: AnsiString; dllResultLen: Cardinal; begin SetLength(dllResult, 256); SetLength(dllResult, ContactSearch(..., PAnsiChar(dllResult), 256)); // use dllResult as needed... {code} > I am sure that Unicode has something to do with it, but not sure how to fix this. It is OK to assign a PAnsiChar to a UnicodeString. The RTL will perform an Ansi->Unicode conversion for you. However, you are casting an AnsiString to a PWideChar, which should not even compile let alone work at runtime. Not taking the memory management issue into account, your D2010 code would need to look more like this: {code:delphi} var lRequest: AnsiString; dllResult: PAnsiChar; FuncContactSearch: function(aCriteria: PAnsiChar): PAnsiChar; stdcall; begin dllHandle := LoadLibrary('Search.dll') ; if dllHandle <> 0 then begin @FuncContactSearch := GetProcAddress(dllHandle, 'ContactSearch') ; if Assigned (FuncContactSearch) then begin dllResult := FuncContactSearch(PAnsiChar(lRequest)); // use dllResult as needed... end; end; end; {code} But you do need to take the memory management issue into account, so you have to fix your DLL first and then adjust the D2010 code if needed. -- Remy Lebeau (TeamB)
![]() |
0 |
![]() |
Remy: Would your suggestion require the use of the Borland Memory Manager. That is one thing the client wants to stay away from? The Delphi exe is on a network server and we were going to put the dll in the same folder, with the exe. When multiple clients launch the exe, do we have to use threads, to make sure the client that called the dll frees their copy? Since I posted this we did change the PChars to PAnsiChar, in the calling program and dll, it did work better. We still have a few searches that give memory errors. I will give the code below a try and thank you for the samples and advice. > {quote:title=Remy Lebeau (TeamB) wrote:}{quote} > Jan wrote: > > > We have a Delphi 6 dll that has a PChar passed to it. > > PChar in D6 was an alias for PAnsiChar, but is now an alias for PWideChar > in D2009+. You need to adjust your D2010 code accordingly to use PAnsiChar > directly instead of PChar generically. > > > function ContactSearch(aCriteria: PChar):PChar; stdcall; > > var > > lCriteria: String; > > begin > > lCriteria := aCriteria; > > lCriteria := Verify(lCriteria, 'Contact'); > > Result := PChar(lCriteria); > > end; > > This kind of code is very dangerous, even in D6. You are returning a PAnsiChar > to an AnsiString that gets freed when the function exits, thus returning > a pointer to invalid memory. The fact that it works at all simply means > that the caller is using the pointer before the Memory Manager has a chance > to overwrite the freed data with something else. Don't rely on that behavior. > > If you need the returned data to persist after the function exits, you must > either: > > 1) save the result of the search in a global variable inside of the DLL so > it remains in memory, then you can return a pointer to it: > > {code:delphi} > var > // if the function gets called by multiple threads, you will have to declare > // this variable as 'threadvar' so each thread gets its own copy of the > variable... > CriteriaResult: String; > > function ContactSearch(aCriteria: PChar):PChar; stdcall; > var > lCriteria: String; > begin > lCriteria := aCriteria; > CriteriaResult := Verify(lCriteria, 'Contact'); > Result := PChar(CriteriaResult); > end; > {code} > > 2) dynamically allocate a memory block to return, and then require the caller > to pass it back to the DLL when finished using it so it can be freed correctly: > > {code:delphi} > function ContactSearch(aCriteria: PChar):PChar; stdcall; > var > lCriteria: String; > begin > lCriteria := aCriteria; > lCriteria := Verify(lCriteria, 'Contact'); > Result := StrNew(PChar(lCriteria)); > end; > > procedure FreeContactSearchResult(aResult: PChar); stdcall; > begin > StrDispose(aResult); > end; > {code} > > {code:delphi} > var > dllResult: PAnsiChar; > > dllResult := ContactSearch(...); > if dllResult <> nil then > begin > // use dllResult as needed... > FreeContactSearchResult(dllResult); > end; > {code} > > 3) have the caller pass in a block of memory for the DLL to fill in, that > way the caller decides how to manage the memory: > > {code} > function ContactSearch(aCriteria: PChar; aResultBuffer: PChar; aBufferSize: > Cardinal): Cardinal; stdcall; > var > lCriteria: String; > lResultLen: Cardinal > begin > lCriteria := aCriteria; > lCriteria := Verify(lCriteria, 'Contact'); > lResultLen := Min(aBufferSize, Length(lCriteria)); > StrPLCopy(aResultBuffer, lCriteria, aBufferSize); > Result := lResultLen; > end; > {code} > > {code:delphi} > var > dllResult: array[0..255] of AnsiChar; > dllResultLen: Cardinal; > begin > dllResultLen := ContactSearch(..., dllResult, 256); > // use dllResult up to dllResultLen AnsiChars as needed... > {code} > > {code:delphi} > var > dllResult: AnsiString; > dllResultLen: Cardinal; > begin > SetLength(dllResult, 256); > SetLength(dllResult, ContactSearch(..., PAnsiChar(dllResult), 256)); > // use dllResult as needed... > {code} > > > I am sure that Unicode has something to do with it, but not sure how to > fix this. > > It is OK to assign a PAnsiChar to a UnicodeString. The RTL will perform an > Ansi->Unicode conversion for you. However, you are casting an AnsiString > to a PWideChar, which should not even compile let alone work at runtime. > Not taking the memory management issue into account, your D2010 code would > need to look more like this: > > {code:delphi} > var > lRequest: AnsiString; > dllResult: PAnsiChar; > FuncContactSearch: function(aCriteria: PAnsiChar): PAnsiChar; stdcall; > begin > dllHandle := LoadLibrary('Search.dll') ; > if dllHandle <> 0 then > begin > @FuncContactSearch := GetProcAddress(dllHandle, 'ContactSearch') ; > if Assigned (FuncContactSearch) then > begin > dllResult := FuncContactSearch(PAnsiChar(lRequest)); > // use dllResult as needed... > end; > end; > end; > {code} > > But you do need to take the memory management issue into account, so you > have to fix your DLL first and then adjust the D2010 code if needed. > > -- > Remy Lebeau (TeamB)
![]() |
0 |
![]() |
Hello Jan, > Remy: > > Would your suggestion require the use of the Borland Memory Manager. > That is one thing the client wants to stay away from? D6 uses the Borland MM by default. D2010 uses FastMM instead. You can download the full version of FastMM from http://fastmm.sourceforge.net if you want to use it in your D6 code. In any case, any memory allocated by the DLL has to be freed by the DLL, and any memory allocated by the app has to be freed by the app. This ensures the same memory manager, whatever it happens to be, is used to allocate a free a given block of memory. > The Delphi exe is on a network server and we were going to put the dll in the same > folder, with the exe. Do you have to use the DLL version of the Memory Manager at all? In most situations, it is desirable to static-link the MM directly into apps/DLLs instead. The only time where you have to use a external MM is if you are using Runtime Packages, so they all share the same MM. -- Remy Lebeau (TeamB)
![]() |
0 |
![]() |
On Wed, 27 Feb 2013 17:34:58 -0800, Jan B <> wrote: >Remy: > >Would your suggestion require the use of the Borland Memory Manager. That is one thing the client wants to stay away from? The Delphi exe is on a network server and we were going to put the dll in the same folder, with the exe. When multiple clients launch the exe, do we have to use threads, to make sure the client that called the dll frees their copy? Since I posted this we did change the PChars to PAnsiChar, in the calling program and dll, it did work better. We still have a few searches that give >memory errors. I will give the code below a try and thank you for the samples and advice. What you are doing is certain to on occasion fail. The only question is the frequency of such failures. Furthermore, bugs like this are extremely hard to find while debugging.
![]() |
0 |
![]() |