⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 compnent.dph

📁 delphi1 的串口通讯控件
💻 DPH
字号:
Components

If you抳e had any exposure to Borland抯 amazing new product, Delphi, you have no doubt noticed 
the minimal amount of code required to write a fully functional application.  This is thanks to
the Visual Component Library (VCL), which contains the components necessary to create an 
application containing buttons, list boxes, and so on simply by 'drawing' them on a form at 
design time.  VCL components are a significant improvement for three reasons:

The component code is linked right into your application, which means that you do not have 
to include seemingly endless custom control files when you ship. However, this does not mean 
that VBX users are left in the cold -- Delphi contains full support for VBX抯 that meet VB 1.0 
specifications.

Delphi has the ability to create custom components as well by simply creating a descendant 
object of a template component (TComponent) or one of its descendants. 	This is actually a 
powerful concept if you think about it for a moment; it means that you can derive your component 
from a fully functional one in a clean, no nonsense fashion. For example, you could derive a 
component from TStringGrid and add in-cell editing, then save it as a separate component.

If all this weren抰 enough, you抣l be pleased to learn that you can debug a component, while 
an application that uses that component is running, by using Delphi抯 integrated debugger.

In Delphi, there are two types of components: visual and non-visual. Visual components do 
something that you can see and often interact with. Non-visual components, on the other hand,
encapsulate a task that has no necessary involvement with the screen at runtime, like run a 
timer, interface with a database, or in our case, implement a simple communications interface. 
You might wonder what the point of using a visual programming language with a non-visual control 
is, and why not just create a unit that contains the same code and place it in your USES 
statement.  At runtime, the difference would be zero, but during your design phase, you wouldn抰 
get the ability to visually change default values, automatically create procedures for events by 
double clicking on their names, or see at a glance exactly what is contained in your application 
-- that is the beauty of a fully visual design environment.


Creating a Component

To begin the process of creating a component, a unit must be created, a parent component must be 
decided upon, and the component must have a register procedure.  Every custom component created
in Delphi must be derived from TComponent or a descendant of it.  Since we do not need graphical 
or user interface abilities, we can stick with deriving our object from TComponent.  All of this 
is done like so:

unit Comm;

interface

uses Classes;

type
  TComm=class(TComponent)
        end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents ('Additional',[TComm]);
end;

end.

If you were to compile the above example and add it to the component library, you might be 
surprised when you found out that it is fully functional.  For example, it can be added to a
form, its position can be changed, and it has properties that can be changed and will stay
changed when you reload the form.  True, it can抰 do anything useful, but you have to start
somewhere.

Now that we have created the skeleton for the component, we can begin to flesh it out.
One of the first things to be decided upon is properties.  Properties for components are usually 
added to the published section of the class declaration.  This allows the property to be 
accessed at design time. A typical property declaration looks like this:

property Port:Byte 
  read FPort  
  write SetPort 
default 1;

This indicates that Port is of type Byte.  But, what is all that funny stuff after the
declaration?  That is a clever way to allow some procedure to be called when you set the value
of a variable.  The read word indicates that a variable called FPort is to be used when reading 
the value of Port.  The write word indicates that the procedure SetPort is to be called when 
setting the value of Port.  The write procedure for a property always has exactly one variable 
of the same type as the property itself, and usually, the variable that the corresponding 
property represents is set within it.  If it had not been necessary to call a procedure when the 
value of Port was set, the declaration could have been rewritten as follows:

property Port:Byte 
  read FPort
  write FPort 
default tptNone;

The write section now indicates that the variable FPort will be accessed for writes as 
well as reads.  This brings up an important point -- properties are not variables -- they are 
access mechanisms for getting and setting values.

Notice the default word in the above statements.  The value after it is the same one that 
appears in the object inspector initially at design time, before you change anything.  Even 
though this default value scheme exists, it is still very important to always initialize the 
variable or call the access function for the property with the same default value during your 
Create constructor.  If you fail to do so, the values you set may not be the values that are 
actually used because the compiler checks to see if the value that is entered in the object 
inspector and stored with the form is the same as the default value.  If it is the actual value, 
it is not set because it was assumed to have been set in the Create constructor.  If this reason 
is not compelling enough, you should be aware that if you create a component at run time, the 
values indicated by the default directive are not used at all!  The only initialization that 
will be done is that which occurred in the Create constructor.  One final hazard concerning the 
Create constructor should be noted; always declare it with the override word, otherwise it will 
never be called at all.  The reason you should be on alert for this is if you have been 
programming in Borland Pascal for any length of time, it is probably etched within your head 
that constructors can never be virtual, so you automatically hit Enter at the end of the line 
without a second thought.


Events and Their Handlers

Now that properties are taken care of, we can go on to the events. Events are properties that 
access pointers to functions.  Consider the following declaration:

property OnReceive:TNotifyReceiveEvent 
  read FOnReceive
write FOnReceive;

This is similar to what we have seen before, except the return type is TNotifyReceiveEvent.
One of the changes to Delphi抯 implementation of Pascal is that a function can now return nearly 
any type.  The type declaration for TNotifyEvent is shown below:

TNotifyReceiveEvent = procedure (Sender:TObject;Count:Word) of object;

You probably recognize this as a standard function template.  What you may not recognize is the 
of object part.  All this does is indicate to the compiler that this function will be a member 
of an object rather than a far function.

When the OnReceive property is read from or written to, what is really happening is that the 
pointer to a procedure is being accessed.  When a form is created during run time, the setting 
of this pointer happens automatically.  Calling the event function is now a piece of cake; all 
you have to do is call the underlying member of the property, FOnReceive, in our example.


Implementing Communications

The specification for the communications component is rather simple.  We need to be able to 
assign the properties at both run-time and design-time, read and write to the comm port, and 
respond to events, such as when the receive buffer has a certain amount of data and when the 
amount of data in the transmit buffer drops below a certain point, making it ready to receive 
more.  The code for the communication component is shown in Listing 1.

New properties include: baud rate, number of data bits, parity, communications port, 
read buffer size, a value to indicate how full the receive buffer has to be before an event is 
triggered, number of stop bits, a value to indicate how empty the transmit buffer has to be 
before an event is triggered, and the write buffer size.  All of these properties can be set at 
run time by clicking on the appropriate cell in the object inspector and selecting the 
appropriate value.

Perhaps the most important property is Port, which defaults to tptNone because generally 
you do not know what port you will be using at design time.  However, if you抮e doing a quick 
test program, you can set the port value using the object inspector and proceed immediately in 
your program to use the communications component without any runtime setup whatsoever.  By 
setting the value of Port, what you are really doing is calling the method SetPort, which is 
ultimately responsible for opening a port and closing a previously open one if an open port 
exists.

Once the port has been opened, SetPort configures it to the proper state by setting all of the 
appropriate properties such as baud rate and parity.  The code that sets these properties 
is not the most efficient in the world because it calls GetCommState and SetCommState for each 
property instead of calling GetCommState, setting all of the properties and then calling 
SetCommState once.  But because this is done only once during the initialization of the port, it 
was not a major concern, and I decided to keep it the way it is.  However, with a few upgrades 
to the code, you can reduce the intitialization time considerably.

The final step in the call to SetPort is to enable communications event posting by calling 
EnableCommNot-ification.  This results in the posting of three distinct events to a window: 
CN_EVENT, CN_RECEIVE, and CN_TRANSMIT.  This, of course, means that we need a window handle to 
post these events to.  AllocateHWnd is simple function for creating a hidden window.  It takes 
a window procedure as a parameter and returns a window handle.  It is the window procedure that 
receives the communications notifications and, in turn, calls the appropriate event handler.

The OnReceive and OnTransmit event handlers are straightforward.  They are called when 
CN_RECEIVE and CN_TRANSMIT are posted to the window respectively.  The only other processing 
that occurs before the actual event is triggered is to determine how many bytes are in the 
appropriate buffer so the count can be included as a parameter.

The OnEvent event is not quite so straightforward.  When the property Events is set, thereby 
calling the procedure SetEvents, a bit mask of events is created.  It is this bit mask that is 
used in the call to SetCommEventMask to enable the desired events.  During the receipt of a 
CN_EVENT, the events that occurred are retrieved and reset by calling GetCommEventMask.  These 
events are added to a set which is then passed to the OnEvent event handler.

To write data to a port, you must call the Write method:

procedure TComm.Write(Data:PChar;Len:Word);

Here, Data is a pointer to a block of data, and Len is the length of the block.  Reading data is 
done similarly:

procedure TComm.Read(Data:PChar;Len:Word);

The parameters are the same as before.  Remember that the value that you use for Len can be 
determined from the Count parameter provided by the OnReceive event.

Error handling is minimal at best. Exceptions are not used, but rather a member function 
-- IsError -- returns True when an error state occurrs.  Calling IsError also resets the error 
state.  There are only three cases when IsError will return True: In trying to open a port, in 
reading from the port, and in writing to the port.


Initializing By the Numbers

When designing a component, the initialization sequence is important to keep in mind.  At first 
I had the initial call to SetPort in the Create method, but a problem quickly showed up: Only 
the property values set in the Create method would be used, rather than the expected values that 
were set using the Object Inspector during design time.  This is because design time properties 
are set after the Create method.

The Loaded method, on the other hand, provided what I was really looking for because it is not 
called until all Object Inspector properties and events are set.  This, unfortunately, created 
another minor problem: SetPort would be called twice, once during the automatic setting of all 
the properties when the form was loaded, and again during my call in Loaded.  The problem is not 
strictly that the same function would be called twice without just cause, but that the first 
call to SetPort would be called at some unknown (at least to me) time.  This meant that none, 
some, or all of the other properties would have been set and their write procedures called 
after SetPort was called.  This can get messy if you consider that properties like 
WriteBufferSize and ReadBufferSize work by actually closing the port and reopening it.  This fix 
was simple: A flag, HasBeenLoaded, is set to False during the Create method and set to True 
during the Loaded method.  When SetPort is called, it checks the flag to make sure it is True 
before allowing the port to open.


Adding the Component

To add the communications component to the Component Palette, use the Install Components dialog 
box.  A palette bitmap (see Figure 1) was designed for the component and will be displayed on 
the Component Palette(see Figure 2) in the 揂dditional

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -