Alternative for RunProgram

Just starting out? Need help? Post your questions and find answers here.
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Alternative for RunProgram

Post by blueznl »

Hi peeps. I've been gone for quite a while (doing lots of other stuff) but I need to build a little tool right now, and I'll (obviously) do so in PureBasic. I'm looking for something (and I know I've seen it somewhere on the forum, but I can't find it back).

What's the best way to launch / open certain apps / programs?

I want to simulate:

- start notepad -> should start (you guessed it) notepad.exe :-) without specifying the path, ie. let windows figure out the right app, potentially simply by launching something in such a way that the windows path is searched, but I don't thing RunProgram offers options for that

- launch the right application for a given file+extension, ie. word for .docx, excel for .xlsx etc., in other words, open the right application by only specifiying the file

Is there a way to do that easily?
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4955
Joined: Sun Apr 12, 2009 6:27 am

Re: Alternative for RunProgram

Post by RASHAD »

I think RunProgram can run any shell and no need to the path
Anyhow

Code: Select all

ShellExecute_()
ShellExecuteEx_()
Egypt my love
boddhi
Enthusiast
Enthusiast
Posts: 524
Joined: Mon Nov 15, 2010 9:53 pm

Re: Alternative for RunProgram

Post by boddhi »

Hello,
blueznl wrote: - launch the right application for a given file+extension, ie. word for .docx, excel for .xlsx etc., in other words, open the right application by only specifiying the file
If you use RunProgram() with filename and without appname, it will lauch the default system app:

Code: Select all

RunProgram(#PB_Compiler_Home+"Examples\Sources\Data\WebView\webview.html")
RunProgram(#PB_Compiler_Home+"Examples\Sources\Data\ToolBar\Readme.txt")
If my English syntax and lexicon are incorrect, please bear with Google translate and DeepL. They rarely agree with each other!
Except on this sentence...
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Re: Alternative for RunProgram

Post by blueznl »

Wait... I thought I tested that... Trying again. Moment...
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
Axolotl
Addict
Addict
Posts: 853
Joined: Wed Dec 31, 2008 3:36 pm

Re: Alternative for RunProgram

Post by Axolotl »

If you want to know the associated application and you are working under Windows, try this. (Reading a registry value)

Code: Select all

Procedure.s GetAssociatedProgram(Extension$)  ; returns executable or Empty$ 
  Protected hKey.l, dataSize.l, pos.l
  Protected result$, keyValue$, keyNext$, associatedProgram$ 

  hKey = 0 : dataSize = 255 : keyValue$ = Space(dataSize) : associatedProgram$ = ""  

  If Left(Extension$, 1) <> "." 
    Extension$ = "." + Extension$   ; extension == <.ext> 
  EndIf 

  If RegOpenKeyEx_(#HKEY_CLASSES_ROOT, Extension$, 0, #KEY_READ, @hKey)  = #ERROR_SUCCESS  
    If RegQueryValueEx_(hKey, "", 0, 0, @keyValue$, @datasize) = #ERROR_SUCCESS  
      keyNext$ = Left(keyValue$, dataSize - 1) 

      hKey = 0 : dataSize = 255 : keyValue$ = Space(dataSize) 
      If RegOpenKeyEx_(#HKEY_CLASSES_ROOT, keyNext$ + "\Shell\Open\Command", 0, #KEY_READ, @hKey) = #ERROR_SUCCESS  
        If RegQueryValueEx_(hKey, "", 0, 0, @keyValue$, @dataSize) = #ERROR_SUCCESS  
          associatedProgram$ = Left(keyValue$, dataSize - 1)   
        EndIf  
      EndIf  
    EndIf  
  EndIf                                             ; : Debug " -->" + associatedProgram$ + "<--" 

  ; remove the stored parameter (i.e. %1 or /Shell "%1" or etc. 
  ; 
  pos = FindString(LCase(associatedProgram$), ".exe", 1)  
  If pos <> 0  
    associatedProgram$ = Left(associatedProgram$, pos + 4)  ; keep the .exe
    associatedProgram$ = RemoveString(associatedProgram$, #DQUOTE$)  ; remove any "  
  EndIf  

  ProcedureReturn associatedProgram$  
EndProcedure  

;  test on some very well known extensions..... 
; Debug "doc => " + GetAssociatedProgram("doc")  
; Debug "xls => " + GetAssociatedProgram("xls")  
; Debug "mdb => " + GetAssociatedProgram("mdb")  
; Debug "jpg => " + GetAssociatedProgram("jpg")  
; Debug "gif => " + GetAssociatedProgram("gif")  
; Debug "htm => " + GetAssociatedProgram("htm")  
; Debug "done" 
That app is used if you call something like

Code: Select all

RunProgram("test.doc") 


On the other hand if you call apps like robocopy.exe or cmd.exe you don't need a path because of the environment variable %PATH%.
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
User avatar
chi
Addict
Addict
Posts: 1087
Joined: Sat May 05, 2007 5:31 pm
Location: Austria

Re: Alternative for RunProgram

Post by chi »

Hi blueznl, on Windows you could also use AssocQueryString as shown here
Et cetera is my worst enemy
AZJIO
Addict
Addict
Posts: 2192
Joined: Sun May 14, 2017 1:48 am

Re: Alternative for RunProgram

Post by AZJIO »

https://azjio.narod.ru/PureBasic/pb_use ... String.htm
Axolotl wrote: Fri Jul 26, 2024 10:57 am

Code: Select all

GetAssociatedProgram
This method does not take into account the HKEY_CLASSES_ROOT\Applications key.
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Re: Alternative for RunProgram

Post by blueznl »

Thanks all! I remembered playing around with this in the past, and suddenly recalled I wrote a procedure for this ages ago :-) (Probably stole it from someone... credits to the unknown creator then :-) )

Memory, and too busy with other things than programming :-)

Code: Select all

Procedure x_shellexecute(executable.s, parameters.s="", working.s="")                            ; alternative for runprogram()
  Protected x_retval.i, coinit.i, shellexecuteinfo.shellexecuteinfo\cbSize = SizeOf(shellexecuteinfo)
  ;
  ;shellexecuteinfo\fMask = #SEE_MASK_FLAG_NO_UI
  ;
  shellexecuteinfo\lpFile = @executable
  If parameters > ""
    shellexecuteinfo\lpParameters = @parameters
  EndIf
  If working
    shellexecuteinfo\lpDirectory = @working
  EndIf
  shellexecuteinfo\nShow = #SW_SHOWNORMAL
  coinit = CoInitializeEx_(0, #COINIT_APARTMENTTHREADED | #COINIT_DISABLE_OLE1DDE)
  If coinit = #S_OK Or coinit = #S_FALSE Or coinit & $FFFFFFFF = #RPC_E_CHANGED_MODE & $FFFFFFFF
    If ShellExecuteEx_(@shellexecuteinfo)
      x_retval = #True
    EndIf
    If coinit & $FFFFFFFF <> #RPC_E_CHANGED_MODE & $FFFFFFFF
      CoUninitialize_()
    EndIf
  EndIf
  ;
  ProcedureReturn x_retval
EndProcedure
Thanks for helping me jog my memory!
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
Fred
Administrator
Administrator
Posts: 18264
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Alternative for RunProgram

Post by Fred »

Is it different than RunProgram() ?
BarryG
Addict
Addict
Posts: 4186
Joined: Thu Apr 18, 2019 8:17 am

Re: Alternative for RunProgram

Post by BarryG »

@blueznl: What advantage does that extra code have over a single "RunProgram" command?

@Axolotl: Here's how I get the exe for any given document (no Registry access needed):

Code: Select all

Procedure.s ExeForDoc(doc$)
  doc$=LCase(doc$)
  If FileSize(doc$)=-1
    doc$=GetTemporaryDirectory()+Chr(160)+"."+GetExtensionPart(doc$)
    f=CreateFile(#PB_Any,doc$) : If f : CloseFile(f) : tmp=1 : EndIf
  EndIf
  exe$=Space(999) : FindExecutable_(GetFilePart(doc$),GetPathPart(doc$),@exe$)
  long$=Space(999) : GetLongPathName_(exe$,@long$,990)
  If LCase(exe$)=doc$ Or LCase(long$)=doc$ : exe$="" : EndIf ; Not a document.
  If tmp=1 : DeleteFile_(doc$) : EndIf
  ProcedureReturn exe$
EndProcedure

Debug ExeForDoc("C:\PROGRA~1\INTERN~1\iexplore.exe") ; Null because path is invalid.
Debug ExeForDoc("C:\Windows\System32\Bubbles.scr") ; Null because is not a document.
Debug ExeForDoc("C:\WINDOWS\regedit.exe") ; Null because is not a document.

Debug ExeForDoc("C:\Non-Existent-File.jpg") ; Shows exe for your image viewer.
Debug ExeForDoc("C:\test.txt") ; Shows exe for your text editor.
Debug ExeForDoc("index.html") ; Shows exe for your web browser.
Quin
Addict
Addict
Posts: 1135
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: Alternative for RunProgram

Post by Quin »

Think you meant 999 here :wink:. Super useful tip though, thanks for sharing! Feels like I learn some new Windows API functions every couple of days now.

Code: Select all

long$=Space(999) : GetLongPathName_(exe$,@long$,999)[/code]
Axolotl
Addict
Addict
Posts: 853
Joined: Wed Dec 31, 2008 3:36 pm

Re: Alternative for RunProgram

Post by Axolotl »

Thanks for the different solutions.
Funny thing, they all show a different result (at least on my computer). Just an observation.
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
BarryG
Addict
Addict
Posts: 4186
Joined: Thu Apr 18, 2019 8:17 am

Re: Alternative for RunProgram

Post by BarryG »

Code: Select all

long$=Space(999) : GetLongPathName_(exe$,@long$,990)
I once read that the allocated string should always be larger than the buffer given to the API command, which is why I allocate 999 and request a max of 990 to that buffer. My apps used to crash in the old days if I didn't do that, due to Unicode lengths. So it's a habit I've stuck with.
Axolotl
Addict
Addict
Posts: 853
Joined: Wed Dec 31, 2008 3:36 pm

Re: Alternative for RunProgram

Post by Axolotl »

Well, in the ANSI version there is the limit of #MAX_PATH (260) and on UNICODE you need "\\?\" to extend to 32767 TCHARs.
Fun Fakt: Most of the windows functions offer a help on that by using size ZERO for a first call to receive the needed buffer size.

Learned from MSDN:
Return value
If the function succeeds, the return value is the length, in TCHARs, of the string copied to lpszLongPath, not including the terminating null character.
If the lpBuffer buffer is too small to contain the path, the return value is the size, in TCHARs, of the buffer that is required to hold the path and the terminating null character.
If the function fails for any other reason, such as if the file does not exist, the return value is zero. To get extended error information, call GetLastError.
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
AZJIO
Addict
Addict
Posts: 2192
Joined: Sun May 14, 2017 1:48 am

Re: Alternative for RunProgram

Post by AZJIO »

BarryG
If you use Space(), then you don't need to add anything, since the internal variables are in #PB_Unicode format and create a variable with +2 bytes, meaning you have a variable that doesn't need to be appended with 2 bytes for the terminating 00.
If you use AllocateMemory() for a string, then you need to add 2 bytes. You don't need to add 9 bytes, everything should be accurate.
Post Reply