|
|
|||||||||
|
|||||||||
| |||||||||
|
|
|
| ||||||||||||||||||||||||||
![]() |
|
|
«
Previous Thread
|
Next Thread
»
|
Thread Tools | Search this Thread | Rate Thread | Display Modes |
|
#1
|
|||
|
|||
|
Hi;
I'm creating an application which connects to some medical instruments and gathers data. The application itself does nothing more than looking in a designated folder to load instruments. Each instrument is encapsulated in a DLL, and has a function which loops and reads the RS232 port, and write the data to a MSSQL2000 database. Because of this loop, which looks like this: repeat Sleep(500); GetDataFromPort; ParseData; if AbortLoop then // AbortLoop is set by a button on a form Break; until False; the application is almost completely blocked, and the CPU usage is about 100 %. I'm looking for a way to run each loop in a separate thread. I have a simple problem. "CoInitialize() has not been called" I have called it in my host application, and in my library. But I'm still getting this error. Any Idea? ![]() |
|
#2
|
|||
|
|||
|
From what I can tell from that piece of code you are in a loop that will in fact take up a lot of processing on the CPU, I would rather use a timer component and put that code in the trigger method excluding the repeat..until part.
|
|
#3
|
|||
|
|||
|
I cannot do that because it would require me to modify the code of about 95 libraries. That's why I'm trying to spawn each library in a new thread in the host application
|
|
#4
|
|||
|
|||
|
Quote:
Seems like there is something awfully wrong with your code or the components you are using. What happened to inheritance? BTW: Why do you think threads will help? I believe that sleep statement is still going to absorb time. Are you sure that all your code is thread safe and that the DLLs are re-entrant? |
|
#5
|
|||
|
|||
|
I'm not using any components; I open the ports with CreateFile.
Each library is completely stand alone and thread safe, but I don't know what re-entrant means. |
|
#6
|
|||
|
|||
|
Quote:
Then I don't understand your environment. Perhaps you can give a more complete explanation. You said (I thought) that your code was loading instruments contained in third party DLLs. Can you clarify which is your code, which are the libraries and which are the DLLs and what you have source to and what you only have an API for. Perhaps that will help us help you. What are the gauges measuring? |
|
#7
|
|||
|
|||
|
Quote:
There's no third party library. Both the host application and libraries are mine. I can not post attachments in here; if you tell me how to send some source files, I'll post the host application and one of the libraries. |
|
#8
|
|||
|
|||
|
Quote:
I will leave this to someone else. I am not really getting a feel for what you are doing. What about the DLLs? What do you mean by a library? Is it a unit a class or ?? If they are all yours, why do you have to change 95 of them? I don't know enough about what you are doing; but it seems like you need to do some serious refactoring with an inheritance tree. Good luck. |
|
#9
|
|||
|
|||
|
I try to describe my application in more details. Sorry if the information I provided was not clear.
There's a host application named "Transfer.exe". It looks in a folder named "Instruments" for files matching the following pattern: "*.INS". Each ".INS" file is a library which exports this functions: Code:
function OpenPort(APort: Byte;
ABaudRate: Integer;
ADataSize: Byte;
AParity: Byte;
AStopBits: Byte): Boolean; export;
procedure ClosePort; export;
procedure ShowConfigDlg(ADataSource, ADataBase: ShortString); export;
procedure ShowAboutDlg; export;
procedure Communicate(ADataSource, ADataBase: ShortString;
APrefix: ShortString; AHandle: HWND;
ATimeOut: Cardinal); export;
function GetInstrumentClass: Byte; export;
function GetDescription: ShortString; export;
For each file, the host application creates an instance of a class which is declared like this: Code:
type
TGetInstrumentName = function(AAppHandle: HWND): ShortString;
TGetDescription = function: ShortString;
TGetInstrumentClass = function: Byte;
TOpenPort = function(APort: Byte;
ABaudRate: Integer;
ADataSize: Byte;
AParity: Byte;
AStopBits: Byte): Boolean;
TCommunicate = procedure(ADataSource, ADataSet: ShortString; APrefix: ShortString; AHandle: HWND; ATimeOut: Cardinal);
TClosePort = procedure();
TShowConfigDlg = procedure(ADataSource, ADataSet: ShortString);
TShowAboutDlg = procedure;
type
{$IFDEF MultiThread}
TTransferModule = class(TThread)
{$ELSE}
TTransferModule = class(TObject)
{$ENDIF}
private
LibHandle: HWND;
_GIN: TGetInstrumentName;
_GD: TGetDescription;
_GIC: TGetInstrumentClass;
_OP: TOpenPort;
_Comm: TCommunicate;
_CP: TClosePort;
_SCD: TShowConfigDlg;
_SAD: TShowAboutDlg;
public
LibFile: String;
{$IFDEF MultiThread}
procedure Execute; override;
{$ELSE}
procedure Execute;
{$ENDIF}
function GetInstrumentName: ShortString;
function GetDescription: ShortString;
function GetInstrumentClass: Byte;
function OpenPort(APort: Byte;
ABaudRate: Integer;
ADataSize: Byte;
AParity: Byte;
AStopBits: Byte): Boolean;
procedure ClosePort;
procedure ShowConfigDlg;
procedure ShowAboutDlg;
constructor Create(ALibFile: String);
end;
Each function has a body which looks like this: Code:
procedure TTransferModule.ShowAboutDlg; begin @_SAD := GetProcAddress(LibHandle, 'ShowAboutDlg'); _SAD; end; When the user invokes the Communicate command in the host application, this code is executed: Code:
procedure TdlgTransfer.btnConnectClick(Sender: TObject);
var
Parity: Byte;
begin
APrefix := cbSerial.Text;
ATimeOut := StrToInt(cbTimeOut.Text);
Parity := NOPARITY;
case cbParity.ItemIndex of
1: Parity := EVENPARITY;
2: Parity := ODDPARITY;
end;
if not CurrentModule.OpenPort(cbPort.ItemIndex + 1, StrToInt(cbBaudRate.Text), StrToInt(cbDataSize.Text), Parity, cbStopBit.ItemIndex) then
raise Exception.Create('Connection Failed');
{$IFDEF MultiThread}
CurrentModule.Resume;
{$ELSE}
CurrentModule.Execute;
{$ENDIF}
end;
The communicate function which contains the loop is defined in a library with an extension of ".INS" like this: Code:
procedure Communicate(ADataSource, ADataBase: ShortString; APrefix: ShortString; AHandle: HWND; ATimeOut: Cardinal); export;
var
AbortLoop: Boolean;
procedure CallBack(ALine: ShortString);
var
CopyData: TCopyDataStruct;
begin
with CopyData do
begin
dwData := 0;
cbData := SizeOf(ALine);
lpData := @ALine;
end;
SendMessage(AHandle, WM_USER, dlgResults.Handle, Integer(@COPYDATA));
Application.ProcessMessages;
AbortLoop := dlgResults.AbortLoop;
end;
var
{some local variables}
begin
dmData := TdmData.Create(nil);
OldHandle := Application.Handle;
try
Application.Handle := AppHandle;
dmData.OpenConnection(ADataSource, ADataBase);
AbortLoop := False;
dlgResults := TdlgResults.Create(nil);
try
if not dlgResults.Visible then
dlgResults.Show
else
dlgResults.BringToFront;
repeat
Buffer := '';
FillChar(PR, SizeOf(PR), 0);
repeat
CallBack('');
until (ReadFromSerial(Port, TimeOut) = STX) or AbortLoop;
if AbortLoop then
Break;
repeat
c := ReadFromSerial(Port, TimeOut);
if (c <> ETX) and (c <> #0) then
Buffer := Buffer + c;
CallBack('');
until (c = ETX) or AbortLoop;
if AbortLoop then
Break;
WriteToSerial(Port, ACK, TimeOut);
{process the data}
MessageBeep(MB_OK);
until False;
finally
ClosePort;
dlgResults.Release;
end;
finally
CallBack(#27);
ClosePort;
dmData.Free;
Application.Handle := OldHandle;
end;
end;
Now, if I declare the conditional define MultiThread, I get an error when I try to create my DataModule in the "*.INS" file. The error says "CoInitialize has not been called". I tried to call it at the initialization section of my first unit in the "*.INS" file, but I had no chance. Hope this makes my question a little more clear |
|
#10
|
|||
|
|||
|
Quote:
You need to call it when you initialize each thread and call CoUnInitialize before you close each thread. A lot of your other code looks very unconventional also. Not sure why you have all those DLLs or why you suffix them with .INS but that is none of my business. |
|
#11
|
|||
|
|||
|
So I need to call CoInitialize and CoUnintialize in my host application?
|
|
#12
|
|||
|
|||
|
Quote:
In each thread. |
|
#13
|
|||
|
|||
|
:-)
I did call CoInitialize() at the beginning of Communicate() procedure, and CoUninitialize() at the finally block. Everything seems to work fine, but I guess I need to send a message back to the host application indicating that the procedure is quitting.
Thank you for your help :-) |
![]() |
| Viewing: Dev Shed Forums > Programming Languages - More > Delphi Programming > Multithreading in Libraries using ADO |
| Thread Tools | Search this Thread |
| Display Modes | Rate This Thread |
|
|
|
|
|