> I want a batch process to continue, but I want a popup message window
> if it discovers an error.
Use a normal TForm for that.
Gambit
> I want a batch process to continue, but I want a popup message window
> if it discovers an error.
If the batch process is a console process just write a message to the
console window using WriteLn. If the console is supposed to be hidden
or you want something more visible for the user that does *not* block
further processing then you would have to create a message dialog (use
Windows.MessageBox) in a secondary thread. Using a normal form with
Show would not work if the program does not return to the message loop
afterwards. You could make it paint itself by calling its Update method
after the Show, but it would otherwise appear dead to the user (and the
OS).
An alternative method would be to collect the error messages in a
TStringlist first and only show a dialog with the collected messages
once the processing has completed.
--
Peter Below (TeamB)
Don't be a vampire (http://slash7.com/pages/vampires),
use the newsgroup archives :
http://www.tamaracka.com/search.htm
http://groups.google.com
> Windows.MessageBox in a secondary thread.
Thanks, I've written this code below and that seems to work. :-)
type
TMsgRecord = record
thread : Integer;
msg : string[30];
end;
var
id : LongWord;
threadhandle : Integer;
msgrec : TMsgRecord;
ThreadVar msgPtr : ^TMsgRecord;
function ShowMsg(Parameter : Pointer) : Integer;
begin
// Set up a 0 return value
Result := 0;
msgPtr := Parameter;
ShowMessage(msgPtr.msg);
// End the thread
EndThread(0);
end;
//.... blah die blah, etcetera
if TLogger.getInstance.GetWrittenError() then
begin
msgrec.thread := 1;
msgrec.msg := 'Error in populate main dbf';
threadhandle := BeginThread(nil, 0, Addr(ShowMsg), Addr(msgrec), 0, id);
end;
>
> "Peter Below (TeamB)" <no...@nomail.please> wrote in
>
>
> > Windows.MessageBox in a secondary thread.
>
>
> Thanks, I've written this code below and that seems to work. :-)
>
> var
> id : LongWord;
> threadhandle : Integer;
> msgrec : TMsgRecord;
> ThreadVar msgPtr : ^TMsgRecord;
>
> function ShowMsg(Parameter : Pointer) : Integer;
> begin
> // Set up a 0 return value
> Result := 0;
>
> msgPtr := Parameter;
>
> ShowMessage(msgPtr.msg);
I said "Windows.Messagebox" for a reason, you know. The VCL is not
threadsafe, it is not a good idea to show a VCL form in a secondary
thread, and ShowMessage does exactly that.
> I said "Windows.Messagebox" for a reason, you know. The VCL is not
> threadsafe, it is not a good idea to show a VCL form in a secondary
> thread, and ShowMessage does exactly that.
Ok I'll look into it and change it. I made the mistake that I thought TForm
was not the good option, but I could use ShowMessage.
> I said "Windows.Messagebox" for a reason, you know.
Ok I changed this:
//ShowMessage(msgPtr.msg);
Windows.MessageBox(0, Addr(msgPtr.msg[1]), 'Populate Main',
MB_SETFOREGROUND );
Now the handle should be the handle of the calling form. But, it's a dll
extern fuction with no GUI, so there is no calling form, hence I made it 0.
Also the MessageBox seems to take a pointer to a null terminated string, but
at msgPtr.msg[0] I think Delphi puts the length of the string. Also I am a
bit surprised by the syntax string[128], I thought I should use char[128],
but that's probably C++/Java syntax. I do hope string[128] produces a null
terminated string like this then?
It seems to work though.
Oh yes, I do have more experience in C++, C# and Java then in Delphi, so I
am a bit confused about the string syntax in Delphi. But this is ok then?
>
> "Peter Below (TeamB)" <no...@nomail.please> wrote
>
> > I said "Windows.Messagebox" for a reason, you know.
>
> Ok I changed this:
>
> //ShowMessage(msgPtr.msg);
> Windows.MessageBox(0, Addr(msgPtr.msg[1]), 'Populate Main',
> MB_SETFOREGROUND );
Argh, do not use Addr, especially not with strings. If msgPrt.msg is an
array of string or a TStringlist use a PChar cast:
Windows.MessageBox(0, PChar(msgPtr.msg[1]), 'Populate Main',
MB_SETFOREGROUND );
A PChar cast on a string returns the address of the first character
(and Delphi strings are zero-terminated to be compatible with API
routines when needed). But it also does something extra: it calls
UniqueString on the string first to make sure the address resulting
from the PChar cast is for a string with a reference count of 1, in
case the API function needs to write to the string memory.
> Now the handle should be the handle of the calling form. But, it's a
> dll extern fuction with no GUI, so there is no calling form, hence I
> made it 0.
Perfectly OK, but you could also pass the return value of
GetActiveWindow or GetForegroundWindow to it.
>Also the MessageBox seems to take a pointer to a null
> terminated string, but at msgPtr.msg[0] I think Delphi puts the
> length of the string.
Only if you are using the old Shortstring or String[n] types.
S: Shortstring;
as a variable declaration is the same as
S: String[255];
255 is the maximum size for a shortstring type, you can declare types
with smaller memory footprints by using lower number inside the square
brackets.
It gives you memory for a 256 byte array, the first element at index 0
stores the length of the string contained in the array (there is no #0
terminator for shortstrings), 1-byte characters are stored from index
1. If you need a #0 terminator you have to add it in code. To pass a
shortstring as a char* to an API function add a #0 and pass @S[1] (or
Addr(S[1])).
S: String;
declares a variable of type AnsiString (at the moment, with Tiburon
that will change to UnicodeString). The Ansistring type is a dynamic
array of 1 byte characters, with memory mostly managed by the compiler.
The array can store up to Max(Longint)-8 characters theoretically and
it has an associated reference count managed by the compiler. The
compiler also makes sure the strings end in a #0 (which is not counted
by Length). Shortstrings are value types, Ansistrings are reference
types (the variable stores a pointer to a memory block on the heap). To
pass an ansistring as a char* you use Pchar(S).
Also I am a bit surprised by the syntax
> string[128], I thought I should use char[128], but that's probably
> C++/Java syntax. I do hope string[128] produces a null terminated
> string like this then?
No, see above.
> It seems to work though.
Famous last words <g>.
> Now the handle should be the handle of the calling form. But, it's a
> dll extern fuction with no GUI, so there is no calling form, hence I
> made it 0.
That is fine.
> Also the MessageBox seems to take a pointer to a null terminated
> string, but at msgPtr.msg[0] I think Delphi puts the length of the string.
Only for short strings, not long strings. Use a PChar() cast instead of
Addr():
Windows.MessageBox(0, PChar(msgPtr.msg), 'Populate Main', MB_OK);
> Also I am a bit surprised by the syntax string[128]
That creates a short string that has a maximum of 128 characters in it. It
takes up a fixed 129 bytes of memory - 1 byte for the length and 128 bytes
for the characters..
> I thought I should use char[128], but that's probably C++/Java syntax.
You can do that, but you would have to manually ensure that a null character
exists in the array.
> I do hope string[128] produces a null terminated string like this then?
It does not guarantee that. However, the compiler knows how to convert a
short string to a null-terminated long string, so you can do this:
var
msg: string[128];
Windows.MessageBox(0, PChar(String(msg)), 'Populate Main', MB_OK);
Gambit
> Windows.MessageBox(0, PChar(String(msg)), 'Populate Main', MB_OK);
Hi guys,
Actually there is no reason why msg should no be a string itself, or you
should say otherwise. I copied the code for a large part from
http://www.delphibasics.co.uk/RTL.asp?Name=ThreadVar
If I change msg its type from 'string[128]' to 'string', and call:
Windows.MessageBox(0, PChar(msgPtr.msg), 'Populate Main',
MB_SETFOREGROUND );
It seems to work. (Although that could be my 'famous last words')
And thanks for the help with these Delphi questions. I am a quite
experienced programmer, and I did C++, Java, C#, and my manager thinks that
I then can do a little Delphi too if there is no-one else for the job. Which
is of course correct, in principle, but you always run into the little
detials you do not know that cost a lot of time.
:-)