Jump to content
emendelson

Can a compiled script detect if it was made for CUI or GUI?

Recommended Posts

emendelson

I am working on a script that will perform differently if it has been compiled for CUI or for GUI. I would like to use the same script for both versions, and simply compile it twice, once for CUI, once for GUI.

Is it possible for a compiled script to detect whether it has been compiled for the CUI or GUI? Right now I have set the script to detect its own name (whether or not "CUI" is in the filename) and use its CUI-only behavior if it finds "CUI" in its name, but of course this method isn't foolproof. Is there any better method?

Thanks for any help.

Share this post


Link to post
Share on other sites
Beege

You could define a variable in the scripts that are CUI. Then check if that variable exists using IsDeclared() function.

Edited by Beege

Share this post


Link to post
Share on other sites
kylomas

emendleson,

Curious as to why this matters?

kylomas


Forum Rules         Procedure for posting code

"I like pigs.  Dogs look up to us.  Cats look down on us.  Pigs treat us as equals."

- Sir Winston Churchill

Share this post


Link to post
Share on other sites
GerrOrneq

@emendelson

The way I see it you have a couple of options:

a. You could set an appropriate Environment variable in the AutoExec and check that from you script (Look up EnvSet, EnvGet, EnvUpdate in the help file.)

b. Run you app whilst specifying a commandline parameter and have you app check for the parameter and take action accordingly. e.g. MyApp.exe /CUI or MyApp.exe /GUI.

(see $CmdLine and $CmdLineRaw in the help file.)

Regards

DeMo.


Quote of the week:"BASIC programmers never die, they GOSUB and don't RETURN." -- UnknownWisdom of the ages:

  

  • I'd be unstoppable... if not for law enforcement and physics.
  • Marriage, the number 1 cause of divorce.
  • Don't steal... the government hates competition.
  • Irish Government Motto: We’ve got what it takes to take what you’ve got.
  • Birthdays are good for you. Statistics show that the people who have the most live the longest.
  • Failure is not an option. It comes bundled with your Microsoft product.-- Ferenc Mantfeld
  • If you learn from your mistakes, then why ain't I a genius?! -- Anonymous
  • Remember, live every day as if it was your last day! one day you will be right.
  • How is it one careless match can start a forest fire, but it takes a whole box to start a campfire?
  • Sure my system is secure, it just locked up again.
  • I haven't lost my mind; I have a tape back-up somewhere.  ~Author Unknown

Share this post


Link to post
Share on other sites
emendelson

The way I see it you have a couple of options:

a. You could set an appropriate Environment variable in the AutoExec and check that from you script (Look up EnvSet, EnvGet, EnvUpdate in the help file.)

b. Run you app whilst specifying a commandline parameter and have you app check for the parameter and take action accordingly. e.g. MyApp.exe /CUI or MyApp.exe /GUI.

(see $CmdLine and $CmdLineRaw in the help file.)

Thank you - the second one (command-line switch) is what I'm using now.

As for the reason for doing this: The program (which converts a PCL printfile into PDF format and then prints the PDF) will be used in two different ways - as a program that can be run by a user (or a batch file launched by a user), or as a program that is launched by a Windows service that is owned by the "system", not by an individual user. A program run from a service that is owned by the system cannot interact with any user, so it can't display message boxes and wait for an OK, and it can't use any GUI elements at all, or it will never shut down. So I want the CUI version to write error messages to a log file, while the GUI version will display error messages in message boxes for user interaction.

Edited by emendelson

Share this post


Link to post
Share on other sites
Valik

Even if you compile as a CUI AutoIt will still create an internal window.

Share this post


Link to post
Share on other sites
UEZ

As far as I know the value of the PE header at position 0x0154 will become 0x03 if it is a CUI, otherwise 0x02.

Read out the value at the position and you should know whether it is a CUI compiled exe.

Br,

UEZ

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites
emendelson

Even if you compile as a CUI AutoIt will still create an internal window.

The internal window is perfectly all right for the program that is launched by a service, as long as it doesn't require the user to interact with it, and as long as it closes when the macro exits - which it seems to do.

Share this post


Link to post
Share on other sites
emendelson

As far as I know the value of the PE header at position 0x0154 will become 0x03 if it is a CUI, otherwise 0x02.

Read out the value at the position and you should know whether it is a CUI compiled exe.

That could be very useful. Can an AutoIt exe read its own file while that file is in use because it the program is running? (I know the answer is that I should try it and see, and will do so as soon as I get back to my development machine. Thank you for this - it sounds like the only foolproof method.

Share this post


Link to post
Share on other sites
Valik

The internal window is perfectly all right for the program that is launched by a service, as long as it doesn't require the user to interact with it, and as long as it closes when the macro exits - which it seems to do.

You seem to be failing to see the forest for the trees or you have failed to provide a crucial piece of information. Why do you compile the script as CUI? Why not compile it as GUI and write it correctly so that a single flag is checked before any interactive GUI's are shown? Services can be invoked with command line parameters so it is trivial to setup the service invocation with a /service flag which is processed and sets an internal flag which causes all GUI code to be skipped. No reading the PE header. No separate compilation for special circumstances, et cetera.
  • Like 1

Share this post


Link to post
Share on other sites
emendelson

You seem to be failing to see the forest for the trees or you have failed to provide a crucial piece of information. Why do you compile the script as CUI? Why not compile it as GUI and write it correctly so that a single flag is checked before any interactive GUI's are shown? Services can be invoked with command line parameters so it is trivial to setup the service invocation with a /service flag which is processed and sets an internal flag which causes all GUI code to be skipped. No reading the PE header. No separate compilation for special circumstances, et cetera.

Ah - thank you. I have now done exactly what you suggest, and it works perfectly. Thank you again.

Share this post


Link to post
Share on other sites
ljcorsa

I liked UEZ's reply for my purpose. I coded up a function "isCompiledGUI.au3" which returns True/False based on the value at position 0x154 of the executable.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Func isCompiledGUI()
;  Detect if running in a compiled GUI
;  returns True and sets @error = 0 if it completely succeeded
;  returns False  and sets @error = 1 if not (@extended gives more insight to a dev)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;  Based on a post "Can a compiled script detect if it was made for CUI or GUI?"
; in the AutoIt Forums:
;  UEZ, on 29 December 2011 - 09:29 AM, said:
;  As far as I know the value of the PE header at position 0x0154 will become 0x03 if it is a CUI, otherwise 0x02.
;  Read out the value at the position and you should know whether it is a CUI compiled exe.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#include-once
#include <Constants.au3>
Func isCompiledGUI()
; use (experimental) Static Local vars to allow quicker response on subsequent calls
Static Local $beenrun = False
Static Local $result = False
SetError(0, 1)
If $beenrun Then Return $result
; Check:
; - are we running v3 of AutoIt?  if not, the magic number 0x154 may be wrong
; - if not compiled, don't care
; - if running within AutoIt, bail
; - if can't open, read, or fileseek the executable file in which I'm running, fail
;  - if the value at the magic position is 0x2, succeed!
Local Const $magicPosition = 0x154
Local Const $magicValue = 0x2
If 3 = Floor(@AutoItVersion) Then
  If @Compiled Then
   If 0 = StringInStr(@AutoItExe, "autoit.exe", 0) Then
    Local $exeFileHandle = FileOpen(@AutoItExe, 16)
    If (-1) <> $exeFileHandle Then
     If FileSetPos($exeFileHandle, $magicPosition, $FILE_BEGIN) Then
      Local $byte = FileRead($exeFileHandle, 1)
      If 0 = @error Then
       If $magicValue = $byte Then
        SetError(0, 0)
        $result = True
       Else
        SetError(1, 7)
       EndIf
      Else
       SetError(1, 6)
      EndIf
     Else
      SetError(1, 5)
     EndIf
    Else
     SetError(1, 4)
    EndIf
   Else
    SetError(1, 3)
   EndIf
  Else
   SetError(1, 2)
  EndIf
Else
  SetError(1, 1)
EndIf
$beenrun = True
Return $result
EndFunc   ;==>isCompiledGUI
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;  test code for function isCompiledGUI
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
If 0 < StringInStr(@ScriptFullPath, "\isCompiledGUI.") Then
Local $i
For $i = 1 To 2
  Local $isgui = isCompiledGUI()
  Local $err = @error
  Local $ext = @extended
  Local $msg = @ScriptName
  If $isgui Then
   $msg &= " returned TRUE."
   If $ext > 0 Then $msg &= " (not re-evaluated)"
   SplashTextOn(@ScriptName, $msg)
   Sleep(2000)
  Else
   $msg &= " returned False. "
   If $err > 0 Then
    $msg &= "("
    Switch $ext
     Case 1
      $msg &= "only tested on AutoIT version 3"
     Case 2
      $msg &= "not compiled"
     Case 3
      $msg &= "running within AutoIt.EXE"
     Case 4
      $msg &= "FileOpen failed"
     Case 5
      $msg &= "FileSetPos failed"
     Case 6
      $msg &= "FileRead failed"
     Case 7
      $msg &= "CONSOLE (CUI) app"
     Case Else
      $msg &= "@extended = " & $ext
     EndSwitch
     $msg &= ")"
   EndIf
  EndIf
  ConsoleWrite($msg & @CRLF)
Next
EndIf
Edited by ljcorsa

Share this post


Link to post
Share on other sites
Simpel

Hi.

Sorry for necroposting but I exactly was looking for this. I tried it out and I put my compiled scripts in a hex editor. Value at position 0x154 is always 0x0. I think there will be a lot of changes since that AutoIt version. I guess the $magicPosition in version 3.3.14.2 is 0x16C. Can someone verifiy this?

Regards, Conrad


SciTE4AutoIt = 3.7.3.0   AutoIt = 3.3.14.2   AutoItX64 = 0   OS = Win7Pro SP1   OSArch = X64   Language = 0407/german
H:\...\AutoIt3\SciTE     H:\...\AutoIt3      H:\...\AutoIt3\Include     (H:\ = Network Drive)

   88x31.png  Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind.

Share this post


Link to post
Share on other sites
AspirinJunkie

Offset does not mean that it counts from the beginning of the file, but from the beginning of the PE header.
At offset 60 (0x3C) is a pointer where the PE header starts in the file. Follow this pointer and from there, the offset should be +92 (+5C) for x86 files. I have just tested this with a file and got the correct result 0x02.

Here you can research more about the structure of PE files: https://en.wikibooks.org/wiki/X86_Disassembly/Windows_Executable_Files

 

Share this post


Link to post
Share on other sites
Simpel

Very interesting I read your link. But in my scripts with v 3.3.14.2 PE header starts at offset 272 (0x110). When I follow this pointer +92 (+5C) then I exactly found the correct result 0x02 for GUI and 0x03 for CUI. Complete offset is 364 (0x16C) as I found out earlier. But why is the offset to PE header larger than it should?

0x16C.PNG

Conrad


SciTE4AutoIt = 3.7.3.0   AutoIt = 3.3.14.2   AutoItX64 = 0   OS = Win7Pro SP1   OSArch = X64   Language = 0407/german
H:\...\AutoIt3\SciTE     H:\...\AutoIt3      H:\...\AutoIt3\Include     (H:\ = Network Drive)

   88x31.png  Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind.

Share this post


Link to post
Share on other sites
AspirinJunkie
3 minutes ago, Simpel said:

But why is the offset to PE header larger than it should?

What do you mean with "should"? The offset depends on the the length of the dos-header. So there doesn't exist a fixed offset.

Share this post


Link to post
Share on other sites
Simpel

Ok. So I understood your post and the text following your link wrong: "At offset 60 (0x3C) from the beginning of the DOS header is a pointer to the Portable Executable (PE) File header." I thought DOS header is fixed.

Ok, so I have first to search the offset of PE header in my script and then add offset to cui/gui byte +92 (+5C). I'll will try it. Til now I didn't search inside a file in hex but I will find out.

Thanks, Conrad


SciTE4AutoIt = 3.7.3.0   AutoIt = 3.3.14.2   AutoItX64 = 0   OS = Win7Pro SP1   OSArch = X64   Language = 0407/german
H:\...\AutoIt3\SciTE     H:\...\AutoIt3      H:\...\AutoIt3\Include     (H:\ = Network Drive)

   88x31.png  Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind.

Share this post


Link to post
Share on other sites
jguinch

Maybe you can just use this :

#include <WinAPI.au3>

If _IsAutoItGUIMode() Then
    MsgBox(0, "", "GUI Mode")
Else
    ConsoleWrite("CUI Mode")
EndIf


Func _IsAutoItGUIMode()
    Local $iStdHandle = _WinAPI_GetStdHandle ( 2 )
    Return ($iStdHandle And @Compiled) ? 0 : 1
EndFunc

 

Share this post


Link to post
Share on other sites
Simpel

Got it. As it should work from shell:sendto I worked only with $CmdLine:

#include <Constants.au3>
If $CmdLine[0] < 1 Then Exit
Local $sScriptFullPath = $CmdLine[1]
Local $sCompileStyle = _isCUIorGUI($sScriptFullPath)
If @error Then
    MsgBox(0, 'CUI_or_GUI', "There is no valid result." & @CRLF & @CRLF & "Error: " & @error & @CRLF & "Extended: " & @extended)
Else
    MsgBox(0, 'CUI_or_GUI', $sScriptFullPath & @CRLF & "is compiled as: " & $sCompileStyle)
EndIf
Exit

Func _isCUIorGUI($sScriptFullPath) ; parts by ljcorsa
    Local $result = False
    SetError(0, 1)
    Local Const $magicValueGUI = 0x2
    Local Const $magicValueCUI = 0x3
    Local Const $iOffsetDecPEtoCuiGuiByte = 92
    Local $magicPosition
    If StringInStr($sScriptFullPath, ".exe") Then ; is compiled?
        Local $exeFileHandle = FileOpen($sScriptFullPath, 16)
        Local $HeaderSearch = FileRead($exeFileHandle)
        $HeaderSearch = StringTrimLeft($HeaderSearch, 2)
        Local $iOffsetDecPEHeader = (StringInStr($HeaderSearch, "50450000") - 1) / 2 ; 50450000 is HEX-Code for PE.. (. means NULL)
        Local $magicPositionDecimal = $iOffsetDecPEHeader + $iOffsetDecPEtoCuiGuiByte
        $magicPositionDecimal = Int($magicPositionDecimal)
        $magicPosition = "0x" & Hex($magicPositionDecimal)
        If (-1) <> $exeFileHandle Then ; - if can't open, read, or fileseek the executable file in which I'm running, fail
            If FileSetPos($exeFileHandle, $magicPosition, $FILE_BEGIN) Then
                Local $byte = FileRead($exeFileHandle, 1)
                If 0 = @error Then
                    If $magicValueGUI = $byte Then ; - if the value at the magic position is 0x2, succeed!
                        SetError(0, 0)
                        $result = "GUI"
                    ElseIf $magicValueCUI = $byte Then ; - if the value at the magic position is 0x3, succeed!
                        SetError(0, 0)
                        $result = "CUI"
                    Else
                        SetError(1, 5)
                    EndIf
                Else
                    SetError(1, 4)
                EndIf
            Else
                SetError(1, 3)
            EndIf
        Else
            SetError(1, 2)
        EndIf
    Else
        SetError(1, 1)
    EndIf
    Return $result
EndFunc

Conrad


SciTE4AutoIt = 3.7.3.0   AutoIt = 3.3.14.2   AutoItX64 = 0   OS = Win7Pro SP1   OSArch = X64   Language = 0407/german
H:\...\AutoIt3\SciTE     H:\...\AutoIt3      H:\...\AutoIt3\Include     (H:\ = Network Drive)

   88x31.png  Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×