Love this site? Hate it? Leave us some comments.

View \MODEM.PAS

Simple modem communications program

Submitted By: WEBMASTER
Rating: (Not rated) (Rate It)


{************************************************}
{                                                }
{   Turbo Pascal for Windows                     }
{   Tips & Techniques Demo Program               }
{   Copyright (c) 1991 by Borland International  }
{                                                }
{************************************************}


program ModemoDemo;

{$R Modem}
uses WinTypes, WinProcs, WObjects, Strings;

type
  TEditLine = array[0..50] of Char;

const
  idEdit      = 100;
  idDial      = 201;
  idDialStart = 101;
  idPhoneNum  = 102;
  idConfigure = 202;
  id1200      = 101;
  id2400      = 102;
  id4800      = 103;
  id9600      = 104;
  idOdd       = 105;
  idEven      = 106;
  idNone      = 107;
  idComm1     = 108;
  idComm2     = 109;
  id1Stop     = 110;
  id2Stop     = 111;
  id7Data     = 112;
  id8Data     = 113;

  LineWidth   = 80{ Width of each line displayed.                 }
  LineHeight  = 60{ Number of lines that are held in memory.      }

  { The configuration string below is used to configure the modem.  }
  { It is set for communication port 2, 2400 baud, No parity, 8 data }
  { bits, 1 stop bit.                                                }

  Comm  : Char = '2';
  Baud  : Word = 24;
  Parity: Char = 'n';
  Stop  : Char = '1';
  Data  : Char = '8';

  DialStart: TEditLine = 'ATDT';
  PhoneNumber: TEditLine = '';

type
  TApp = object(TApplication)
    procedure Idle; virtual;
    procedure InitMainWindow; virtual;
    procedure MessageLoop; virtual;
  end;

  PBuffer = ^TBuffer;
  TBuffer = object(TCollection)
    Pos: Integer;
    constructor Init(AParent: PWindow);
    procedure FreeItem(Item: Pointer); virtual;
    function PutChar(C: Char): Boolean;
  end;

  PCommWindow = ^TCommWindow;
  TCommWindow = object(TWindow)
    Cid: Integer;
    Buffer: PBuffer;
    FontRec: TLogFont;
    CharHeight: Integer;
    constructor Init(AParent: PWindowsObject; ATitle: PChar);
    destructor Done; virtual;
    procedure Configure(var Message: TMessage);
      virtual cm_First + idConfigure;
    procedure Dial(var Message: TMessage);
      virtual cm_First + idDial;
    procedure Error(E: Integer; C: PChar);
    procedure Paint(PaintDC: HDC; var PaintInfo: TPaintStruct); virtual;
    procedure ReadChar; virtual;
    procedure SetConfigure;
    procedure SetHeight;
    procedure SetUpWindow; virtual;
    procedure wmChar(var Message: TMessage);
      virtual wm_Char;
    procedure wmSize(var Message: TMessage);
      virtual wm_Size;
    procedure WriteChar;
  end;

{ TBuffer }
{ The Buffer is used to hold each line that is displayed in the main   }
{ window.  The constant LineHeight determines the number of lines that }
{ are stored.  The Buffer is prefilled with the LineHeight worth of    }
{ lines.                                                               }
constructor TBuffer.Init(AParent: PWindow);
var
  P: PChar;
  I: Integer;
begin
  TCollection.Init(LineHeight + 1, 10);
  GetMem(P, LineWidth + 1);
  P[0] := #0;
  Pos := 0;
  Insert(P);
  for I := 1 to LineHeight do
  begin
    GetMem(P, LineWidth + 1);
    P[0] := #0;
    Insert(P);
  end;
end;

procedure TBuffer.FreeItem(Item: Pointer);
begin
  FreeMem(Item, LineWidth + 1);
end;

{ This procedure processes all incoming information from the com     }
{ port.  This procedure is called by TCommWindow.ReadChar.           }

function TBuffer.PutChar(C: Char): Boolean;
var
  Width: Integer;
  P: PChar;
begin
  PutChar := False;
  Case C of
    #13: Pos := 0;                          { if a Carriage Return.  }
    #10:                                    { if a Line Feed.        }
      begin
        GetMem(P, LineWidth + 1);
        FillChar(P^, LineWidth + 1, ' ');
        P[Pos] := #0;
        Insert(P);
      end;
    #8:
      if Pos > 0 then                       { if a Delete.           }
      begin
        Dec(Pos);
        P := At(Count - 1);
        P[Pos] := ' ';
      end;
   #32..#128:                               { else handle all other  }
    begin                                   { displayable characters.}
      P := At(Count - 1);
      Width := StrLen(P);
      if Width > LineWidth then             { if line is to wide     }
      begin                                 { create a new line.     }
        Pos := 1;
        GetMem(P, LineWidth + 1);
        P[0] := C;
        P[1] := #0;
        Insert(P);
      end
      else                                   { else add character    }
      begin                                  { to current line.      }
        P[Pos] := C;
        Inc(Pos);
        P[Pos] := #0;
      end;
    end;
  end;
  if Count > LineHeight then                 { if too many lines     }
  begin                                      { have been added delete}
    AtFree(0);                               { current line and let  }
    PutChar := True;                         { the call procedure    }
  end;                                       { scroll up.            }
end;

{ TCommWindow }
{ The CommWindow displays the incoming and out going text.              }
{ Note that the text typed by the user is displayed by                  }
{ being echoed back to the ReadChar procedure.  So there is no need for }
{ wmChar to write a character to the screen.                          }
constructor TCommWindow.Init(AParent: PWindowsObject; ATitle: PChar);
begin
  TWindow.Init(AParent, ATitle);
  Attr.Style := Attr.Style or ws_VScroll;
  Attr.Menu := LoadMenu(HInstance, 'Menu_1');
  Scroller := New(PScroller, Init(@Self, 1, 1, 100, 100));
  Buffer := New(PBuffer, Init(@Self));
end;

{ Close the Comm port and deallocate the Buffer.                      }
destructor TCommWindow.Done;
begin
  Error(CloseComm(Cid), 'Close');
  Dispose(Buffer, Done);
  TWindow.Done;
end;

procedure TCommWindow.Configure(var Message: TMessage);
var
  Trans: record
    R1200,
    R2400,
    R4800,
    R9600,
    ROdd,
    REven,
    RNone,
    RComm1,
    RComm2,
    R1Stop,
    R2Stop,
    R7Data,
    R8Data: Word;
  end;

  D: TDialog;
  P: PWindowsObject;
  I: Integer;

begin
  D.Init(@Self, 'Configure');
  for I := id1200 to id8Data do
    P := New(PRadioButton, InitResource(@D, I));
  with Trans do
  begin
    R1200 := Byte(Baud = 12);
    R2400 := Byte(Baud = 24);
    R4800 := Byte(Baud = 48);
    R9600 := Byte(Baud = 96);

    ROdd  := Byte(Parity = 'o');
    REven := Byte(Parity = 'e');
    RNone := Byte(Parity = 'n');

    RComm1 := Byte(Comm = '1');
    RComm2 := Byte(Comm = '2');

    R1Stop := Byte(Stop = '1');
    R2Stop := Byte(Stop = '2');

    R7Data := Byte(Data = '7');
    R8Data := Byte(Data = '8');
  end;
  D.TransferBuffer := @Trans;
  if D.Execute = id_Ok then
  begin
    with Trans do
    begin
      Baud := (R1200 * 12) + (R2400 * 24) + (R4800 * 48) + (R9600 * 96);
      if ROdd = bf_Checked then
        Parity := 'o';
      if REven = bf_Checked then
        Parity := 'e';
      if RNone = bf_Checked then
        Parity := 'n';
      if R1Stop = bf_Checked then
        Stop := '1'
      else
        Stop := '2';
      if RComm1 = bf_Checked then
        Comm := '1'
      else
        Comm := '2';
      if R7Data = bf_Checked then
        Data := '7'
      else
        Data := '8';
      SetConfigure;
    end;
  end;
  D.Done;
end;


procedure TCommWindow.Dial(var Message: TMessage);
var
  Trans: record
    Start: TEditLine;
    Phone: TEditLine;
  end;
  D: TDialog;
  P: PWindowsObject;
begin
  D.Init(@Self, 'Dial');
  P := New(PEdit, InitResource(@D, idDialStart, SizeOf(TEditLine)));
  P := New(PEdit, InitResource(@D, idPhoneNum, SizeOf(TEditLine)));
  StrCopy(Trans.Start, DialStart);
  StrCopy(Trans.Phone, PhoneNumber);
  D.TransferBuffer := @Trans;
  if D.Execute = id_Ok then
  begin
    StrCopy(DialStart, Trans.Start);
    StrCopy(PhoneNumber, Trans.Phone);
    StrCat(PhoneNumber, #13);
    StrCat(PhoneNumber, #10);
    Error(WriteComm(CId, DialStart, StrLen(DialStart)), 'Writing');
    Error(WriteComm(CId, PhoneNumber, StrLen(PhoneNumber)), 'Writing');
    PhoneNumber[StrLen(PhoneNumber) - 2] := #0;
  end;
  D.Done;
end;


{ Checks for comm errors and writes any errors.                       }
procedure TCommWindow.Error(E: Integer; C: PChar);
var
  S: array[0..100] of Char;
begin
  if E >= 0 then exit;
  Str(E, S);
  MessageBox(GetFocus, S, C, mb_Ok);
  Halt(1);
end;

{ Redraw all the lines in the buffer using ForEach.                }
procedure TCommWindow.Paint(PaintDC: HDC; var PaintInfo: TPaintStruct);
var
  I: Integer;
  Font: HFont;

  procedure WriteOut(Item: PChar); far;
  begin
    TextOut(PaintDC, 0, CharHeight * I, Item, StrLen(Item));
    inc(I);
  end;

begin
  I := 0;
  Font := SelectObject(PaintDC, CreateFontIndirect(FontRec));
  Buffer^.ForEach(@WriteOut);
  DeleteObject(SelectObject(PaintDC, Font));
end;

{ Read a character from the comm port, if there is no error then call }
{ Buffer^.PutChar to add it to the buffer and write it to the screen. }
procedure TCommWindow.ReadChar;
var
  Stat: TComStat;
  I, Size: Integer;
  C: Char;
begin
  GetCommError(CID, Stat);
  for I := 1 to Stat.cbInQue do
  begin
    Size := ReadComm(CID, @C, 1);
    Error(Size, 'Read Comm');
    if Size = 0 then Exit;
    if C <> #0 then
    begin
      if Buffer^.PutChar(C) then
      begin
        ScrollWindow(HWindow, 0, -CharHeight, Nil, Nil);
        UpDateWindow(HWindow);
      end;
      WriteChar;
    end;
  end;
end;

procedure TCommWindow.SetConfigure;
var
  Config: array[0..20] of Char;
  S: array[0..5] of Char;
  DCB: TDCB;
begin
  StrCopy(Config, 'com?:??,?,?,?');
  Config[3] := Comm;
  Config[8] := Parity;
  Config[10] := Data;
  Config[12] := Stop;
  Str(Baud, S);
  Config[5] := S[0];
  Config[6] := S[1];
  BuildCommDCB(Config, DCB);
  DCB.ID := CID;
  Error(SetCommState(DCB), 'Set Comm State');
end;

procedure TCommWindow.SetUpWindow;
var
  DCB: TDCB;
  CommString: array[0..5] of Char;
begin
  TWindow.SetUpWindow;
  SetHeight;

{ Open for Comm2 2400 Baud, No Parity, 8 Data Bits, 1 Stop Bit }
  StrCopy(CommString, 'Com ');
  CommString[3] := Comm;
  Cid := OpenComm(CommString, 1024, 1024);
  Error(Cid, 'Open');
  SetConfigure;
  WriteComm(Cid, 'ATZ'#13#10, 5){ Send a reset to Modem. }
end;

{ Call back function used only to get record structure for fixed   }
{ width font.                                                      }
function GetFont(LogFont: PLogFont; TM: PTextMetric; FontType: Word;
  P: PCommWindow): Integer; export;
begin
  if P^.CharHeight = 0 then
  begin
    P^.FontRec := LogFont^;
    P^.CharHeight := P^.FontRec.lfHeight;
  end;
end;

{ Get a fixed width font to use in the TCommWindow.  Use EnumFonts  }
{ to save work of create the FontRec by hand.                         }
{ The TScroller of the main window is also updated know that the font }
{ height is known.                                                    }
procedure TCommWindow.SetHeight;
var
  DC: HDC;
  ProcInst: Pointer;
begin
  DC := GetDC(HWindow);
  CharHeight := 0;
  ProcInst := MakeProcInstance(@GetFont, HInstance);
  EnumFonts(DC, 'Courier', ProcInst, @Self);
  FreeProcInstance(ProcInst);
  ReleaseDC(HWindow, DC);

  Scroller^.SetUnits(CharHeight, CharHeight);
  Scroller^.SetRange(LineWidth, LineHeight);
  Scroller^.ScrollTo(0, LineHeight);
end;


{ Write the character from the pressed key to the Comuniction Port.   }
procedure TCommWindow.wmChar(var Message: TMessage);
begin
  Error(WriteComm(CId, @Message.wParam, 1), 'Writing');
end;

procedure TCommWindow.wmSize(var Message: TMessage);
begin
  TWindow.wmSize(Message);
  Scroller^.SetRange(LineWidth, LineHeight - (Message.lParamhi div CharHeight));
end;

procedure TCommWindow.WriteChar;
var
  DC: HDC;
  Font: HFont;
  S: PChar;
  APos: Integer;
begin
  APos := Buffer^.Count - 1;
  S := Buffer^.AT(APos);
  APos := (APos - Scroller^.YPos) * CharHeight;
  if APos < 0 then exit;
  if HWindow <> 0 then
  begin
    DC := GetDC(HWindow);
    Font := SelectObject(DC, CreateFontIndirect(FontRec));
    TextOut(DC, 0, APos, S, StrLen(S));
    DeleteObject(SelectObject(DC, Font));
    ReleaseDC(HWindow, DC);
  end;
end;

{ TApp }
procedure TApp.Idle;
begin
  if MainWindow <> Nil then
    if MainWindow^.HWindow <> 0 then
      PCommWindow(MainWindow)^.ReadChar;
end;

procedure TApp.InitMainWindow;
begin
  MainWindow := New(PCommWindow, Init(Nil, 'Comm Test'));
end;

{ Add Idle loop to main message loop.                                 }
procedure TApp.MessageLoop;
var
  Message: TMsg;
begin
  while True do
  begin
    if PeekMessage(Message, 0, 0, 0, pm_Remove) then
    begin
      if Message.Message = wm_Quit then
      begin
        Status := Message.WParam;
        Exit;
      end;
      if not ProcessAppMsg(Message) then
      begin
        TranslateMessage(Message);
        DispatchMessage(Message);
      end;
    end
    else
      Idle;
  end;
end;

var
  App: TApp;
begin
  App.Init('Comm');
  App.Run;
  App.Done;
end.
 
corner
© 1996-2008. All rights reserved. Reproduction in whole or in part, in any form or medium without express written permission is prohibited.
Violators of this policy may be subject to legal action. Please read our Terms Of Use and Privacy Statement for more information.
Publisher: Lars Hagelin.
bootstrapLabs Logo A bootstrapLabs project.