Delphi datasnap callback - BroadCast question - delphi

I'm again in a situation where I've spend an obscene amount of time on trying to customize datasnap callback samples to my needs.
I'm old school OOP programmer and have several very large Object hierakies in my "toolbox" PODO style :-) .. and having this great datasnap feature, I want to utilize the forces of the callback.
BUT - when I implement it ... it simply fails ... (FASTMM4 reports mem leaks).
Try and create a simple VCL datasnap server - TCP.
And add a button and this source ...
procedure TForm1.Button1Click(Sender: TObject);
var
// AObject : TObject;
aJSONVal : TJSONValue;
begin
// AObject := TObject.Create;
// ServerContainer1.DSServer1.BroadcastObject('SomeChannel','SomeCallbackID', AObject);
// AObject.Free;
aJSONVal := TJSONObject.Create;
ServerContainer1.DSServer1.BroadcastMessage('SomeChannel','SomeCallbackID',aJSONVal);
// aJSONVal.Free; // Mat pointed out that this is done by the broadcast.
end;
It will work - as long as you keep using TJSONValue ...
But try and switch the commented code - and you will see what I mean.
I could of course change all my existing code to JSON ... but that is simply not acceptable.
Does anyone have any idea on how to use the BroadcastOBJECT or NotifyOBJECT ?
Regards
Bjarne

The object which you give to a Notify or Broadcast call is then owned by that call. Therefore do not call "AObject.Free;" or "aJSONVal.Free;". Doing so will often result in an Access Violation or other memory management related problems.
Note also that Broadcasted messages get put in a queue on the server and are later sent, in a different thread. Meaning, when your call to Broadcast returns, it hasn't actually sent the message to all the clients yet.
I hope that helps,
Mat

Possible answer: Your question was vague but based on what you've said, I'd start here:
Delphi XE help: (ms-help://embarcadero.rs_xe/vcl/DSServer.TDSServer.BroadcastObject.html): function BroadcastObject(const ChannelName: String; const CallbackId: String; const Msg: TObject): boolean; overload;
The second overload sends an object to all client callbacks with a given registered callback identifier. For this purpose, an additional CallbackId parameter is required in the call."
You are using the second overload which takes 3 params - are your callback identifiers set up properly?

Related

Is TIdHTTP threadsafe?

I'm using TIdHTTP in my class to handle web APIs (TWebAPI). Since it might happen that this class is used in a TIdHTTPServer.OnCommandGet event handler, I need to make sure that TWebAPIis thread safe.
Do I need to wrap a PUT/GET/POST inside a TMultiReadExclusiveWriteSynchronizer?
EDIT: Code-Sample
TWebAPI
TWebAPI=class(TObject)
FSocket:TidHTTP;
end;
procedure TWebAPI.Send;
Var
Response:TSTringStream;
begin
FSocket.Get(fURL,Response);
end;
Main Program
TMain=class(TForm)
Server:TidHTTPServer;
WebAPI:TWebAPI;
end;
procedure TMain.ServerCommandGet(....)
begin
WebAPI.Send;
end;
So my WebAPI would be used in different threads on each command the server gets. SHould I use the CriticalSection in TMain, or implement it in TWebAPI like this?
TWebAPI=class(TObject)
FSocket:TidHTTP;
FLock:TCriticalSection;
end;
procedure TWebAPI.Send;
Var
Response:TSTringStream;
begin
FLock.Aquire;
try
FSocket.Get(fURL,Response);
finally
FLock.Release;
end;
end;
A single TIdHTTP could be protected by a TCriticalSection or TMonitor. There is no R/W involved, so don't use TMultiReadExclusiveWriteSynchronizer - just a mutex/lock. But if you use a single TIdHTTP you will need a mutex/lock, so all HTTP calls will be serialized, which may be not a good idea on a multi-threaded server.
I would maintain one connection per thread, or write a connection pool. Perhaps just a given TIdHTTP each time may not be too bad. At least it will be safe and there will be room for improvement later on. Reopening a TCP/HTTP connection is quick in practice.

Cant seem to recive a reply vir com port

Im trying to send a command to a dev. board and then receive a reply
E.G
(me) set attrib=yes
(Dev. Board) O.K
or
(Dev. Board) E.R.R
But it doesn't bounce back anything ... not an O.K or an E.R.R
while the board is booting echo is on .. so if I send the commands while the board is booting it
it will bounce back an 'set attrib=yes' and once booted an 'E.R.R' because you cant send commands while booting.
my best guess is that it isn't reading the reply in time or trying to read it too soon.
procedure TReaderProgrammer.Button2Click(Sender: TObject);
Var
Buffer: AnsiString;
RxData: string;
Count : integer;
begin
if ComPort1.Connected then
begin
ComPort1.WriteStr(edtSendText.Text+#13);
comport1.ReadStr(RxData,Count);
Buffer := Buffer + Rxdata;
memoRxData.Text := memoRxData.Text+Buffer+#13+#10;
end;
end;
Here are several open questions in the air, so I have to make some assumptions that might be wrong, but let's see.
I don't know what comm port library you are using, so I'm assuming it is the CPort library from SourceForge. I have never used it myself, but I have read that it is made Unicode aware, such that you can call the write methods with a unicodestring parameter which will be converted by the library to ansistring before sending. Similarily when receiving ansistring from the outer world, the library will convert to unicodestring for the Read methods.
Due to the asynchronous nature of serial communication, it is important to understand that when you send something using the write method, the method returns immediately while the library and OS spit out the characters one at at time at a pace defined by the baud rate. As a result your first code never received anything, because you were already attempting to read from the comm port before the external device even received the first character.
It is good to see that you have now taken the first step to success by implementing an event handler for (presumably library event) OnRxChar.
The OnRxChar probably fires for each character (or couple of characters). You need to have a buffer that is persistent between these events. A local var (as you have it now and which is allocated on the stack) in the event handler is not persistent, it is lost every time the event handler exits.
You should declare the Buffer variable as a field of TReaderProgrammer. I don't know why you defined the buffer to be AnsiString, but I suggest you try with string (ref discussion above regarding Unicode awareness).
type
TReaderProgrammer = class
..
RxBuffer: string;
..
end;
The buffer needs to be cleared when you send a new command to the external device in order for it to be ready to receive new data as a response to your command.
EDIT: Alternatively you can clear the RxBuffer immediately when you have received and processed a full response.
The TReaderProgrammer.ComPort1RxChar should look like this sofar:
procedure TReaderProgrammer.ComPort1RxChar(Sender: TObject; Count: Integer);
var
RxData : string;
begin
(Sender as TComPort).ReadStr(RxData,Count);
RxBuffer := RxBuffer + Rxdata;
...
end;
The rest of the event handler is, I guess, probably just to see progress of reception, so nothing more about that.

Notifying about events from dll to main app

I am developing application which intend to be cross platform. I used to use Windows Messages but now I am dropping it out. I replaced messages with callbacks but regardless I can use different technologies I am not aware of different possibilites when not using windows messages.
Well I have main exe aplication and some dll plugins. I have some objects and threads in dll and I would like to notify main application about some changes that DLL made to data structure.
As I said I am currently working with some callbacks. To provide compatibility with different languages (C++, VB, C#) I have non-object type of callback. I am not sure if other languages supports callback of object.
So my questions are:
What are the alternatives (cross-platform) to windows messages? Can callbacks replace messages?
Do other languages support callback of object?
I guess other languages have different technologies as alternative to messages?
You can certainly use callback functions instead of messages. You can't use callback methods because only Delphi and C++ Builder understand how to invoke Delphi method pointers. However, you can use callback objects with any language that supports COM. Here's an example for a plug-in to notify the application that the data structure has changed:
Define an interface.
type
IDataStructureChanged = interface
['{GUID}']
procedure Call; stdcall;
end;
You could add some parameters to the method so the plug-in can tell how the data structure changed, or pass some value indicating which plug-in is making the notification.
Implement it in the application.
type
TDataStructureChangedListener = class(TInterfacedObject, IDataStructureChanged)
private
FForm: TForm;
procedure Call; stdcall;
public
constructor Create(Form: TForm);
end;
When you instantiate that class, you can pass it a reference to your program's main form, or whatever other information your program will need to be able to take action when a plug-in eventually calls the Call method. Implement Call to make your application do whatever it needs to do when a data structure changes.
Pass a reference to each of the plug-ins when you initialize them.
ChangeListener := TDataStructureChangedListener.Create(Self);
for i := 0 to Pred(PlugIns.Count) do
PlugIns[i].Init(ChangeListener);
The plug-in should store a reference to the listener object, and when the data structure changes, it can call the Call method to notify your application.
What I've described here is what's generally known as an event sink. You can have more than one in your program. If there are multiple events to handle, you could have a separate interface for each kind of event, or you could group them all into a single interface and have a different method for each event. You could have a different sink object for each plug-in, or you could give each plug-in a reference to the same sink object, and then pass a plug-in-ID parameter.
I would definately use callbacks. The main app could give a callback function to the DLL to call when needed, and then the callback function itself can send window messages to the app if it needs to.
I agree with Remy, (!). A straightforward callback allows the handler to implement any kind of further communication it chooses - it might post a message, it may push a parameter onto a queue, whatever it wants. If you want to be cross-platform, you are going to have to resort to passing in, and out, simple types. It's usual to pass in a 'user context' pointer when callbacks are set up. The callback passes this pointer into the handler. This allows callers to pass in a context object as a pointer/int and to recover it in the handler, (by casting the pointer/int back to an object). The handler can then call methods on the context, no matter whether it's Delphi, C++ etc.
So my questions are:
What are the alternatives (cross-platform) to windows messages? Can callbacks replace messages?
Yes you can replace messages with callbacks.
Do other languages support callback of object?
You shouldn't use object methods as callbacks. Common practice in portable code is use of handles (notify calling convention):
DLL source:
type
THandle = LongWord;
{$IF SizeOf(THandle) < SizeOf(Pointer))}
{$MESSAGE Error 'Invallid handle type'}
{$ENDIF}
TCallback = procedure(const aHandle: THandle); cdecl;
var
gCallback: record
Routine: TCallback;
Obj: TObject;
Info: string
end;
function Object2Handle(const aObj: TObject): THandle;
begin
Result:= THandle(Pointer(aObj))
end;
function Handle2Object(const aHandle: THandle; out aObj: TObject): Boolean;
begin
if gCallback.Obj <> nil then
if aHandle = Object2Handle(gCallback.Obj) then
begin
aObj:= gCallback.Obj;
Result:= true;
Exit // WARRNING: program flow disorder
end;
aObj:= nil;
Result:= false
end;
procedure DoCallback();
begin
if Assigned(gCallback.Routine) then
gCallback.Routine(Object2Handle(gCallback.Obj))
end;
procedure SetupCallback(const aCallback: TCallback); cdecl;
begin
gCallback.Routine:= aCallback;
end;
procedure DoSomething(const aHandle: THandle; out aInfo: string); cdecl;
var
O: TObject;
begin
if Handle2Object(aHandle, O) then
aInfo:= Format('%s class object %s', [O.ClassName(), gCallback.Info])
end;
procedure Test();
begin
gCallback.Obj:= TStream.Create();
try
gCallback.Info:= 'created';
DoCallback();
finally
FreeAndNil(gCallback.Obj)
end;
gCallback.Obj:= TMemoryStream.Create();
try
gCallback.Info:= 'will be freed';
DoCallback();
finally
FreeAndNil(gCallback.Obj)
end
end;
exports
SetupCallback,
DoSomething,
Test;
Executable source:
procedure Cb(const aHandle: THandle); cdecl;
const
STUPID: THandle = 1;
EQUALLY_STUPID = $DEAD;
var
S: string;
begin
DoSomething(STUPID, S);
DoSomething(aHandle, S);
DoSomething(EQUALLY_STUPID, S)
end;
begin
SetupCallback(#Cb);
Test()
end.
Edited: You can't shoot yourself in you leg now.
I guess other languages have different technologies as alternative to messages?
OS have a few message alternatives. However not many truly portable.
You can also use:
sockets,
(IMO too big in this case?) ready messaging system (my favorite 0MQ)

How to wait for COM port receive event before sending more data in a loop

I'm working on a small component for writing and reading AT Commands using an old Sony Ericsson phone.
Sending and writing to/from the phone is no problem at all, however I would like to be able to pause my SendATCmd function and wait for the COM Port component to notify me with a Notification Event, and then resume the SendATCmd function again.
Scenario: I want to get the count of SMS messages in the phone.
Normally I'd just tell the phone: Hey, how many SMS messages do you have?
and the phone would reply in the notification event.
Thats all good.
But what I really want to do is something like
if SendATCmd('CountSMS')>0 then
for 0 to SMSCount do
AddSMSToList;
The code for SendATCmd looks like this:
function TSE_Z1010.SendATCmd(Cmd: string): TATResult;
begin
fCOMPort.PutString(Cmd); //Sending AT command
//Here is where I would like to pause this function
//wait for the fCOMPort to notify me when data is available
//and then resume this function again.
result:=fTMPATResult;
end;
I've tried using a while-loop, pause, etc etc, but nothing's worked except for one thing, and that's when I put a ShowMessage where the pause should be.
I don't know how ShowMessage works internally but it seems that it doesn't halt the program like while-loop and pause do.
====================
Fixed it.
All I had to do was to add Forms in the uses clause, and then I added while fTMPATResult.Full=false do Application.ProcessMessages; in the part where I wanted to pause the procedure.
"fTMPATResult" is the variable where the incoming COM Port data is stored, globally within the component.
While AsyncPro does have some solutions for this (ontriggerdata), they are event based and make code difficult to read/understand.
here is SendAndWaitForResponse with AsyncPro (like Remy suggested):
TForm1 = class(TForm)
...
private
IOEvent : THandle; // used for IO events
IORx : string;
Comport : TapdComport;
...
procedure TForm1.ComportTriggerAvail(CP: TObject; Count: Word);
var i : integer;
begin
for i:=1 to Count do
IORx:=IORx+Comport.GetChar;
SetEvent(IOEvent);
end;
function TForm1.SerialSAWR(tx : string; TimeOut : integer) : boolean;
begin
Result := False;
try
IORx := ''; // your global var
ResetEvent(IOEvent);
Comport.PutString(tx);
Result := WaitForSingleObject(IOEvent, TimeOut) = WAIT_OBJECT_0;
except
on E : Exception do
// dosomething with exception
end;
end;
// constructor part
IOEvent := CreateEvent(nil, True, False, nil);
// destructor part
if IOEvent <> 0 then
CloseHandle(IOEvent);
Best solution is to create a thread with the comport so your GUI won't be blocked.
I have several applications in production with Asyncpro this way and it works like a charm...
Any time you need to call Application.ProcessMessages() manually, you need to rethink your code design. Doubly so when calling it in a loop.
I do not know how Asynch Pro works, but the Win32 API has a WaitCommEvent() function that does what you are asking for. You call that function to ask the serial port for notification of the desired event(s) and then you can use either WaitForOverlappedResult() or WaitForSingleObject() to wait for those events to actually occur, depending on whether the serial port is operating in overlapped mode or not. No message processing is needed. I would be surprised if Asynch Pro does not somehow expose that functionality.

Preventing crash when doing time consuming task with COM (SKYPE4COM)

I am using the Skype4COM control. My program is trying to delete around 3K contacts from my contact list in Skype using a For loop, however
1) It takes a lot of time
2) it may crash, with a "MyApp has stopped working"
My guess is that somehow I need to "slow down" what I am doing.
Would I do that with Sleep();? Because I am not sure if that is also gonna "pause" the connection between Skype and my program.
To summarize: I am doing an action with a huge ammount of entries, and because of that big ammount, my program is hanging for a long time, and eventually crashes (sometimes). Is there a way to prevent that?
Skype4COM is STA by the way.
Thanks!
Move the processing into a separate thread. Your problem appears to be that Windows thinks the app has stopped responding because it's not processing it's message loop.
Calling Application.ProcessMessages is the wrong solution, because it does a lot more than you might think. You can end up with problems with reentrancy, or things happening that you don't expect.
Make sure that the thread calls CoInitialize before it creates the COM object, and calls CoUnitialize when it's done. You can find examples of using COM in a thread here; the article refers to ADO, but demonstrates the use of CoInitialize/CoUninitialize.
EDIT: After the comments, I'm adding an example of receiving a custom message in a Delphi app. The thread will need access to the UM_IDDELETED constant; you can do this by (preferably) adding it to a separate unit and using that unit in both your main form's unit and the thread's unit, or simply by defining it in both units.
// uCustomMsg.pas
const
UM_IDDELETED = WM_APP + 100;
// Form's unit
interface
uses ..., uCustomMsg;
type
TForm1=class(TForm)
// ...
private
procedure UMIDDeleted(var Msg: TMessage); message UM_IDDELETED;
//...
end;
implementation
procedure TForm1.UMIDDeleted(var Msg: TMessage);
var
DeletedID: Integer;
begin
DeletedID := Msg.WParam;
// Remove this item from the tree
end;
// Thread unit
implementation
uses
uCustomMsg;
// IDListIdx is an integer index into the list or array
// of IDs you're deleting.
//
// TheFormHandle is the main form's handle you passed in
// to the thread's constructor, along with the IDList
// array or list.
procedure TYourThread.Execute;
var
IDToDelete: Integer; // Your ID to delete
begin
while not Terminated and (IDListIdx < IdList.Count) do
begin
IDToDelete := IDList[IDListIdx];
// ... Do whatever to delete ID
PostMessage(TheFormHandle, UM_IDDELETED, IDToDelete, 0);
end;
end;
if you are using a loop to delete each contact you can place a call to Application.ProcessMessages this should fix the issue
[edit]
the call should be in the loop

Resources