2

I'm using Delphi 7 and can't predict the target version of Windows.

I need to create a database (probably MySql, but might be something else) and define some table structures. I do not need to populate any data. Unfortunately, all of the ADO components seem to expect that a database already exists and they will then allow you to manipulate it.

So, since it's only a few simple commands, I thought that I might as well use ShellExectute().

Agree? Disagree?

Can anyone give me a sample code which will attempt to run "MySql --version" and let me check the result? After that I should be able to figure it out for myself. Thanks.


[edit]

No offence intended, but I do know how to google. It's just that I don't find useful results. It's my own fault for not being explicit in this question, so please accept my apology - what I need is a code example, not just the name of a component.

Sorry (and thanks for the replies so far (all of which are +1))


[edit]

The links which Robert gives do the job (procedure RunDosInMemo() does the trick) ... B U T you Must remember to include an .exe extension (so, 'notepad.exe', not just 'notepad', and to ge a full path if your command is not on the path.

2
  • 1
    Are you asking for advice on how to create a database from scratch, or are you asking how to run a command and capture its output? (If your answer is both, then you should have asked two separate questions.) Commented May 25, 2010 at 13:42
  • Rob, I will take either. Bottom line is that I need to create database and some tables, but not populate any data. Several folks in diufferent questions have tried to help mby making mention of firebird or ado, but those folks do not give details of how to programatically create the database with those tools - and I can't see how to. So, since I know the command line to create a database and some table I thought that I might execute a shell command instead. Bottom line, I don't care how it gets down, but can someone show me how to create a MySql database with a single table from Delphi? Commented May 26, 2010 at 2:33

6 Answers 6

6

Here is an article that explains it detail

Capture the output from a DOS (command/console) Window

But in short you need to Create two pipes to read and write the output. Then you need to set StdInput and StdOutput in the TStartUpInfo structure, then pass this structure to the CreateProcess() call.

Here is another article that shows how to Wait for the process to finish.

Sign up to request clarification or add additional context in comments.

5 Comments

hmmm, delphi.about.com/cs/adptips2001/a/bltip0201_2.htm looked very good, but when I run it in Windows 7 then CreateProcess() fails.
Aaaaaaaaaaargh!!!! You can't just give a command name, like "notepad"!!!! You have to give "Notepad.exe" !!!!
No, you can just type "notepad", but you need to skip application name for that, use only command line. If you use appname - then you must specify "notepad.exe" (there still can be just "notepad" in command line though). Quite obvious. If you can't do it - then something is wrong with your enviroment. This works even in x64 Win7.
"if not CreateProcess(...) then RaiseLastOSError;" or "if CreateProcess(...) then begin ... end else RaiseLastOSError;" should give you enough information to diagnose the problem. Remember: 90% of FAQ-code are crap (it's not bad, as they are just examples, but everybody copy&paste them). And this 90% includes code from above links too. Your homework is to figure why. If you want quality code - then write it yourself or use code from well-known widely-tested libraries (JCL for example). May be these libraries aren't perfect, but they are a lot better than average "advices" in the internet.
The sample code for RunDosInMemo does not work in XE2. I get an Access Violation on the CreateProcess. Most likely due to PWideChar/PAnsiChar differences between the old Delphis and the new ones that support Unicode.
5

Use DSiExecuteAndCapture from DSiWin32:

var
  exitCode: integer;
  output  : TStringList;
begin
  output := TStringList.Create;
  try
    if DSiExecuteAndCapture('mysql --version', output, '', exitCode) = 0 then
      Log(Format('error %d, cannot start', [GetLastError])) 
    else begin
      // check exitCode and output
    end;
  finally FreeAndNil(output); end;
end;

2 Comments

This looks very good, so plus 1, but I got a bit bogged down trying to install it (not much documentation). I will come back to it if the other stuff does not work, but in general I would prefer not to use 3rd party stuff.
There's nothing to install, just add DSiWin32 to the 'uses' clause. Alternatively, copy&paste DSiExecuteAndCapture to your code.
3

I wonder why everybody who wants launch a new process tends to use ShellExecute instead, which is designed to open file in associated application?

You want new process? Then use CreateProcess. End of story.

CreateProcess allows you to wait for process, to get its exit code, to read its console output and many more.

4 Comments

Everyone uses ShellExecute because it's a lot easier: no security settings to think about, no separate structure to fill before you call it, no process and thread handles to clean up afterward. People would use WinExec if they could.
> any specific code examples? Have you tried search? Search "console" in Delphi tag. Here: stackoverflow.com/questions/1212176/… stackoverflow.com/questions/2015388/… stackoverflow.com/questions/2856772/… And a lot of other questions...
> Everyone uses ShellExecute because it's a lot easier Yeah, I know that. It was rhetorical question ;)
"Easier" does not necessarily mean "better" though. ShellExecute() uses CreateProcess() internally. If you know the exact .exe/command to run, it is less overhead to call CreateProcess() directly then to have the OS invoke a lot of overhead trying to figure out what to do.
3

What you are looking for, probably, is an embedded database. Some options:

  • Firebird Embedded
  • MySQL Embedded
  • SQLite

With either of databases and with appropriate data access components (not sure about ADO support for embedded FB and MySQL), you will be able to:

  • attach without a DB
  • create a DB
  • then create the tables

6 Comments

sorry, and absolutely no offence intended, but I already asked another question and got a similar answer. I ought to make it clear that I need a code example - can you help, please? Sorry for not stating thha more explicitly, I will edit the question.
At least you have to explicitly say - which DBMS you are going to use. Also, are you considering ADO only ?
What's not explicit enough about "Can anyone give me a sample code which will attempt to run "MySql --version" and let me check the result?"
@mghie: "probably MySql, but might be something else"
|
2

I saved this from a newsgroup post a long time ago; I don't know who originall wrote it, though.

This code allows you to run a DOS application and capture it's output in a TMemo. You can then pull what you need out of the memo with a little trial and error parsing the lines.

procedure TFMainForm.RunDosInMemo(const DosApp: String; AMemo: TRichEdit);
const
  ReadBuffer = 2400;
var
  Security : TSecurityAttributes;
  StdInPipeR, StdInPipeW : THandle;
  StdOutPipeR, StdOutPipeW : THandle;
  StartInfo : TStartUpInfo;
  ProcessInfo : TProcessInformation;
  Buffer : PByte;
  BytesAvailable, BytesRead : DWord;
  sDosApp: String;
  sData: RawByteString;
begin
  sDosApp := DosApp;
  UniqueString(sDosApp);

  with Security do begin
    nLength := SizeOf(TSecurityAttributes);
    bInheritHandle := True;
    lpSecurityDescriptor := nil;
  end;

  if CreatePipe(StdInPipeR, StdInPipeW, @Security, 0) then
  try

    SetHandleInformation(StdInPipeW, HANDLE_FLAG_INHERIT, 0);
    if CreatePipe(StdOutPipeR, StdOutPipeW, @Security, 0) then
    try
      SetHandleInformation(StdOutPipeR, HANDLE_FLAG_INHERIT, 0);
      GetMem(Buffer, ReadBuffer);
      try
        ZeroMemory(@StartInfo, SizeOf(StartInfo));
        StartInfo.cb := SizeOf(StartInfo);
        StartInfo.hStdOutput := StdOutPipeW;
        StartInfo.hStdInput := StdInPipeR;
        StartInfo.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
        StartInfo.wShowWindow := SW_HIDE;

        if CreateProcess(nil, 
                         PChar(sDosApp), 
                         nil, 
                         nil, 
                         True, 
                         NORMAL_PRIORITY_CLASS, 
                         nil, 
                         nil, 
                         StartInfo, 
                         ProcessInfo) then
          try
            while WaitForSingleObject(ProcessInfo.hProcess, 500) <> WAIT_TIMEOUT do
              Application.ProcessMessages;
            while PeekNamedPipe(StdOutPipeR, nil, 0, nil, BytesAvailable, nil) do 
            begin
              if BytesAvailable < 1 then 
                Break;
              if BytesAvailable > ReadBuffer then 
                BytesAvailable := ReadBuffer;
              if not ReadFile(StdOutPipeR, 
                              Buffer[0], 
                              BytesAvailable, 
                              BytesRead, 
                              nil) then 
                Break;
              SetString(sData, PAnsiChar(Buffer), BytesRead);
              // assign an appropriate codepage for the output data:
              // 0 for default Ansi, 1252 or 20157 for ASCII, 1200 for 
              // Unicode, etc...
              SetCodePage(sData, ...);
              // this is faster and more efficient than reading/writing the 
              // Text property directly...
              AMemo.SelStart := AMemo.GetTextLen;
              AMemo.SelLength := 0;
              AMemo.SelText := sData;
            end;
          finally
            CloseHandle(ProcessInfo.hThread);
            CloseHandle(ProcessInfo.hProcess);
          end;
      finally
        FreeMem(Buffer);
      end;
    finally
      CloseHandle(StdOutPipeR);
      CloseHandle(StdOutPipeW);
    end;
  finally
    CloseHandle(StdInPipeR);
    CloseHandle(StdInPipeW);
  end;
end;

4 Comments

A similar technique as the About.com code Robert's answer links to. Both are susceptible to the same general problem. If the receiving program doesn't read from the pipe, then when the pipe's buffer fills, the sending program will block on its next WriteFile call until there's enough room for another write. But if the receiving program is waiting for the sender to terminate before reading anything, then you have deadlock.
+1 for posting. Thanks. It doesn't help me as RawByteString appeared in D2009 and I have D7.
Just add "{$IFNDEF UNICODE}type RawByteString = AnsiString;{$ENDIF}" somewhere and you're done. All Strings are RawByteStrings in D7.
@mawg: I apologize. I should have thought to mention the changes for D7 and didn't. @Alexander: Thanks for stepping up and doing so. :-)
1

I wrote a unit and set of components to handle console redirection in delphi quite a long time ago:

http://www.fulgan.com/delphi/dospipes15.zip

But beyond that, you shouldn't have to go through that: use the ADO connection component just to connect to the database's default catalog and then use the "execute" method to create whatever database and schema you need. The database SERVER need to be properly installed and running for this to work but, at least with MSSQL, there is no problem with creating new databases this way. the exact connection string to use might change depending on the target DB, though.

1 Comment

Btw, I posted a question asking for ADO coding help at stackoverflow.com/questions/2918016/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.