Interprocess comm & single sign-on - best practices...

Hello all,

We are in the middle of designing a suite of programs for a client and I have a couple of questions....

The suite consists of 5 applications that can all be run simultaneously on the client’s computer. For these 5 clients we need single sign-on functionality and also a way for these 5 to talk to each other. We were thinking along the lines of having an application running in the task tray that handles logon. So when you start one of the five apps it checks to see if the task app is running. If it is, the new app gets its logon info from there, if not it starts the task app, which handles logon and then starts the original app. This way, once the user has logged on for the day the task app stays in the task tray and makes sure you can start the other apps without having to logon...

The five apps may also need to talk to each other, and we where thinking that this could be handled through the task app.

Is this feasible? If not, how should we solve it? Also, how do the apps talk to each other? IPC?

Does anyone know of any sample code out there that does this, or something similar? Or any good libraries for inter process comms?

All input appreciated....

Best regards,
Marius
BarCode99Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

geobulCommented:
Hi,

You may use different way for IPC like regular files, memory mapped files, pipes, DDE, etc. (even TCP using local host). The example below uses Windows message WM_COPYDATA:

// sending app - sends a string here but you could also send complicated structures (records, for instance)
type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    procedure SendToServer(WindowName: string; data: ShortString);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.SendToServer(WindowName: string; data: ShortString);
var
  COPYDATA: TCopyDataStruct;
  h: HWND;
begin
  h := FindWIndow(nil, PChar(WindowName));
  if IsWindow(h) then begin
    // Fill the COPYDATASTRUCT:
    with COPYDATA do begin
      dwData:= 0;
      cbData:= SizeOf(data); // size
      lpData:=@data; // address
    end;
    // Send to the server:
    SendMessage(h, // Who are we sending to?
             WM_COPYDATA,  // This message enables it all
             Handle,       // Handle of the sender
             Integer(@COPYDATA) // Address of the filled COPYDATASTRUCT
            );
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  SendToServer('ServerApp', 'This text was sent');
end;

// receiving app:
type
  TForm1 = class(TForm)
  private
    { Private declarations }
    procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  data: String;

implementation

{$R *.DFM}

procedure TForm1.WMCopyData(var Msg: TWMCopyData);
begin
  data := String(ShortString(Msg.CopyDataStruct^.lpData^));
  // do something with the received info here
  ShowMessage(data);
end;

Regards, Geo
0
geobulCommented:
Forgot to say that main form's name (caption, title) of the receiving app should be set to 'ServerApp' in order to make the example above work.
0
Colin_DawsonCommented:
Another approach is the RegisterWindowsMessage api call.  You can use this to dynamically allocate some windows messages that are common to your applications.  Can you then use the normal windows message loop to send messages between applications.  You can place any payload that you wish on the message, but I prefer to think that the application that sends the message is responsible for any memory cleanup.   I find that this is best achieved using the SendWindowsMessageCallback function (I've used it internally within an application, but not tried sending between multiple apps).

Ideally, there wouldn't be a program that runs in the tray, if one/or more applications are loaded then why not ask one of them for the logon details direct?

You could also implement a scheme that uses the Windows user accounts to provide the security.  This approach would mean that your users never need to "Logon" as such, what with having already done so to get into windows.
0
Cloud Class® Course: Certified Penetration Testing

This CPTE Certified Penetration Testing Engineer course covers everything you need to know about becoming a Certified Penetration Testing Engineer. Career Path: Professional roles include Ethical Hackers, Security Consultants, System Administrators, and Chief Security Officers.

Wim ten BrinkSelf-employed developerCommented:
I'd suggest that you create a COM object for the logon data. Preferably an out-of-process COM object. This would then be an executable that could have a trayicon associated to itself. All the client applications would just have to use the COM interface to communicate with this COM server. And once all the applications have unloaded, the COM server itself will unload itself too. (Unless you explicitly loaded it.)
The advantage is that basically, all you have to do is create a COM object with two methods:

  function Logon(const Name, Password:WideString):Boolean;
  function IsLoggedOn:Boolean;

One function to log on and the other just to check if the user is logged on. The logon dialog could be stored in the COM object too, btw. But it's nicer to use a generic form in the other projects so you can customize them for every project.

Communications between those applications could technically be done through the COM mechanism but it's a bit more complex, since it involves callbacks/events...

COM isn't an easy technique for the uninitiated, though. But on the Windows platform it works quite well for inter-process communications.
0
JohnjcesCommented:
I have two NT services that talk to each other using the WM_COPYDATA message and it works well. They talk back and forth easily!

I used a free component from:

http://www.delphipages.com/edit/count.cfm?ID=3804

That solved my problem. Instead of a tray app, try a service application for your logon info and validation.
0
BarCode99Author Commented:
Thanks so far guys,

I'm currently looking at Workshop_Alex's proposal using COM objects for the centralised logon process. Looks like it could be just what we need...
What I think we'll end up doing is having the logon form and processing in the out-of-process COM object and let it run as long as the user does not log off or ends his windows session...

At the moment I don't know the exact requirement for the interprocess comms, but I'll know more by the end of the week so I'll leave it for the moment...

More later in the week...

Marius
0
BarCode99Author Commented:
Workshop_Alex,

You haven't by any chance got an example kicking about of a "out-of-process COM object"?

I have the book Mastering Delphi 7 and there is bit about COM in there, but mainly in-process. By out-of-process, do you mean a straight forward application that implements COM interfaces?

Aby thoughts appreciated...

marius

0
Wim ten BrinkSelf-employed developerCommented:
I know a very good book for Delphi and COM: "Delphi COM Programming" by Eric Harmon
# Textbook Binding: 500 pages
# Publisher: New Riders Publishing; 1st edition (January, 2000)
# ISBN: 1578702216
# Product Dimensions: 9.0 x 6.1 x 1.0 inches
# Shipping Weight: 1.5 pounds.

http://www.amazon.com/exec/obidos/tg/detail/-/1578702216/qid=1101391693/sr=8-2/ref=sr_8_xs_ap_i2_xgl14/104-6140450-8589513?v=glance&s=books&n=507846

It's what I used when I started to learn about COM. By now, I consider it outdated (because I'm more advanced than the book now) but it's a very good place to start. :-)

In-process COM objects are just DLL's. Out-Of-Process COM objects are executables. Basically, you start an OOP COM object by creating a new application and then with File/New you add a COM object to this application. It's reasonable similar to an IP COM object from this point on, but there will be a few pitfalls too. Unlike a DLL, an OOP COM object gets registered by just executing it once.
In this OOP COM Server you could use global variables to store data that must be shared with ALL clients. Of course you will have to be aware of possible multi-threading issues and make sure you protect this data well. (Critical sections, for example.)

But the COM object will stay in existance for as long as at least one application is keeping an instance open to it. To build a quickie, do the following:
Start a new (EXE) project, name it COMServer and use File/New to add a COM object to it. Give it some name (e.g. Test) and make it apartment threaded for now. You get the type library editor in front of you and just add a single method called 'SayHello' to it. Go to the object source and add: "var Count: Integer = 0;" as a global variable. (You can make it more complex later on, if you like.) Then fill in the SayHello function like this:

function TTest.SayHello: HResult;
begin
  Result := S_OK;
  Inc( Count );
  MessageBox( GetDesktopWindow, PChar( Format( 'Call #%d', [ Count ] ) ), 'Hello, World.', MB_OK );
end;

(You'll need to use SysUtils for this too, btw.) Compile this project, run it once to initialize it and you're done.

For the client application, we do something near-similar. We start with a new (EXE) project and we go to the form. Our previous project generated a file called something like COMServer_TLB.pas so we add the unit COMServer_TLB to this form. (It saves us the trouble of generating it based on the type library.) then add an OnClick event to this form. One line of code is needed here: "CoTest.Create.SayHello;".
Above code will create the COM object, execute the method and immediately release the object again once the method is finished. (Since it shows a messagebox, it is finished once you click [OK].)
If you run just the client, you will see that it always displays "Call #1".
If you first start the server, then the client then you will see the counter increase after every click on the client form.
If you run multiple clients and first click on one client but don't close the messagebox, clicking on the other clients will also increase the counter.
Be aware that while the clients are running, closing the server could cause some problems for them, since that would kill the COM object before they're finished with it.

Above "quickie" is just the simplest example of OOP COM objects. You could make it more complex, as long as you're aware of the many threading issues that you might encounter... But these problems will also exist with other solutions.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
BarCode99Author Commented:
125 to geobul for the WM_COPYDATA
125 to workshop_alex for the COM input

BTW, we decided to use Windows Messaging for the inter app comms (as the requirement was simply sending a command and an ID). Also, have ordered the COM book and will implement the tray app as a COM server.

Thanks guys!
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.