Solved

Mailslots and Terminal Server

Posted on 2001-07-02
13
331 Views
Last Modified: 2010-04-06
Hello there,

I have developed a system consisting of a series of applications, which uses mailslots to communicate changes in data among each other, both locally on a machine and over the network. This practice has proven solid and very practical for my purposes, but...

Now I have had to install the system on a network where we use a Terminal Server (Win2000), from which some of the users run the applications. When the first user logs on to the terminal server and starts an application, the mailslot is created and everything works fine. When the second user logs on to the same terminal server, and starts the same application, I get an error saying that the "file" already exists, and the communication over the mailslot won't work.

So, now I'm looking for another way (or a more advanced way of using mailslots) to communicate among the applications. It has to be in the broadcasting form, meaning that I have to be able to send a "messages" (in my case a string will do) out on the network, and the applications how "cares" must all be able to read it.

Any ideas ?

Best regards
NetGeek
0
Comment
Question by:NetGeek
  • 4
  • 4
  • 2
  • +3
13 Comments
 
LVL 3

Expert Comment

by:smurff
ID: 6244192
NetGeek

I have found mailslots to be a pain. Why not use TCP? There are some great components out there like Indy and errmm the french one :) (cant remember the name off by heart).
You could make a server app that monitors on one port number e.g. 200 for login in then you could keep a string list of who is logged on so when you need to make a broadcast you could just write a loop to go through them. Or if you want to you can send it on ip address 255.255.255.255 which is supposed to be the broadcast ip for TCP but Ive never tried it.

I do have some examples of some client / server apps with TCP if you want them.
just a thought.
regards
Smurff
0
 
LVL 13

Expert Comment

by:Epsylon
ID: 6244216
Use Named Pipes (NT only). Start both client and server from a DOS window.

Modyfied version of: http://www.jgsoftware.com/files/pipes.zip



======= PipeClnt.dpr =========

program PipeClnt;

{$APPTYPE CONSOLE}
 
uses
  Windows, SysUtils, PipeObjs;

var
  Pipe: TPipeClient;
  BrokenPipe: Boolean;
  buf: AnsiString;

begin
  Pipe:= TPipeClient.Create('\\.\pipe\TestServer');
  repeat
    Readln(buf);
    if buf <> '' then begin
      Pipe.WriteStr(buf + #13#10);
      Pipe.ReadStr(buf);
      write('From server: ', buf);
    end;
  until buf = '';
  Pipe.Free;
end.



======= PipeSrv.dpr =========

program PipeSrv;

{$APPTYPE CONSOLE}

uses
  Windows, SysUtils, PipeObjs;

const
  CR = #13;
  LF = #10;
  CRLF = CR + LF;

var
  Pipe: TPipeServer;
  BrokenPipe: Boolean;
  buf: AnsiString;
  sa: TSecurityAttributes;

begin
  writeln('Halt with ^C');
  // Set NIL security
  sa.nLength:= sizeOf(sa);
  sa.bInheritHandle:= false;
  sa.lpSecurityDescriptor:= nil;
  while true do begin
    try
      Pipe:= TPipeServer.Create('\\.\pipe\TestServer', sa);
    except
      on E: Exception do begin
        writeln(E.Message);
        halt(1);
      end;
    end;
    { Wait for client to connect }
    if not Pipe.Connect then begin
      Writeln('Connect failed');
      Pipe.Destroy;
      halt(2);
    end;
    { Client is connected, process input }
    writeln('Client has connected!');
    BrokenPipe:= false;
    repeat
      try
        Pipe.ReadStr(buf);
      except
        BrokenPipe:= true;
      end;
      if not BrokenPipe then
        Pipe.WriteStr(UpperCase(buf));
    until BrokenPipe;
    { Clean up }
    Pipe.Free;
  end; // while
end.



======= PipeObjs.pas=========

unit PipeObjs;

interface

uses
  Windows, Classes, SysUtils;

type
  EPipe = class(Exception);

  TPipe = class(TObject)
    pipeHandle: THandle;
    rc        : Boolean;

    destructor  Destroy; override;

    procedure Read(var buf; bufsize: Integer; var BytesRead: Cardinal);
    procedure Write(var buf; bufsize: Integer; var BytesWritten: Cardinal);
    procedure ReadStr(var buf: AnsiString);
    procedure WriteStr(ToSend: AnsiString);
  end;

  TPipeServer = class(TPipe)
    constructor Create(AName: AnsiString; var sa: TSecurityAttributes); virtual;

    function Connect: Boolean;
  end;

  TPipeClient = class(TPipe)
    constructor Create(AName: AnsiString); virtual;
  end;

implementation

procedure TPipe.Read(var buf; bufsize: Integer; var BytesRead: Cardinal);
begin
  rc:= ReadFile(pipeHandle, buf, bufsize, BytesRead, nil);
  if not rc then
    raise EPipe.Create('TPipe.Read : Broken pipe, error = ' + IntToStr(GetLastError));
end;

procedure TPipe.Write(var buf; bufsize: Integer; var BytesWritten: Cardinal);
begin
  rc:= WriteFile(pipeHandle, buf, bufsize, BytesWritten, nil);
  if not rc then
    raise EPipe.Create('TPipe.Write : Broken pipe, error = ' + IntToStr(GetLastError));
end;

procedure TPipe.ReadStr(var buf: AnsiString);
var
  BytesRead: Cardinal;
begin
  SetLength(buf, 255);
  Read(buf[1], length(buf), BytesRead);
  SetLength(buf, BytesRead);
end;

procedure TPipe.WriteStr(ToSend: AnsiString);
var
  buf: AnsiString;
  BytesWritten: Cardinal;
begin
  buf:= ToSend;
  Write(buf[1], length(buf), BytesWritten);
end;


destructor TPipe.Destroy;
begin
  if pipeHandle <> 0 then CloseHandle(pipeHandle);
  Inherited Destroy;
end;

constructor TPipeServer.Create(AName: AnsiString; var sa: TSecurityAttributes);
begin
  Inherited Create;
  pipeHandle:= CreateNamedPipe(PChar(AName), PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE +
                               PIPE_WAIT, 1, 0, 0, 150, @sa);
  if pipeHandle = INVALID_HANDLE_VALUE then
    raise EPipe.Create('TPipeServer.Create, error = ' + IntToStr(GetLastError));
end;

function TPipeServer.Connect: Boolean;
begin
  Result:= ConnectNamedPipe(pipeHandle, nil);
end;

constructor TPipeClient.Create(AName: AnsiString);
begin
  Inherited Create;
  pipeHandle:= CreateFile(PChar(AName), GENERIC_READ + GENERIC_WRITE,
                          FILE_SHARE_READ, nil, OPEN_EXISTING,
                          FILE_ATTRIBUTE_NORMAL, 0);
  if pipeHandle = INVALID_HANDLE_VALUE then
    raise EPipe.Create('TPipeClient.Create, error = ' + IntToStr(GetLastError));
end;

end.
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 6244393
NetGeek, as quick fix, you could use an identifier unique to the logged on user as Mailslot identifier, so that different users in different sessions on the same server will work correctly.
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 6244409
I found what you need as identifier: when TS is running, a global environment variable SESSIONNAME is set with the session identifier unique to the system. If SESSIONNAME is empty (does not exist), you're app is running on the console.
0
 

Author Comment

by:NetGeek
ID: 6244621
smurff, I agree completely that mailslots are a real pain, but I have a component that makes it very easy to use them, so.. ;-). I've also thought about using TCP/IP sockets with a service application controlling it all, but I really would prefer the appliocations to control it all themselves. I'm quite familiar with the use of TCP/IP sockets (I've written a nice little thing that allows sharing clipboards on the entire network), but thanks for you offer anyway :-)

epsylon, that looks very interessting at first glance, and I'll look over as soon as I can. I'm somewhat worried about the createfile call, but I hope it will work :-)

AvonWyss, giving the mailslots unique identifiers wont work, because one messages has to get to all other mailslots currently running (created).

I'll be back with more as soon as I have tried out the named pipes solution

Best regards
NetGeek
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 6244650
NetGeek, you'll very probably encounter the very same problem when using named pipes since there also the file will already be open when one app is running. And even if you use sockets, you'll run into basically the same problem: only one socket can listen on a specific port...

A possibility would be to make some sort of proxy per machine (as application, service, COM-object or whatever) which opens the one mailslot instance and allows multiple apps with unique ID's to subscribe to the mailslot data distribution (this can again be using mailslots or any other IPC like memory mapped file, named pipes, sockets etc.). By this, you will have one mailslot per machine and several applications connecting to this single mailslot instance.
0
How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

 

Author Comment

by:NetGeek
ID: 6245511
Yeah, the Pipes solution has the same problem :-(

So far I've solved the problem by keeping track of what machines are running on the TS and then using those machine names as additional aliases, but that slows it down some.

I'll try the TCP/IP solution tomorrow, cause I dont think I'll have the same problem there, becasuse the service will only run in one instance,and it keeps track of the connected clients with their handles, not their IP numbers (i hope).

Please keep those ideas coming :-)

Best regards
NetGeek
0
 
LVL 13

Expert Comment

by:Epsylon
ID: 6245550
How about impersonating another user before creating pipes or mailslots?
0
 

Author Comment

by:NetGeek
ID: 6247629
Sorry, Epsylon, but even if I where able to program something like that, I wouldn't feel comfortable using that. I'm installing the system on a clients network, and I have no real control over their network setup.

I've actually started looking into the Terminal Server settings, hoping there's a way to completely separate the individual sessions on the TS.

Best regards
NetGeek
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 6426427
NetGeek, has any solution come up?
0
 

Author Comment

by:NetGeek
ID: 6431328
Not really AvonWyss, I've installed a system where I keep track of what connections are using the terminal server, then assign them a unique alias. The problem with this of course thatI have to send everything out to all the assigned aliases :-(

0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 8701403
No comment has been added lately, so it's time to clean up this TA.
I will leave a recommendation in the Cleanup topic area that this question is:

To be PAQ/Refund

Please leave any comments here within the next seven days.
 
PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!
 
Thank you,
Russell

EE Cleanup Volunteer
0
 

Accepted Solution

by:
PashaMod earned 0 total points
ID: 8816820
Per recommendation,

PashaMod
Community Support Moderator @Experts Exchange
0

Featured Post

Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

Join & Write a Comment

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
This video explains how to create simple products associated to Magento configurable product and offers fast way of their generation with Store Manager for Magento tool.

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now