External exe: Run hidden using gtwvt

426 views
Skip to first unread message

Yakano

unread,
Apr 3, 2023, 1:03:05 PM4/3/23
to Harbour Users
Hi guys

I'm curious to know how you can run an application from Harbor without showing anything on the screen.

In my case, the application is 7za.exe, which I use to zip and unzip (yes, I already saw that I could do it with hb_zip* libraries, but it would take me a long time to manage my use of -...@exclude.lst and ... If something works, you better not touch).

Well, when I make the call from the pure console, nothing shows up, but I'm migrating some harbour executables using -gtwvt and then it does show up.

I have tried to use NIRCMD (https://www.nirsoft.net/utils/nircmd2.html#using), which manages to hide the execution of 7za, and it works, but I can't get errorlevel from 7za, but from nircmd.exe and I can't identify if there was some error message during extraction.

After all, the question is simple:

I can run exe minimized, but I can also hide it using pure harbour ???

Best greetings

Auge & Ohr

unread,
Apr 3, 2023, 5:04:52 PM4/3/23
to Harbour Users
hi,

you can use ShellExecute() and SW_HIDE

#define SW_HIDE             0
#define SE_ERR_NOASSOC      31

FUNCTION SHELLOPENFILE( cPath, cFILE, cPara, cHome )
LOCAL lSuccess   := 0
LOCAL Retvar     := .F.

   DEFAULT cPara TO ""
   DEFAULT cHome TO CURDIR()

   lSuccess := ShellExecute( 0, ;
                             "open", ;
                             cPath + cFILE, ;
                             cPara, ;
                             cHome, ;
                             SW_HIDE )
   DO CASE
      CASE lSuccess > 32                                              // Aufruf erfolgreich
         Retvar := .T.
      CASE lSuccess = SE_ERR_NOASSOC                 // Keine verknü pfte Anwendung

   OTHERWISE
      // ShellExecute war erfolglos.
      // Boolean-Standardwert False zur ückgeben
      Retvar := .F.
   ENDCASE

RETURN Retvar

Jimmy

theos theos

unread,
Apr 3, 2023, 8:18:57 PM4/3/23
to Harbour Users

   mira  el tópico de el manejo de las funciónes
                  hb_processOpen()   ó
                  hb_processRun()


en el ejemplo de manejo del ChatGTPAll con Harbour

Yakano

unread,
Apr 4, 2023, 8:05:24 AM4/4/23
to Harbour Users
Hello Jimmy

At the beginning of "my" migration from Clipper to Harbor, I found a Blinker.prg file to hold calls made from Clipper (yes, lazy).

The SwpRunCmd function that you were running from Clipper now looks like this:

/**/
FUNCTION SwpRunCmd( cCommand, nMem, cRunPath, cTempPath )
   HB_SYMBOL_UNUSED( nMem )
   HB_SYMBOL_UNUSED( cRunPath )
   HB_SYMBOL_UNUSED( cTempPath )
    IF Empty( cCommand )
      #if defined( __PLATFORM__UNIX )
         cCommand := GetEnv( "SHELL" )
      #else
         cCommand := GetEnv( "COMSPEC" )
      #endif
   ENDIF
    t_nErrorLevel := hb_run( cCommand )
 RETURN ( t_nErrorLevel != -1 )
/**/

In this function, which is still used in my older code, the same value that hb_Run() collects is returned, that is, the ErrorLevel() returned by the executed application.

This value is necessary to control if there has been an error in 7za and the reason. Can this be done from ShellExecute() ? Maybe with GetEnv()

Another question : When you say ShellExecute, I think you mean wapi_ShellExecute(). It's right ? I have only used this function to open files with the associated application in Windows (Start command?) but never for .EXE.

Thanks to Petewg for his contribution, where he reports that there is another wapi_ShellExecute_Wait(), which can be very interesting to wait for the launched .EXE to finish (I leave the link in case it helps someone else)

I will try and report...

Best regards

Yakano

unread,
Apr 4, 2023, 8:06:08 AM4/4/23
to Harbour Users
Le echaré un vistazo.
Gracias.

Bernard Mouille

unread,
Apr 4, 2023, 9:17:10 AM4/4/23
to Harbour Users
With Windows, I use this function :

Compile with -lhbwin

/* bh_RunWithReturn.ch Lancer une application non visible, arrêtant Harbour et avec retour. Erreur possible : - Le programme s'arrête. Cela vient que bh_RunWithReturn() ne gère que des fichiers texte. Dernière modification : 2022-10-26 */ #ifndef _bh_RunWithReturn_ch_ #define _bh_RunWithReturn_ch_ function bh_RunWithReturn( cCom, cCodepage ) local cReturn := "" if cCodepage == nil cCodepage := "Not" endif with object win_oleCreateObject( "WScript.Shell" ):Exec( cCom ) do while .not. :StdOut:AtEndOfStream cReturn += :StdOut:ReadLine() + hb_eol() enddo end with // Conversion des caractères si besoin. do case case cCodepage == "OemToAnsi" cReturn := hb_OemToAnsi( cReturn ) case cCodepage == "StrToUtf8" cReturn := hb_StrToUtf8( cReturn ) case cCodepage == "Utf8ToStr" cReturn := hb_Utf8ToStr( cReturn ) endcase return cReturn #endif // _bh_RunWithReturn_ch_

Auge & Ohr

unread,
Apr 4, 2023, 1:05:53 PM4/4/23
to Harbour Users
hi,

ShellExecuteis the "Original" Windows API Function
wapi_ShellExecute() is a Wrapper in HbWIN LIB and is the same

---

if ShellExecute() fail you got these possible Message

#define SE_ERR_FNF                     2  // File not found.
#define SE_ERR_PNF                     3  // Path not found.
#define SE_ERR_ACCESSDENIED            5  // Access denied.
#define SE_ERR_OOM                     8  // Out of memory.
#define SE_ERR_SHARE                   26 // Cannot share an open file.
#define SE_ERR_ASSOCINCOMPLETE         27 // File association information not complete.
#define SE_ERR_DDETIMEOUT              28 // DDE operation timed out.
#define SE_ERR_DDEFAIL                 29 // DDE operation failed.
#define SE_ERR_DDEBUSY                 30 // DDE operation is busy.
#define SE_ERR_NOASSOC                 31 // File association not available.
#define SE_ERR_DLLNOTFOUND             32 // Dynamic-link library not found.

      OTHERWISE
         // ShellExecute war erfolglos.
         // Boolean-Standardwert False zur ckgeben
         DO CASE
            CASE lSuccess = SE_ERR_FNF
               MsgInfo( "File not found.", cPath + cFILE )
            CASE lSuccess = SE_ERR_PNF
               MsgInfo( "Path not found.", cPath + cFILE )
            CASE lSuccess = SE_ERR_ACCESSDENIED
               MsgInfo( "Access denied !", cPath + cFILE )
            CASE lSuccess = SE_ERR_OOM
               MsgInfo( "Out of memory !", cPath + cFILE )
            CASE lSuccess = SE_ERR_SHARE
               MsgInfo( "Cannot share an open file.", cPath + cFILE )
            CASE lSuccess = SE_ERR_ASSOCINCOMPLETE
               MsgInfo( "File association information not complete.", cPath + cFILE )
            CASE lSuccess = SE_ERR_DDETIMEOUT
               MsgInfo( "DDE operation timed out.", cPath + cFILE )
            CASE lSuccess = SE_ERR_DDEFAIL
               MsgInfo( "DDE operation failed.", cPath + cFILE )
            CASE lSuccess = SE_ERR_DDEBUSY
               MsgInfo( "DDE operation is busy.", cPath + cFILE )
         ENDCASE
         Retvar := .F.

---

wapi_ShellExecute_Wait(),  seems me the same as HB_FUNC( WAITRUN ) under HMG

---

there is a Function in HMG
c:\hmg.3.4.4\0\HMG_MYSYNC\HMG_MySync.prg

Procedure ZipWith7Zip(cZip,cFolder)
   Local obj,cFiles,c7zip
   *
   c7zip := '"'+GetProgramFilesFolder()+'\7-zip\7z.exe'+'"'
   *
   If !File(c7zip)
      c7zip := strtran(c7zip,' (x86)')
   Endif
   *
   If !File(c7zip)
      *
      cFiles := Scandir(cFolder,{'.ZIP'},'C')
      *
      WAIT WINDOW '..........Compressing With 7Zip............' NOWAIT
      Memowrit('Zip.Bat',c7zip+' a -r0 -tzip '+cZip+' '+cFiles)
      obj := CreateObject("WScript.Shell.1")
      obj:Run('ZIP.Bat',0,.T.)
      WAIT CLEAR
      Ferase('ZIP.BAT')
   Else
      MsgInfo('Error! 7Zip.exe Non Found!!')
   Endif
   *
Return

Jimmy

Yakano

unread,
Apr 4, 2023, 2:02:59 PM4/4/23
to Harbour Users
Hello Bernard

Thanks for your code, it works perfectly, but it also doesn't let me know the value of ErrorLevel() returned by 7za.exe (something I can do if I use hb_Run() or the SwpRunCmd() wrapper).

I have an opportunity using the return value of your function, which would be the same as what I would get by redirecting the output of the executed command to a file.log [7za.exe t file.zip > file.log].

Well, for that I would have to use the -bso0 -bsp0 -bse1 parameters of 7za to limit the messages to only errors, but I still keep losing the information that 7za supplies via ErrorLevel [https://sourceforge.net/p/ sevenzip/discussion/45797/thread/c374fd35/]

Any idea how to get it back?

How does hb_Run() do it?

Best regards

Yakano

unread,
Apr 4, 2023, 2:04:18 PM4/4/23
to Harbour Users
Hello Jimmy

All #defines target the arguments of wapi_ShellExecute() and that's fine, but I need to access the ErrorLevel() returned by 7za.exe and I can't. 7za.exe (not 7z.exe) is portable and works only in console mode, for me it's perfect, because my executables are mostly console.

https://sourceforge.net/p/sevenzip/discussion/45797/thread/c374fd35/


Any idea how to get it back?

How does hb_Run() do it?

Best regards

Auge & Ohr

unread,
Apr 4, 2023, 3:50:44 PM4/4/23
to Harbour Users
hi,

to use "old" DOS Style with Error Level i recommend to use a *.BAT

C:\TEMP\MyAPP.EXE %1 %2
IF ERRORLEVEL 0 GOTO RAUSHIER
IF ERRORLEVEL 1 GOTO RAUS1
IF ERRORLEVEL 2 GOTO RAUS2
IF ERRORLEVEL 3 GOTO RAUS3
IF ERRORLEVEL 4 GOTO RAUS4
IF ERRORLEVEL 5 GOTO RAUS5
IF ERRORLEVEL 6 GOTO RAUS6
IF ERRORLEVEL 7 GOTO RAUS7
IF ERRORLEVEL 8 GOTO RAUS8
GOTO RAUSHIER

:RAUS1
ECHO  Error 1 >> Error.TXT
GOTO RAUSHIER
...
:RAUS8
ECHO  Error 8 >> Error.TXT
GOTO RAUSHIER
.
:RAUSHIER

Jimmy

Yakano

unread,
Apr 5, 2023, 12:15:57 AM4/5/23
to Harbour Users
Hi Jimmy

Currently from console I can control ErrorLevel without having to use .BAT , using this function...

FUNCTION  ExtractZip ()
Local cCommand, nError, cMessage, cReturn
   cCommand := "7ZA.exe x -y -bso0 -bsp0 -bse1 -...@EXCLUDE.LST MYFILE.ZIP -oX:\"+" > 7ZAINFO.LOG"
   if SwpRunCmd(cCommand) == .f.
      Alert('SwpRunCmd: Error Executing 7ZA for Extracting Data',{'Abort'})
      cMessage := SwpRunCmd (.f.) Error Executing 7ZA for Extracting Data'
   else
      nError := SwpErrLev()
      do case
      case nError == 0 .or. nError = 1
         cMessage := '' // No error or simple warnig
      case nError == 2
         cMessage := '7ZA (2) A fatal error occurred'
      case nError == 3
         cMessage := '7ZA (3) A CRC error occurred when unpacking'
      case nError == 4
         cMessage := '7ZA (4) Attempt to modify an archive previously locked'
      case nError == 5
         cMessage := '7ZA (5) Write to disk error'
      case nError == 6
         cMessage := '7ZA (6) Open file error'
      case nError == 7
         cMessage := '7ZA (7) Command line option error'
      case nError == 8
         cMessage := '7ZA (8) Not enough memory for operation'
      case nError == 9
         cMessage := '7ZA (9) Create file error'
      case nError == 255
         cMessage := '7ZA (255) User stopped the process'
      otherwise
         cMessage := '7ZA ('+AllTrim(Str(nError))+') Unknown Error'
      endcase
   endif
return(cMessage)

After the call, you can optionally display more details by displaying 7ZAINFO.LOG, but usually the first message is enough.

From the console I don't have any problem, since the execution of 7ZA is transparent to the user (nothing is shown on the screen).

I'm trying to advance the look of the application and have started with -gtwvt on some EXEs, thinking I might use GUI at some point (I don't really know when) so I'm looking into various possibilities.

In any case, I think that going back to using .BAT would be a step backwards, since it will be even more difficult to hide the call from other executables.

In that sense, I think Bernard has pointed in a more viable future direction by using -gtwvt or even -gtwvg.

By the way, what GUI flavor would you recommend to migrate from console?

(I have no experience coding GUI)

Best regards

Bernard Mouille

unread,
Apr 5, 2023, 1:01:53 AM4/5/23
to Harbour Users
Hello Yakano,
Only errorlevel in the code.
Regards,
Bernard.

// Test.prg
// Compile with -lhbwin.

procedure Main
   // ANSI ( Windows ) en français.
   request HB_LANG_FR
   request HB_CODEPAGE_FRWIN
   hb_cdpSelect( 'FRWIN' )
   hb_langSelect( 'FR' )
   // Taille de la console.
   setmode( 43, 80 )
   setcolor( "GR+/B" )
   @ 0, 0, maxrow(), maxcol() box space( 9 )

   ? "ERRORLEVEL returned by 7Zip : "
   ?? bh_Run( 'cmd /K "C:\Program Files\7-Zip\7z.exe" &exit', 0, .T. )
//   ?? bh_Run( 'cmd /K ' + hb_dirbase() + 'e\Test.exe &exit' )
   ?
   wait
   return

procedure bh_Run( cCommand, nDisplay, lStop )
   if lStop == nil
      lStop := .T.
   endif
   if nDisplay == nil
      nDisplay := 1
   endif
   win_oleCreateObject( "WScript.Shell" ):Run( cCommand, nDisplay, lStop )
   return

Yakano

unread,
Apr 5, 2023, 2:06:11 AM4/5/23
to Harbour Users
Hi Bernard

Sorry, but this code doesn't work for me.

I just change the executable command...

   ?? bh_Run( 'cmd /K "7ZA.exe t -y -bso0 -bsp0 -bse1 KK.zip" &exit', 0, .T. )

The teste.dbf (in KK.ZIP) file is open with dbu /E (exclusive) and cannot be overwritten, so 7ZA returns ErrorLevel =2 (Fatal Error), but ErrorLevel=0 (everything Ok) is displayed.

Best regards

Auge & Ohr

unread,
Apr 5, 2023, 5:39:33 AM4/5/23
to Harbour Users
hi,

> By the way, what GUI flavor would you recommend to migrate from console?


it is a "out-of-the-box" packet which have Compiler / Linker (MinGW), IDE, Debugger, 2-Way Form Designer and over 400 Sample

Jimmy

Yakano

unread,
Apr 5, 2023, 1:01:22 PM4/5/23
to Harbour Users
Hello

I don't know why (perhaps under the effects of caffeine) I thought that Bernard's 1st solution was hiding the execution of 7ZA.exe using -gtwvt, but it wasn't.

I'm back to Jimmy's first proposal, but with the latest CMD insight provided by Bernard.

Ultimately and finally, I have opted for this system...

wapi_ShellExecute_Wait(nil, 'Open', "CMD.exe", "/C 7ZA.exe x -y -bso0 -bsp0 -bse1 KK.zip > KK.log", nil, 0)

It's not perfect for all executables, because Errorlevel is missing, but works (hide and log).

Hopefully one day hb_Run() will include an option to run hidden like this... hb_Run(<cCommand>, [<lHide>]) -> <nErrorLevel>.

In the case of 7ZA.exe the switches ( -bso0 -bsp0 -bse1 ) allow you to control what information is received.

In the example only errors are shown, which allows you to analyze the KK.log file with the same tools you used before.

The problem will come with other EXEs than with which it is necessary to use ErrorLevel.

Well... "Next level"

Thank you so much guys !!!

Itamar Lins

unread,
Apr 5, 2023, 2:56:20 PM4/5/23
to Harbour Users
Hi!

Function main()
Local nResult := 0
Local cCommand := "tasklist"
Local cStdOut
Local cStdErr

nResult := hb_processRun( cCommand, , @cStdOut, @cStdErr, .t. )

? nResult
? cStdOut
? cStdErr
Return



itamar@itamar-desktop:~$ hbmk2 -find hb_process
Núcleo Harbour (instalado):
  hb_processClose()
  hb_processOpen()
  hb_processRun()
  hb_processValue()


Best regards,
Itamar M. Lins Jr.

Yakano

unread,
Apr 8, 2023, 2:36:13 PM4/8/23
to Harbour Users
Hi Itamar

Great! Really it was so simple? Thank you so much !!!

I read the topic that theos theos recommended, but I didn't understand that the lDetach parameter could achieve it.

Best regards
Reply all
Reply to author
Forward
0 new messages