Page 1 of 1

SendIPCMessage and return a string

PostPosted: Wed Nov 25, 2015 10:41 pm
by Overnissen
I'm sorry,

I have been trolling this entire forum, attempting to wrap my brain around how to use SendIPCMessage (with some kind of message) and return a string (or char/byte array) to the caller, allowing me to call from an injected .dll to the "mothership", receiving an answer in a string form (an IP address and a port number in this case).

It might be spelled out in big capital letters and bend in neon, but could one of you extraordinarily talented coders out there be so kind as to show me a couple of lines of working code ?

I can make SendIPCMessage work returning bools, cardinals and what not, no problemo, but strings seem to elude me..



Re: SendIPCMessage and return a string

PostPosted: Thu Nov 26, 2015 8:03 am
by madshi
Which development language/system?

The key trick is to send the string *including* the terminating zero character. This way SendIpcQueue can simply cast the answer/reply buffer to a PChar (Delphi) or LPSTR (C++).

Re: SendIPCMessage and return a string

PostPosted: Thu Nov 26, 2015 5:17 pm
by Overnissen
Sorry, should've mentioned that..

It's in Delphi (XE5).

Including the terminating null, so basically set the buffer to be the length of the string +1, something like:

MyRetVal: AnsiString
MyString: AnsiString;


... and in the other end

PAnsiChar(MyRetVal) := ReturnString + #0;
MyRetValLen := Length(ReturnString)+1;

... or do I have to "resize" the MyRetVal first ?

As in SetLength(MyRetVal,63) and MyRetValLen := 64 (or a similar construct)


Re: SendIPCMessage and return a string

PostPosted: Fri Nov 27, 2015 8:49 am
by madshi
Ouch, you may want to research pointers, Delphi dynamic strings, zero terminated chars etc. It seems you're missing the basics there.

Anyway, when calling SendIpcMessage, use "Length(MyString) + 1", otherwise you're only sending the characters, but no terminating zero character, which makes it harder for the IPC message receiver to interpret the string properly. Then don't use "@" on a dynamic string. Doing that gives you the address of the dynamic string construct, but *not* a pointer to the actual characters. You can use a dynamic string for that purpose, but you should do that only if you know exactly what you're doing. If you're not sure about how dynamic strings work, then I'd suggest that you use a simple local variable array of "char" for the return buffer like this:

Code: Select all
function YourIpcSendingFunction(...);
var retBuf : array [0..100] of char;
    retStr : AnsiString;
  SendIpcMessage(..., retVal, sizeof(retVal), false);
  retStr := retBuf;

In your IPC callback function you can do this:

Code: Select all
function YourIpcCallback(...); stdcall;
var msgStr : AnsiString;
  msgStr := messageBuf;
  if msgStr = 'bla' then
    StrPCopy(answerBuf, 'reply message')
    StrPCopy(answerBuf, 'other message');

Please note that the string you copy via StrPCopy must not exceed the size of the buffer. You should take extra care that this is the case. E.g. check the "answerLen" parameter and make sure it's long enough to hold the string you want to return before calling StrPCopy.

Generally, please don't use pointer logic with dynamic Delphi strings ("string", "AnsiString", "WideString", "UnicodeString"), unless you have fully researched and understood how these dynamic strings works internally exactly. Otherwise you'll end up doing very bad things.

Re: SendIPCMessage and return a string

PostPosted: Fri Nov 27, 2015 11:05 am
by Overnissen
Thanks Mathias, both for the insult and the advice ;)

I get your point, the StrPCopy() seemed to be the missing trick, it was certainly the "passing data to the answerBuf memory space" that went wrong somehow, thanks for the tip with telling the IPC driver to "include" (Length(AnsiStringVar)+1) the terminating NULL char, normally I would just pass it the length/size of the variable (Length(AnsiStringVar) or SizeOf(ArrayType))..

In the past I have just followed your example and had no other need than to return a Caridnal, which I did with Cardinal(answerBuf^) := MyRetVal;

The code that I got to work with dynamic strings (seems to work for me anyway):

The Sender part:
Code: Select all
  IPCBuffer: AnsiString;
  IPCBufferLen: Integer;

  IPCBufferLen := Length(IPCBuffer);


  IPCString := IPCBuffer;

And for the receiver/reply part:
Code: Select all
  Answer: AnsiString;

  if answerBuf <> nil then
    Answer := 'Testnisse';
    StrPCopy(PAnsiChar(answerBuf), Answer);

... also hope this clears it up for those other madCollection fans out there, who have been challenged with exactly the same problem for a while (getting a reply where the reply is a string, dynamic or otherwise)..

Thanks Mathias for your help as always, this is why I've been a devoted fan for more than a decade :)

Re: SendIPCMessage and return a string

PostPosted: Fri Nov 27, 2015 11:26 am
by madshi
Sorry for the "insult", just being brutally honest. The Delphi dynamic string logic is actually quite beautiful, I love it. It can be used to great effect if you understand how it works inside. But there's also great potential to screw things up if you use pointer stuff with dynamic strings.

I'm not fully happy with the code you're using, but if it works it's probably ok. Instead of "Length(PAnsiChar(TheMessage))+1" I'd simply do "Length(TheMessage) + 1", much simpler and also performs quite a bit faster. IPCString := IPCBuffer is not a good idea because after doing that IPCString will always have a length of 1024 bytes. You probably want IPCString to have the correct length. For that simply do "IPCString := PAnsiChar(IPCBuffer)". Any time you assign a PAnsiChar (zero terminated string) to a dynamic string, Delphi will automatically search for the terminating zero char and size the final string accordingly. So the code I've just suggested practically tells Delphi to set the length of IPCString to the length indicated by the terminating zero char in IPCBuffer, which StrPCopy correctly writes into the answer buffer.

Re: SendIPCMessage and return a string

PostPosted: Fri Nov 27, 2015 7:42 pm
by Overnissen
No worries mate, I prefer honesty :wink:

You're right about the IPCString := PAnsiChar(IPCBuffer) (of course), dunno why I didn't think of that when I wrote the code.

Also, with regards to the "Length(PAnsiChar(TheMessage))", you're right, it's just a bad habit I have I think, from fraggin' around in the past with the three (four) different string types i Delphi, I think it just have grown in on me, but I stand corrected. :wink:

Thanks again for all your help and for a beautiful library.