Link to home
Start Free TrialLog in
Avatar of wqclatre
wqclatre

asked on

Is there a way to solve this problem.

I making a program that use a com object (I create it remote) to do some stuff.. I have no control over this comobject so I can not modify it.

When I send my call to the com-object I just return a value (Tru) That means that it has started to process my request. That's all that I get back from the com-object.

When the comobject is finnished it writes a file on the disk on the server that the comobject is installed. This file has the name  "time when job is completed.txt" contains a id number that I need to get back to my client software that I create. Does anoyne have any idea of a way to solve this. I was thinking of creating another comobject on the server that I can call but I can't see any way to match my first request with the file that is generated. (if 2 clients sends different requests that takes different time to complete there is impossible to se on the file name which one that contains the number I need"

Does anyone have any good idea how to solve this=?
Avatar of swift99
swift99

There is an design weakness if multiple requests can be sent and it is important for them to be returned to the correct user, but there is no way to do so.

1. Is this a COM object you created or a third party COM object?  If you created it then there are about a dozen ways to handle the problem.  If third party, then you may be in some trouble, depending on what's in the files and how you need to notify people.

2. You can write a file monitor thread.  Have it wake up every so often and check the server for files.  Depending on other factors there are about a dozen ways of getting the files back to their respective owners.

3. Is the owner's destination represented or representable in the produced file?
hello wgclatre, it seems from your description that this "com-object" would be very easy to get mixed up with which request the created file is for. . . . I've used the API WaitForMultipleObjects( ) in a sepatate thread, to monitor a folder for the creation (or modification) of a file. When the file is created my app is notified and I can do something with the file, like move it or rename it or get info from it. Is this something that would help you? I'll post some code if it is. I'm not to clear on what your problem is. . . Except you should use another com object that works better.
Avatar of wqclatre

ASKER

Slick812 Your code might help me out I guess.
What do I know to check when this file is created? Do I have to know the filename or can it tell me when a new file is created in this folder? (with any name)

Use another com object is not possible. THis com object is done by a company that seems to always mess things up. (and their software costs awful lot of money.) Unfortanly I'm stucked to use it because I don't know what happens behind. (I guess this com object is just a fontend to 496 other com objects that is installed together with this... )
this code uses an API FindFirstChangeNotification( ) function to have the windows system monitor a Folder for any file name changes using the FILE_NOTIFY_CHANGE_FILE_NAME flag. Using this flag will monitor the Folder for any filename change and cause a WaitForMultipleObjects( ) to return. Changes include renaming, creating, or deleting a filename. You can monitor other changes with different flags like FILE_NOTIFY_CHANGE_SIZE or FILE_NOTIFY_CHANGE_LAST_WRITE. The WaitForMultipleObjects( ) freezes the thread it's in, so another thread is created to monitor the folder.
This form has 2 buttons and a TLabel, label5.

  private
    { Private declarations }
    procedure GetFirstLine(FileName: String);

var
  Form1: TForm1;
  hThrd: THandle;
  HndAry1: Array[0..2] of Cardinal;
  DirPath: String;



procedure TForm1.GetFirstLine(FileName: String);
var
  File1: TextFile;
  Line1: String;
begin
{this is a procedure to use the new file
remember that this is run in another thread and is
NOT syncronized with your main thread}
AssignFile(File1, FileName);
Reset(File1);
Readln(File1, Line1);
CloseFile(File1);
{do something here with the file}
{This Code is in a different thread than your main form
if you want to notifiy the main forms thread to do something
then you could use
SendMessage(Form1.Handle, WM_USER+432,0,0); and start the code
when it get's that message}
end;

function ThrdFunc(Parameter: Pointer): Integer; stdcall;
var
Return1: Cardinal;
AryNum: Integer;

begin
{this is the new Thread's function where you do anything
that you want to happen in this thread}
Result := 0;
{Result isn't used here}
try
 HndAry1[0] := CreateEvent(nil, false, false, nil);
 {you need to create an Event so you can easily Exit this thread,
 the Thread is in a wait state so you need an Event to Activate the thread
 from the Wait state of WaitForMultipleObjects, CloseHandle(hThrd)
 will not end the thread in a wait state}

 HndAry1[1] := FindFirstChangeNotification(PChar(DirPath), False, FILE_NOTIFY_CHANGE_FILE_NAME);
 {FindFirstChangeNotification with the FILE_NOTIFY_CHANGE_FILE_NAME prameter will
 monitor the file names in the specified Folder, you need to give the Handle returned to
 the WaitForMultipleObjects( ) in the HndAry1 Array}
  if (HndAry1[0] < 2) or (HndAry1[1] < 2) then
    begin
    MessageBox(Form1.Handle, 'FindFirst Change Notification Failed, could not monitor Folder',
     'Change Notification Failed', MB_OK or MB_ICONERROR);
    Exit;
    {Exit will allow the finally section to execute and End this thread}
    end;
Form1.Label5.Caption := 'Thread is RUNNING';
{I added this label just to let you know when the thread is running}
while true do
begin // a
Return1 := WaitForMultipleObjects(2, @HndAry1, False, INFINITE);
{WaitForMultipleObjects suspendes (freezes) this thread until it gets an Object
Handle event then it will resume the thread}
if Return1 = WAIT_FAILED then
  begin // 1
  MessageBox(Form1.Handle, 'WaitForMultipleObjects Failed, stopped monitoring Folder',
     'Wait Failed', MB_OK or MB_ICONERROR);
  {Break will stop the while loop and the finally section will execute to
  end this thread}
  Break;
  end; // 1
  AryNum := Return1 - WAIT_OBJECT_0;
  {the Return1 - WAIT_OBJECT_0 value is the index number of the AryHandle Array
  which fired the WaitForMultipleObjects( ) to resume the thread}
    if AryNum = 0 then
      begin // 3
      {the AryNum = 0 means that the "Event" has been Set,
      which is only used to End this thread}
      Break;
      end; // 3
    if AryNum = 1 then
      begin // 4
      {test for the file name you want to monitor with If FileExists.
       you can do other tests like for changes in existing file names}
      If FileExists(DirPath+'\File to watch For.txt') then
        begin
        {add code here to get info from the new file and use that info}
        Form1.GetFirstLine(DirPath+'\File to watch For.txt');
        MessageBox(Form1.Handle, PChar('The File "File to watch For.txt" Has Been Created in '+ DirPath),
        'NEW FILE in folder', MB_OK or MB_ICONERROR);
        Break;
        end;
    {if the file does not exist, then the FindNextChangeNotification is called}
      end;  // 4


{the FindNextChangeNotification starts the folder monitoring again}
if not FindNextChangeNotification(HndAry1[1]) then
    begin
    MessageBox(Form1.Handle, 'Find Next Change Notification Failed, stopped monitoring Folder',
        'Next Change Notification Failed', MB_OK or MB_ICONERROR);
    Break;
    end;
end; // a

finally
{MAKE SURE you close all the handles}
CloseHandle(AryHandle[0]);
FindCloseChangeNotification(AryHandle[1]);
CloseHandle(AryHandle[1]);
Form1.Label5.Caption := 'NO Thread';
EndThread(Result);
end;
end;

procedure TForm1.button_GetAddFileClick(Sender: TObject);
var
ThreadID: Cardinal;
begin
DirPath := 'C:\Stuff';
{put the Folder Path that you want to monitor for the
text file in DirPath variable}
SetEvent(HndAry1[0]);
  {SetEvent(HndAry1[0]); is used to end the thread see the ThrdFunc for
   "if AryNum = 0 then" which Exits the Thread}
if GetCurrentThreadID = MainThreadID then
  while MsgWaitForMultipleObjects(1, hThrd, False, INFINITE,
      QS_SENDMESSAGE) = WAIT_OBJECT_0 + 1 do PeekMessage(Msg1, 0, 0, 0, PM_NOREMOVE)
  else WaitForSingleObject(hThrd, INFINITE);
CloseHandle(hThrd);
{the lines above make sure the thread is Dead, but you could disable this
button and not have them}
hThrd := BeginThread(nil, 0, @ThrdFunc, nil, 0, ThreadId);
  {start a New Thread with BeginThread.  All the Wait functions like
  WaitForMultipleObjects suspend the Thread until it gets an Event}
  if hThrd > 0 then
    begin
    //SetThreadPriority(hThrd, THREAD_PRIORITY_BELOW_NORMAL);
    sbut_EndFileAdd.Enabled := True;
    end else
    Memo1.Text := 'Could not Create Thread';
end;

procedure TForm1.button_EndFileAddClick(Sender: TObject);
var
  Msg1: TMsg;
begin
if hThrd = 0 then Exit;
SetEvent(HndAry1[0]);
{using SetEvent will cause the event to fire and the new thread
to drop out of it's wait state and end the thread. Since its in
another thread, you need to wait for that thread to end before you
set the hThrd to 0. }
if GetCurrentThreadID = MainThreadID then
  while MsgWaitForMultipleObjects(1, hThrd, False, INFINITE,
      QS_SENDMESSAGE) = WAIT_OBJECT_0 + 1 do PeekMessage(Msg1, 0, 0, 0, PM_NOREMOVE)
  else WaitForSingleObject(hThrd, INFINITE);
CloseHandle(hThrd);
hThrd := 0;
end;

procedure TForm1.FormDestroy(Sender: TObject);
var
Msg1: TMsg;
begin
{make sure you close the handles before you exit the program to
avoid memory leaks}
if hThrd > 0 then
  begin
  SetEvent(HndAry1[0]);
  if GetCurrentThreadID = MainThreadID then
  while MsgWaitForMultipleObjects(1, hThrd, False, 600,
      QS_SENDMESSAGE) = WAIT_OBJECT_0 + 1 do PeekMessage(Msg1, 0, 0, 0, PM_NOREMOVE)
  else WaitForSingleObject(hThrd, 600);
  CloseHandle(hThrd);
  end;
end;

- - - - - - - - - - - - - - - - - - - - - - - - - - -

if you haven't used threads much this may seem less than easy, ask questions if you need more info
I have never used threads. I will read about it so I know how to ask a good question that I will understand the answer for.. It might take me some time....
One question Slick.

If you look at my old question at:

https://www.experts-exchange.com/jsp/qManageQuestion.jsp?ta=delphi&qid=20320178

Do you think this method here will make my service (in the old question) to not take so much cpu time?
How can I use the abowe in a simple test application that just do a ShowMessage('File is modified'); when c:\test\testfile.txt  is changed?
Btw I can't compile the abowe code.... the AryHandle in the finaly statement is not declared.
the AryHandle should be HndAry1, I copied this witout changing it.  Sorry
as I said in the Intro to the code You can change the flag in FindFirstChangeNotification( ) to watch for other things, there are several file aspects it can monitor. You can set it to monitor if files are written-to or change there size.

if you change the
 HndAry1[1] := FindFirstChangeNotification(PChar(DirPath), False, FILE_NOTIFY_CHANGE_FILE_NAME)

to

 HndAry1[1] := FindFirstChangeNotification(PChar(DirPath), False, FILE_NOTIFY_CHANGE_LAST_WRITE)

it will monitor any files that are written to, but you will still have to check the individual files you are interested in to see if they have changed.

Also if you want to keep monitoring you need to remove the "Break" from the

if AryNum = 1 then
     begin
When I push the "stop button"

I get an acess violation.

Here is what I try:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    procedure GetFirstLine(FileName: String);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  hThrd: THandle;
  HndAry1: Array[0..2] of Cardinal;
  DirPath: String;

implementation

{$R *.dfm}


procedure TForm1.GetFirstLine(FileName: String);
var
 File1: TextFile;
 Line1: String;
begin
{this is a procedure to use the new file
remember that this is run in another thread and is
NOT syncronized with your main thread}
AssignFile(File1, FileName);
Reset(File1);
Readln(File1, Line1);
CloseFile(File1);
{do something here with the file}
{This Code is in a different thread than your main form
if you want to notifiy the main forms thread to do something
then you could use
SendMessage(Form1.Handle, WM_USER+432,0,0); and start the code
when it get's that message}
end;


function ThrdFunc(Parameter: Pointer): Integer; stdcall;
var
Return1: Cardinal;
AryNum: Integer;

begin
{this is the new Thread's function where you do anything
that you want to happen in this thread}
Result := 0;
{Result isn't used here}
try
HndAry1[0] := CreateEvent(nil, false, false, nil);
{you need to create an Event so you can easily Exit this thread,
the Thread is in a wait state so you need an Event to Activate the thread
from the Wait state of WaitForMultipleObjects, CloseHandle(hThrd)
will not end the thread in a wait state}

HndAry1[1] := FindFirstChangeNotification(PChar(DirPath), False, FILE_NOTIFY_CHANGE_FILE_NAME);
{FindFirstChangeNotification with the FILE_NOTIFY_CHANGE_FILE_NAME prameter will
monitor the file names in the specified Folder, you need to give the Handle returned to
the WaitForMultipleObjects( ) in the HndAry1 Array}
 if (HndAry1[0] < 2) or (HndAry1[1] < 2) then
   begin
   MessageBox(Form1.Handle, 'FindFirst Change Notification Failed, could not monitor Folder',
    'Change Notification Failed', MB_OK or MB_ICONERROR);
   Exit;
   {Exit will allow the finally section to execute and End this thread}
   end;
   Form1.Label1.Caption := 'Thread is RUNNING';
{I added this label just to let you know when the thread is running}
while true do
begin // a
Return1 := WaitForMultipleObjects(2, @HndAry1, False, INFINITE);
{WaitForMultipleObjects suspendes (freezes) this thread until it gets an Object
Handle event then it will resume the thread}
if Return1 = WAIT_FAILED then
 begin // 1
 MessageBox(Form1.Handle, 'WaitForMultipleObjects Failed, stopped monitoring Folder',
    'Wait Failed', MB_OK or MB_ICONERROR);
 {Break will stop the while loop and the finally section will execute to
 end this thread}
 Break;
 end; // 1
 AryNum := Return1 - WAIT_OBJECT_0;
 {the Return1 - WAIT_OBJECT_0 value is the index number of the AryHandle Array
 which fired the WaitForMultipleObjects( ) to resume the thread}
   if AryNum = 0 then
     begin // 3
     {the AryNum = 0 means that the "Event" has been Set,
     which is only used to End this thread}
     Break;
     end; // 3
   if AryNum = 1 then
     begin // 4
     {test for the file name you want to monitor with If FileExists.
      you can do other tests like for changes in existing file names}
     If FileExists(DirPath+'\File to watch For.txt') then
       begin
       {add code here to get info from the new file and use that info}
       Form1.GetFirstLine(DirPath+'\File to watch For.txt');
       MessageBox(Form1.Handle, PChar('The File "File to watch For.txt" Has Been Created in '+ DirPath),
       'NEW FILE in folder', MB_OK or MB_ICONERROR);
       Break;
       end;
   {if the file does not exist, then the FindNextChangeNotification is called}
     end;  // 4


{the FindNextChangeNotification starts the folder monitoring again}
if not FindNextChangeNotification(HndAry1[1]) then
   begin
   MessageBox(Form1.Handle, 'Find Next Change Notification Failed, stopped monitoring Folder',
       'Next Change Notification Failed', MB_OK or MB_ICONERROR);
   Break;
   end;
end; // a

finally
{MAKE SURE you close all the handles}
CloseHandle(HndAry1[0]);
FindCloseChangeNotification(HndAry1[1]);
CloseHandle(HndAry1[1]);
Form1.Label1.Caption := 'NO Thread';
EndThread(Result);
end;
end;



procedure TForm1.Button1Click(Sender: TObject);
var
ThreadID: Cardinal;
 Msg1 : TMsg;
begin
DirPath := 'd:\sparas';
{put the Folder Path that you want to monitor for the
text file in DirPath variable}
SetEvent(HndAry1[0]);
 {SetEvent(HndAry1[0]); is used to end the thread see the ThrdFunc for
  "if AryNum = 0 then" which Exits the Thread}
if GetCurrentThreadID = MainThreadID then
 while MsgWaitForMultipleObjects(1, hThrd, False, INFINITE,
     QS_SENDMESSAGE) = WAIT_OBJECT_0 + 1 do PeekMessage(Msg1, 0, 0, 0, PM_NOREMOVE)
 else WaitForSingleObject(hThrd, INFINITE);
CloseHandle(hThrd);
{the lines above make sure the thread is Dead, but you could disable this
button and not have them}
hThrd := BeginThread(nil, 0, @ThrdFunc, nil, 0, ThreadId);
 {start a New Thread with BeginThread.  All the Wait functions like
 WaitForMultipleObjects suspend the Thread until it gets an Event}
 if hThrd > 0 then
   begin
   //SetThreadPriority(hThrd, THREAD_PRIORITY_BELOW_NORMAL);
   Button2.Enabled := True;
   end else
   Memo1.Text := 'Could not Create Thread';
end;



procedure TForm1.Button2Click(Sender: TObject);
var
 Msg1: TMsg;
begin
if hThrd = 0 then Exit;
SetEvent(HndAry1[0]);
{using SetEvent will cause the event to fire and the new thread
to drop out of it's wait state and end the thread. Since its in
another thread, you need to wait for that thread to end before you
set the hThrd to 0. }
if GetCurrentThreadID = MainThreadID then
 while MsgWaitForMultipleObjects(1, hThrd, False, INFINITE,
     QS_SENDMESSAGE) = WAIT_OBJECT_0 + 1 do PeekMessage(Msg1, 0, 0, 0, PM_NOREMOVE)
 else WaitForSingleObject(hThrd, INFINITE);
CloseHandle(hThrd);
hThrd := 0;

end;

procedure TForm1.FormDestroy(Sender: TObject);
var
Msg1: TMsg;
begin
{make sure you close the handles before you exit the program to
avoid memory leaks}
if hThrd > 0 then
 begin
 SetEvent(HndAry1[0]);
 if GetCurrentThreadID = MainThreadID then
 while MsgWaitForMultipleObjects(1, hThrd, False, 600,
     QS_SENDMESSAGE) = WAIT_OBJECT_0 + 1 do PeekMessage(Msg1, 0, 0, 0, PM_NOREMOVE)
 else WaitForSingleObject(hThrd, 600);
 CloseHandle(hThrd);
 end;

end;

end.
Ahh it was only the exceptions from delphi.

Cound you just give me one hint how I can modify this if I want to check for a file change on a specific file (ie if I like to monitor a file instead of a folder)
ASKER CERTIFIED SOLUTION
Avatar of Member_2_248744
Member_2_248744
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks for your help!