Re: [twsocket] DLL implementation advice (or example)...

2011-09-15 Thread Arno Garrels
Adam Burgoyne wrote:
> I've now changed the
> code to this which should solve the problem if I understood correctly:
> 
> function ReadMessage: PAnsiChar; stdcall;
> var
>  Note: AnsiString;
> begin
>  EnterCriticalSection(CritSectn);
> 
>  if (NotesList.Count > 0) then
>  begin
>Note := NotesList.Strings[0];
>NotesList.Delete(0);
>Result := PAnsiChar(Note);
>  end
>  else
>Result := '';
> 
>  LeaveCriticalSection(CritSectn);
> end;


That doesn't work either, the string assigned to the local variable is
destroyed when it goes out of scope, that is when the function is left.
You have to change the function like below and have the caller allocate
and free the memory the string is copied to:

{code untested}
function ReadMessage(pBuf: PAnsiChar; pBufSize: PLongWord): LongWord; stdcall;
begin   
  try
EnterCriticalSection(CritSectn);
try
  if pBufSize = nil then
  begin
Result := 0;
  end
  else if pBuf = nil then
  begin // Return required size / number of AnsiChars
pBufSize^ := Length(NotesList[0]);
Result := 0;
  end
  else if pBufSize^ < Length(NotesList[0]) then
  begin // Return required size / number of AnsiChars
pBufSize^ := Length(NotesList[0]);
Result := 0;   
  end
  else begin // Return number of copied AnsiChars 
Result := Length(NotesList[0]);
Move(Pointer(NotesList[0])^, pBuf^, Result);
NotesList.Delete(0);
  end;
finally
  LeaveCriticalSection(CritSectn);
end;
  except
Result := 0;
  end;   
end;
{code}

But..
If that function is called from different threads you cannot
use it to get the size of a string in a first call with a nil
pBuf since on the second call the string might no longer be
the same. 
   
-- 
Arno Garrels

--
To unsubscribe or change your settings for TWSocket mailing list
please goto http://lists.elists.org/cgi-bin/mailman/listinfo/twsocket
Visit our website at http://www.overbyte.be


Re: [twsocket] DLL implementation advice (or example)...

2011-09-15 Thread Adam Burgoyne
Hi, Wilfried - thanks for the explanation - I've been looking at the code
for hours and just couldn't see the cause.  I've now changed the code to
this which should solve the problem if I understood correctly:

function ReadMessage: PAnsiChar; stdcall;
var
  Note: AnsiString;
begin
  EnterCriticalSection(CritSectn);

  if (NotesList.Count > 0) then
  begin
Note := NotesList.Strings[0];
NotesList.Delete(0);
Result := PAnsiChar(Note);
  end
  else
Result := '';

  LeaveCriticalSection(CritSectn);
end;


Regarding the critical section issue, there are 2 threads involved ... the
main DLL thread which services the calls from the client application and the
worker thread which is running the TWSocket message loop.

As I saw things, there were two areas that could be problematic:

1) the Readmessage function above which is checking the list count and
deleting the string after reading it.
2) the worker thread that would be trying to add strings to the end of a
list which might be 1 string shorter at the end of the process than it was
at the beginning i.e. the string count is 5 as the worker thread prepares to
add NotesList.Strings[5] but before that task has completed, the main thread
has deleted NotesList.Strings[0] so the count is then only 4

Is that a possibility or is the Strings class smart / thread-safe enough to
prevent those situations causing an issue?

Regards, Adam


-Original Message-
From: twsocket-boun...@elists.org [mailto:twsocket-boun...@elists.org] On
Behalf Of Wilfried Mestdagh
Sent: 15 September 2011 22:34
To: 'ICS support mailing'
Subject: Re: [twsocket] DLL implementation advice (or example)...

Hi Adam,

> despite tearing out some hair and losing a

Hopefully you have spare :)

> Result := PAnsiChar(NotesList.Strings[0]);
> NotesList.Delete(0);

The problem is that you give a pointer to the return value of that function
to something that not exists anymore after the functions exit:

> Result := PAnsiChar(NotesList.Strings[0]);

You give a pointer to a string

> NotesList.Delete(0);

And here you destroy the string you point to

> critical section code or not... I thought I might as the worker thread

You only need Critical section if you access same data from out different
thread context. So if this is not the case then you don't need it.

Was this clear to answer your questions?

--
mvg, Wilfried
http://www.mestdagh.biz
http://www.comfortsoftware.be
http://www.expertsoftware.be

--
To unsubscribe or change your settings for TWSocket mailing list please goto
http://lists.elists.org/cgi-bin/mailman/listinfo/twsocket
Visit our website at http://www.overbyte.be

--
To unsubscribe or change your settings for TWSocket mailing list
please goto http://lists.elists.org/cgi-bin/mailman/listinfo/twsocket
Visit our website at http://www.overbyte.be


Re: [twsocket] DLL implementation advice (or example)...

2011-09-15 Thread Wilfried Mestdagh
Hi Adam,

> despite tearing out some hair and losing a

Hopefully you have spare :)

> Result := PAnsiChar(NotesList.Strings[0]);
> NotesList.Delete(0);

The problem is that you give a pointer to the return value of that function
to something that not exists anymore after the functions exit:

> Result := PAnsiChar(NotesList.Strings[0]);

You give a pointer to a string

> NotesList.Delete(0);

And here you destroy the string you point to

> critical section code or not... I thought I might as the worker thread

You only need Critical section if you access same data from out different
thread context. So if this is not the case then you don't need it.

Was this clear to answer your questions?

-- 
mvg, Wilfried
http://www.mestdagh.biz
http://www.comfortsoftware.be
http://www.expertsoftware.be

--
To unsubscribe or change your settings for TWSocket mailing list
please goto http://lists.elists.org/cgi-bin/mailman/listinfo/twsocket
Visit our website at http://www.overbyte.be


Re: [twsocket] DLL implementation advice (or example)...

2011-09-15 Thread Adam Burgoyne
Hi, All

Ok - first the good news... despite tearing out some hair and losing a good
few hours of sleep, my new TWSocket-based DLL with the worker thread is
working brilliantly :-)

Now for the bad news... I am getting some strange and intermittent
"corruption" of function return values.

I'm not sure if this is an issue related specifically to D7 but I've
experienced it calling an exported function within the DLL (resolved by not
exporting it) and now from my calling application (written in C++).

The problem only seems to arise when there is conversion between AnsiString
and PAnsiChar types in an exported function so I'm wondering if anyone else
has seen this behaviour and/or has a solution because I certainly can't see
what's wrong.

Essentially, the DLL has a global TStringList which I'm using as a message
buffer. The worker thread adds new lines to the end of the TStringList as
they are received from the server because the burst rate is far higher than
the calling application can process them. The calling application then
simply requests the 1st message in the list when it's ready for it and that
entry is then deleted from the TStringList so that all incoming messages get
processed in sequence.

The actual code I'm using is here:

function ReadMessage: PAnsiChar; stdcall;
begin
  EnterCriticalSection(CritSectn);

  if (NotesList.Count > 0) then
  begin
Result := PAnsiChar(NotesList.Strings[0]);
NotesList.Delete(0);
  end
  else
Result := '';

  OutputDebugStringA(Result);

  LeaveCriticalSection(CritSectn);
end;


Now, a typical entry added to the list would be something like (without the
quotes) "MESSAGE:Hello World!" or just "MESSAGE:".

The correct strings do seem to be getting added to the list as I'm
monitoring the messages using DebugView but, my calling application is
getting some return values truncated with one or two unprintable characters
after the truncated text. For example, "MESSAGE:" might be received as
"MESSAG<"

Here is a live example taken from the application log - the actual message
logged by the DLL (so should be the content of the TStringList entry) is
"TRADE:" but the calling application received "TRAD"

I've tried playing around with the return type i.e. PChar / PAnsiChar /
PWideChar but it makes no difference

Hopefully someone can provide a little insight as it's driving me crazy.

I'd also be grateful if anyone could confirm whether I actually need the
critical section code or not... I thought I might as the worker thread could
be trying to add a new string as the main thread was deleting string [0] but
I read a note somewhere on a Delphi site that TStringLists were thread-safe
so I'm a bit confused about it.

Regards, Adam


--
To unsubscribe or change your settings for TWSocket mailing list
please goto http://lists.elists.org/cgi-bin/mailman/listinfo/twsocket
Visit our website at http://www.overbyte.be