dcsimg
Results 1 to 10 of 10

Thread: Comprehensive Shell (CreateProcess & ShellExecuteEx Together)

  1. #1

    Thread Starter
    Lively Member
    Join Date
    Feb 2015
    Location
    Colorado USA
    Posts
    80

    Comprehensive Shell (CreateProcess & ShellExecuteEx Together)

    Module mShellW – Creating New Processes

    Overview This module greatly extends what you can do with the Shell function in VBA or Visual Basic 5 or 6 (VB6):
    • Everything is Unicode, including the Shelled-to environment.
    • The code works “as-is” in 32 or 64-bit MS Office or in Visual Basic 5 or 6.
    • This module can be run on Windows XP and later.
    • You can start a program by specifying a document with an extension. For example, if you specify D:\MyFiles\abc.docx and MS Word is registered to open .docx files then Word will start with this file.
    • There is a very easy function to use to shell to CMD.EXE that takes care of all of the intricacies in setting up the commandline to CMD.
    • You can specify how long, if at all, the code waits for the shelled-to program to complete before returning. Specify anything from 0 to effectively forever. You can also specify a time for a pop-up window to the user if he wants the program to quit waiting any further. This is a nice escape from a program that you specify “wait for forever to return” and it gets hung up and never quits.
    • You can execute programs from anywhere on your PC. For example, if you are running 32-bit VBA or VB6 but are running on a 64-bit version of Windows, you stil can run any 64-bit program. You can even tell it that you want to run the 64-bit version of calc.exe and many other programs that reside in the Windows\System32 folder. Normally a 32-bit program gets redirected away from this folder “behind the scenes” but we work around that problem.
    • You can run programs that require elevated permissions (the UAC prompt). Normally this is not possible because the Windows CreateProcess function won’t do this but you have the option to 1) keep the privilege level the same as it currenty is, 2) run the new process elevated, 2) run the new process elevated only if it is required (avoids the UAC prompt unless it is necessary) or 3) do not run elevated (fails if elevation is required).
    • If your program is running elevated, you can specify that the shelled-to routine is non-elevated. Usually a shelled-to routine will be elevated if the host program is elevated.
    • You can run batch files (.bat and .cmd), windows scripts and PowerShell all without knowing anything other than the extension of the file to “run.”
    • You can execute a program if you don’t know where it is. For example, you installed Notepad++.exe in “C:\Program Files (x86)\” but as the programmer you don’t know if your users installed it there or if they even installed it at all. And to make matters worse, if they did install it, did they install the 32 or 64-bit version? This code can find the executable file.
    • Normally, shelling is done via the CreateProcess Windows API call, the significance of which is that the new process is independent from the calling program which makes it multi-threaded. You can have the calling program wait until the shell-to routine is complete or you can return control to the calling program and have it continue while the shelled-to program runs in the background. The other common method of shelling to an external program is to use ShellExecuteEx but that routine has limitations which we avoid with CreateProcess. ShellExecuteEx’s main advantage is being able to specify a document instead of an executable and we provide that functionality without using ShellExecuteEx. However, we do include ShellExecuteEx so you can specify it if you really want. Also, ShellExecuteEx is the routine we use to raise permissions when desired or required since CreateProcess lacks that capability.
    • You can use the Windows’ CreateProcess function or ShellExecuteEx functions for your external call. There are pros and cons for each approach; either will work in most instances. The differences are discussed later in this document. CreateProcess is not limited to 255 characters in the commandline. ShellExecuteEx can run programs with elevated privileges.
    • Most programs start a new instance of themselves when we open with CreateProcess even if an existing instance is running. However, this is not true by default for MS Excel, MS Word and Notepad++. If an instance is already running when you specify opening another one, the new document(s) will eb opened in the instance that is already running. This may be okay but know that the new documents share the same memory (if you are running 32-bit versions of these programs this could be limit) and if for some reason something causes the instance to crash then everything in it including the new documents will crash as well. If they are in a different instance they have their own memory space and do not normally crash when another instance crashes. When you use DocShellW in this module you can specify that the shell is to a new instance and if the program is MS Excel, MS Word or Notepad++ then we apply some special code to force them to start a new instance. You can keep the other behavior of putting them al into one instance by specifying NewInstance as False in the call.
    • This module requires the use of my core library mUCCore. It provides my error handling ystem, Unicode support, the basis for support for all flavors of VBA including 64-bit as well as VB6 (compiled or in the IDE) and it contains many routines I use all of the time. It is accompanied by a separate user guide. You don’t need to know what it does in order to use this shelling module but you may find many parts of it useful for your programs.
    • When I test my shell functions I can hard-code various files I want to open or programs to run. I can’t do that with the sample program I have provided because your files are different than mine and are in different locations than mine. So I have included another of my library modules, this one named FileStuffLite. It includes many things not needed for mShellW but I am using a small part of it just for the demo/test. It is not needed for mShellW to function.
    • The code is LARGEADDRESSAWARE. For VB6 runing in 32-bit Windows you can access up to 3 GB of memory and if run in 64-bit Windows (yes, VB6 itself is 32-bit) you can access up to 4 GB. If you are running Excel 2013 or 2016 you can acces up to 4 GB of memory. This shell by itself does not set that up but it is coded such that you won’t get memory crashes from this module when data or code is above the 2 GB mark.

    Main Routines ShellW – The main routine for running another program. DocShellW – Open a file with whatever program is registered in Windows for opening that type of file. For example, you could specify “Test.doc” and the file would be opened with Microsoft Word on most PC’s but if .doc files are set to be opened with some other program then that one is used. CMDRun – Run a batch file, a PowerShell file, a program, a document file or just a command prompt via CMD (very similar to the old DOS prompt). You control whether it goes away after executing whatever you want it to do. Support Routines QuoteIfNec – If the specified string (normally a path to a program or a document) has a space character in it Windows generally wants it enclosed in quotes. This routine checks if the string has a space character and if so it encloses the whole string in quotes. ShellHandlesClose – Closes process and thread handles and resets internal variables for them to 0. HasProcessClosed – Determines if a specified process has ended. IsExeRunning – Returns True if the specified EXE file is running. FindEXEPath – Returns the full path of the specified .EXE file. Can be called within ShellW. FindEXEFromDoc – Returns the program in the current PC’s registry that is set to open the specified document file. Can be called from within DocShellW. NOTE - This same package with VBA samples is being uploaded to the Office Development forum here.
    Attached Files Attached Files
    Last edited by MountainMan; Jul 26th, 2018 at 06:00 AM. Reason: Small bug fix. See post #4.

  2. #2
    Fanatic Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    523

    Re: Comprehensive Shell (CreateProcess & ShellExecuteEx Together)

    Hi, MountainMa!

    I like that people do not hesitate to show the whole helper functions such a way like you.
    I found few interesting things for me, especially the way you also support MS Office x64 bit there.
    And personally, I should say the code written fairly high quality in most cases.
    I have several suggestion and edits to them, if you even like to improve something there
    (I also have W-heper functions, some of them I posted here (look in my signature)).
    But I don't think this thread is appropriate place to discuss that stuff.

    As about ShellW:

    Quote Originally Posted by MountainMan
    Normally, shelling is done via the CreateProcess Windows API call, the significance of which is that the new process is independent from the calling program which makes it multi-threaded.
    Strange conclusion.

    Code:
    Private Declare Function Wow64RevertWow64FsRedirection Lib "kernel32.dll" (ByRef Wow64FsEnableRedirection As Long) As Long
    That's a wrong declaration. Should be ByVal. If you refer to MSDN, PVOID - mean the value represents pointer to service data, not that it's pass type + idx as value.

    Quote Originally Posted by code
    we will use explorer.exe since it is always running and it always is unelevated
    Both statements are not always true.
    Although, first part doesn't matter since you spawn new explorer process rather than using CreateProcessWithTokenW.

    Code:
    Ret = ShellW(AppName:="!cmd", AsAdmin:=AdminNo)
    ???
    Why just not checking for .exe at the end of name or presence of "." dot.
    All right, it's your calling convention... Just a wish. But, when you press Win + R, it hadle line by 'shell' mechanism and windows don't ask for "!" char.

    For VB6 runing in 32-bit Windows you can access up to 3 GB of memory and if run in 64-bit Windows
    Fixing Ptr-s in your code doesn't mean app will/can use large address space. To do so, you also need to patch exe by set up IMAGE_FILE_LARGE_ADDRESS_AWARE flag in COFF header -> Characteristics field, or use special linker switch.
    (the same is actual for some vesions of MS office executables, however I would not recommend to force patch them (for some reason(?), because 3rd party add-ons can be written witout such support and can crash whole app)

    ---
    off.
    And, I'm just curious, what is that mean, what sense:
    GetMem4 ((VarPtr(vArray) Xor &H80000000) + 8&) Xor &H80000000, pSA
    Last edited by Dragokas; Jul 25th, 2018 at 11:23 AM.

  3. #3

    Thread Starter
    Lively Member
    Join Date
    Feb 2015
    Location
    Colorado USA
    Posts
    80

    Re: Comprehensive Shell (CreateProcess & ShellExecuteEx Together)

    Quote Originally Posted by Dragokas View Post
    Strange conclusion.
    Why is it that a strange conclusion? If I create a new process it runs in its own memory space separate from the memory space allocated to the shelling program. They are not connected. you can spawn a 64-bit program from a 32-bit program and vice versa so they don't even have to have the same "bitness." particularly with VBA where we don't even have an option to multi-task, this is an easy way to do this.

    Quote Originally Posted by Dragokas View Post
    Code:
    Private Declare Function Wow64RevertWow64FsRedirection Lib "kernel32.dll" (ByRef Wow64FsEnableRedirection As Long) As Long
    That's a wrong declaration. Should be ByVal. If you refer to MSDN, PVOID - mean the value represents pointer to service data, not that it's pass type + idx as value.
    You are correct. That should be ByVal and not ByRef.

    Quote Originally Posted by Dragokas View Post
    Both statements are not always true.
    Although, first part doesn't matter since you spawn new explorer process rather than using CreateProcessWithTokenW.
    I am not spawning a new explorer.exe but instead using the one that is already running. See here or here for a discussion on this. We are using the original un-elevated version of explorer.exe via ShellExecuteEx. You don't have to look too hard on the web for many different attempts at this and this is the cleanest I have seen that doesn't get tripped up by different versionso of Windows. now if you can convince me that a significant number of users kill the original explorer.exe then I could incorporate onof the other techniques as a back-up.

    Quote Originally Posted by Dragokas View Post
    Code:
    Ret = ShellW(AppName:="!cmd", AsAdmin:=AdminNo)
    ???
    Why just not checking for .exe at the end of name or presence of "." dot.
    All right, it's your calling convention... Just a wish. But, when you press Win + R, it hadle line by 'shell' mechanism and windows don't ask for "!" char.
    The "!" is my own addition. If you put an application such as CMD.EXE into the function, do you assume that it is CMD.EXE (or whatever EXE file) is in the current directory or should we go look for it somewhere and if so is it in the "Program Files" or "Program Files (x86)" set of folders or does it reside in System32 or SysWow64? CreateProcess especially likes the complete path to the executable and ShellExecuteEx does as well so I wanted some way to tell the function that we don't know where on the user's PC the particular file is so please go find it. I had to pick a character to use to signify the "go find it" part and I just don't remember ever seeing a path or filename starting with "!" so I have been using it for a while. So it doesn't mean anything to Windows but it does/should to people writing code around my function.

    Also, I could have put the .EXE on the end of CMD but i was trying to show in the example that if you leave it off for whatever reason I'll put it on for you.

    Quote Originally Posted by Dragokas View Post
    Fixing Ptr-s in your code doesn't mean app will/can use large address space. To do so, you also need to patch exe by set up IMAGE_FILE_LARGE_ADDRESS_AWARE flag in COFF header -> Characteristics field, or use special linker switch.
    (the same is actual for some vesions of MS office executables, however I would not recommend to force patch them (for some reason(?), because 3rd party add-ons can be written witout such support and can crash whole app)
    Difficult topic. What I can say is that if LARGEADRESSAWARE busts a project in VBA or VB6 at least it won't be due to my code. I am not responsible for that bust. I believe Excel 2013 and Excel 2016 are LARGEADDRESSAWARE (32-bit, 64-bit doesn't need). I am not even sure you can turn that off. I don't want a user with one of these systems to crash because of me. I didn't claim that my coding makes all programs LARGEADDRESSAWARE. My code is and I hope more coders do it as well. It doesn't hurt anything and it might help. Once you know what to do in pointer arithmetic to deal with the issue it isn't that difficult to do either.

    Finally you asked about the code line

    Code:
    GetMem4 ((VarPtr(vArray) Xor &H80000000) + 8&) Xor &H80000000, pSA
    That comes from a function in mUCCore that plays around with the VB SafeArray to determine if any dimensions have been set for the array. Of all of them I looked at this was the simplest and quickest (most rely on trying to access an array member and raising an error which takes a lot of time code-wise).

    If you are asking about the strange Xor coding and back, that's how you make your pointer arithmetic LARGEADDRESSAWARE. In 32-bit VB we use Longs for our pointer address but they are signed and don't do well at the 2 GBboundary. We have a discussion a month or so ago on this board about this very topic and the technique used for guaranteeing that the pointer arithmetic works correctly across the 2 GB boundary is to use this math. It's not my code but I think it came from one of TheTrick's colleagues.

  4. #4

    Thread Starter
    Lively Member
    Join Date
    Feb 2015
    Location
    Colorado USA
    Posts
    80

    Re: Comprehensive Shell (CreateProcess & ShellExecuteEx Together)

    Update released. Dragokas found a bug in one my API Declares. I turns out that its sister function had the same bug. Both are now fixed and their knock-off effects in the function ShellW function are now fixed as well.

  5. #5

    Thread Starter
    Lively Member
    Join Date
    Feb 2015
    Location
    Colorado USA
    Posts
    80

    Re: Comprehensive Shell (CreateProcess & ShellExecuteEx Together)

    If you are interested in the concepts around LARGEADDRESSAWARE (which now potentially affects all VB6/VBA programmers), you can read a discussion here on vbForums or here from Microsoft.

    Essentially the LARGEADDRESSAWARE is a feature that enable 32-bit programs to access memory above the 2 GB boundary (the max for a Long variable). With a 32-bit operating system you can potentially address up to 3 GB and 32-bit code running in a 64-bit operating system (quite common now) can access up to 4 GB. If your code does not use pointers (StrPtr, VarPtr, ObjPtr) or the various memory copy or move routines (RtlMoveMemory etc.) then you don't need to be concerned. However, if you do use any of these, as 32-bit programmers we use Long variables to keep track of memory addresses. But longs are signed which means that when we try to go above the max value (which causes the high bit to be set) VB interprets this as a negative number. So for example, if you have the address of a string, you likely found that using StrPtr and if you wanted to access the 2nd character you would take the address of the start of the string and add 2 bytes. But what if the base address for the string is actually above the 2 GB boundary to start with? Now it is a negative number. It turns out that we are okay even using signed Longs but in those cases where we cross the magical 2 GB boundary the calculation goes horribly wrong and a crash is almost certain to occur.

    If you don't use pointers but instead rely on VB to keep track of where variables are then you won't have to be concerned with this. However, if you do any of what I described above (the so-called "pointer arithmetic") then you have to make sure that adding and subtracting values from signed Longs work correctly or you will crash if/when code or data gets really large and crosses the 2 GB boundary. Since you have no idea when/if this will occur I encourage you to know a bit more about this issue. You may elect to do nothing but at least you will be making an informed decision.

    I have taken the approach in all my code now that I will make it LARGEADDRESSAWARE. I can't guarantee that anyone else's code is but I can make sure mine doesn't cause inadvertent problems. I encourage you to think through this if you haven't already. Having access to very large memory can be a wonderful thing; just approach it responsibly.

    BTW, the solution is relatively simple. Anywhere in your code where you do pointer arithmetic, you just want to ensure that the calculation of adding or subtracting an offset to a memory address gets done correctly across the 2 GB boundary. In other words we want to have the 32-bit Long to have its number calculated as if it were an unsigned 32-bit number instead of a 32-bit signed number. From the above referenced link to the vbForums discussion, you will see a proposed calculational technique that does exactly that. Here is a little routine that does just that.

    Code:

    Code:
    Function UnsignedAdd(ByVal UnsignedPtr As Long, ByVal SignedOffset As Long) As Long
    'Safely add *signed* offset to *unsigned* ptr for *unsigned* retval w/o overflow in LARGEADDRESSAWARE processes
    UnsignedAdd = ((UnsignedPtr Xor &H80000000) + SignedOffset) Xor &H80000000
    End Function
    You will find this function in the module mUCCore included with the mShellW module. I have so much code for this that for speed reasons I don't call this function but instead hard code the equation in the function inline. It really doesn't add much to the code size and its one less procedure call to make. But calling the function above works just as well, albeit a bit more slowly. If yo look through any of my modules you will see this unsigned pointer arithmetic scattered throughout.

    It is also important that you understand that you don't need to know anything about any of this to my code. All of the Subs and Functions I provide for you to use do not require knowing any of this. As VB guys/gals we always catch crap from programmers using other languages that we don't do pointers so we are somehow inferior. I like to believe that by using pointers but doing it where you or I are not even aware of it is the best of both worlds. We can do things that we other wise can't even do with VB code but you don't have to deal with it. After all, that's the basis of VB code anyway. The folks who wrote VB used pointers all the time; they just hid that all from us to let us focus on our coding. I am trying to do the same thing.

    BTW, if you code for 64-bit VBA you don't need to do any of this complicated pointer arithmetic thing. Even if you do pointer arithmetic, you use 64-bit LongPtr or LongLong to hold the memory address so your memory would have to be enormous (9,223,372,036,854,775,807 bytes to be exact) before you crossed the signed/unsigned boundary. So for 64-bit, just take the base address and add or subtract the offset value.

    A word of caution - You may have noticed that starting with Office 2010 we have access to a LongPtr in VBA and it is tempting to think that this is one of the huge numbers described in the paragraph above so we don't need to worry about any of this. Don't be fooled! If your code is running in a 32-bit environment, the LongPtr is the same as a Long; it is only a 64-bit number when the code is run in a 64-bit version of Excel, Word, etc. So just having your pointer math done via LongPtr's doesn't alleviate the concerns of LARGEADDRESSAWARE in the 32-bit world.

    this is discussed starting on page 48 in my documentation for module mUCCore which is included with this ShellW package. Also, I write all of my code to work in VBA as well as VB6 so I have a very similar submission here that has a similar discussion to what I have put above.
    Last edited by MountainMan; Jul 27th, 2018 at 12:48 PM.

  6. #6

    Thread Starter
    Lively Member
    Join Date
    Feb 2015
    Location
    Colorado USA
    Posts
    80

    Re: Comprehensive Shell (CreateProcess & ShellExecuteEx Together)

    ....
    Last edited by MountainMan; Jul 27th, 2018 at 12:46 PM. Reason: Duplicate post

  7. #7
    Frenzied Member
    Join Date
    Jun 2015
    Posts
    2,010

    Re: Comprehensive Shell (CreateProcess & ShellExecuteEx Together)

    FYI, Dragokas was merely pointing out the difference between multi-threaded vs multi-process. You said multi-threaded when you clearly meant multi-process.

    Quote Originally Posted by Wikipedia
    Multithreading is a widespread programming and execution model that allows multiple threads to exist within the context of one process.
    It's not surprising you got it wrong - there's a ton of misinformation on this topic. Even portions of Wikipedia confuse the difference between the context of "computer architecture" and "computing".

    Quote Originally Posted by Wikipedia
    the definition of multiprocessing can vary with context
    Last edited by DEXWERX; Jul 26th, 2018 at 09:14 AM.
    Imagine what it would be like to set breakpoints in, or step through subclassing code;
    and then being able to hit stop/end/debug or continue, without crashing the IDE.

    VB6.tlb | Bulletproof Subclassing in the IDE (no thunks/assembly/DEP issues)

  8. #8

    Thread Starter
    Lively Member
    Join Date
    Feb 2015
    Location
    Colorado USA
    Posts
    80

    Re: Comprehensive Shell (CreateProcess & ShellExecuteEx Together)

    Thanks for pointing that out. The shells are multi-processing and I should not use the word "multi-threading" because we are not sharing memory between the processes and that's a key feature of multi-threading. My bad.

  9. #9
    Fanatic Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    523

    Re: Comprehensive Shell (CreateProcess & ShellExecuteEx Together)

    MountainMan, thank you for update and detail explanation.
    I understand now, that Xor &H80000000 intended for temporarily sign preserve.

    here from Microsoft regarding Excel.
    Interesting info. So, LongPtr type in Excel 2013/2016 can be both 4 byte and 8 byte depending on KB (build) installed.
    ---
    yes, DEXWERX cathced my thoughts. Perhaps, you mean multi-task, not multi-thread. Though, that's kind of diffuse term. I could call it MDI, but it's not, because it have no common client area. So, It's better an official term "Multi-instance".
    particularly with VBA where we don't even have an option to multi-task, this is an easy way to do this.
    We have. Here are the options:
    1) CreateObject("Excel.Application")
    2) Microsoft registry tweak, see DisableMergeInstance.

    I am not spawning a new explorer.exe but instead using the one that is already running. See here or here for a discussion on this.
    You are spawning. Check PID. See the link you provided and compare with your call. ShellExecuteEx is not the same. You can refer to this topic, where 2 solutions were provided.
    I'm not speaking that your code not do the job. No. I just want to point out that in the 1st post there is a disinformation.
    You managed to take Limited token just because Windows care itself about new explorer.exe not launch elevated (but, only if there is already one exist, otherwise it will be launched elevated in Win7 (Win8+ differently)):
    CommandLine=C:\Windows\explorer.exe /factory,{75dff2b7-6936-4c06-a8bb-676a7b00b24b} -Embedding
    Quote Originally Posted by MountainMan
    now if you can convince me that a significant number of users kill the original explorer.exe then I could incorporate onof the other techniques as a back-up.
    Again, no, don't need, and I will not/can not. The main goal is bolded above.

    Quote Originally Posted by MountainMan
    The "!" is my own addition. If you put an application such as CMD.EXE into the function, do you assume that it is CMD.EXE (or whatever EXE file) is in the current directory or should we go look for it somewhere and if so is it in the "Program Files" or "Program Files (x86)" set of folders or does it reside in System32 or SysWow64?
    Of course I can assume. And it's not my wish, that is how Windows work.
    That is called "Search order". You even have such code that care about PATHEXT and PATH. So I didn't see the reason why do you need "!". Look:
    You can even go to experiment. Win + R, enter some.doc and Windows will search it for you in current folder first (by default, for Win+R it's a %UserProfile%), next in the paths stored in %PATH%. Next, if Windows can't find it at all, it uses %PATHEXT% by prepending extension one by one and go by %PATH% again, like some.doc.exe, some.doc.bat e.t.c.
    As about do we need search or not: it's a term absolute/relative path. Before searching Windows normalize path (GetFullPathName) and just check file in place (is it exist). If not and if user not provided full or relative path (just filename), search order takes place.
    This is how it's implemented by me:
    Code:
    Public Function FindOnPath(ByVal sAppName As String, Optional bUseSourceValueOnFailure As Boolean, Optional sAdditionalDir As String) As String
        On Error GoTo ErrorHandler:
        
        '// TODO:
        'replace PathFindOnPath API by manual parsing of %PATH%, because PathFindOnPath includes folders while searching, that is unappropriate
    
        AppendErrorLogCustom "FindOnPath - Begin"
    
        Static Exts() As String
        Static isInit As Boolean
        Dim ProcPath$
        Dim sFile As String
        Dim sFolder As String
        Dim pos As Long
        Dim i As Long
        Dim sFileTry As String
        Dim bFullPath As Boolean
        Dim bSuccess As Boolean
    
        If Not isInit Then
            isInit = True
            Exts = Split(EnvironW("%PathExt%"), ";")
            For i = 0 To UBound(Exts)
                Exts(i) = LCase$(Exts(i))
            Next
        End If
    
        If Len(sAppName) = 0 Then Exit Function
    
        If Left$(sAppName, 1) = """" Then
            If Right$(sAppName, 1) = """" Then
                sAppName = UnQuote(sAppName)
            End If
        End If
    
        If Mid$(sAppName, 2, 1) = ":" Then bFullPath = True
    
        If bFullPath Then
            If FileExists(sAppName) Then
                FindOnPath = sAppName
                Exit Function
            End If
        Else
            If 0 <> Len(sAdditionalDir) Then
                sFileTry = BuildPath(sAdditionalDir, sAppName)
                If FileExists(sFileTry) Then
                    FindOnPath = sFileTry
                    Exit Function
                End If
            End If
        End If
    
        pos = InStrRev(sAppName, "\")
    
        If bFullPath And pos <> 0 Then
            sFolder = Left$(sAppName, pos - 1)
            sFile = Mid$(sAppName, pos + 1)
    
            For i = 0 To UBound(Exts)
                sFileTry = sFolder & "\" & sFile & Exts(i)
    
                If FileExists(sFileTry) Then
                    FindOnPath = sFileTry
                    Exit Function
                End If
            Next
        Else
            ToggleWow64FSRedirection False
    
            If InStr(sAppName, ".") <> 0 Then
                ProcPath = Space$(MAX_PATH)
                LSet ProcPath = sAppName & vbNullChar
            
                If CBool(PathFindOnPath(StrPtr(ProcPath), 0&)) Then
                    FindOnPath = TrimNull(ProcPath)
                    If FileExists(FindOnPath) Then 'if not a folder
                        bSuccess = True
                    End If
                End If
            End If
            
            If Not bSuccess Then
                'go through the extensions list
    
                For i = 0 To UBound(Exts)
                    sFileTry = sAppName & Exts(i)
    
                    ProcPath = String$(MAX_PATH, 0&)
                    LSet ProcPath = sFileTry & vbNullChar
    
                    If CBool(PathFindOnPath(StrPtr(ProcPath), 0&)) Then
                        FindOnPath = TrimNull(ProcPath)
                        Exit For
                    End If
                    
                Next
                
            End If
    
            ToggleWow64FSRedirection True
        End If
    
        AppendErrorLogCustom "FindOnPath - App Paths"
    
        If Len(FindOnPath) = 0 And Not bFullPath Then
            sFile = Reg.GetString(HKEY_LOCAL_MACHINE, "Software\Microsoft\Windows\CurrentVersion\App Paths\" & sAppName, vbNullString)
            If 0 <> Len(sFile) Then
                If FileExists(sFile) Then
                    FindOnPath = sFile
                End If
            End If
        End If
    
        If Len(FindOnPath) = 0 And bUseSourceValueOnFailure Then
            FindOnPath = sAppName
        End If
    
        AppendErrorLogCustom "FindOnPath - End"
    
        Exit Function
    ErrorHandler:
        ErrorMsg Err, "FindOnPath", "AppName: ", sAppName
        ToggleWow64FSRedirection True
        If inIDE Then Stop: Resume Next
    End Function
    If, for some reason, you like to run executables by CreateProcess and registered extensions by Shell, there is GetBinaryType API.
    However, it's just suggestion. Don't take it too close. If you are already use "!" in many projects, of course, there are no reasons for re-make.

    What I can say is that if LARGEADRESSAWARE busts a project in VBA or VB6 at least it won't be due to my code. I am not responsible for that bust
    Again, I don't want to force this support. I just wanted to care that description of your project was correct. Currently:
    or VB6 runing in 32-bit Windows you can access up to 3 GB of memory and if run in 64-bit Windows
    read as like, "if 'Comprehensive Shell' project code is Large adress aware, so using your code any VB6 program automatically can use large address space", but it's impossible without flag in header, and you didn't mention it as well as a notice about non-updated Office 2013/2016.

  10. #10
    Fanatic Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    523

    Re: Comprehensive Shell (CreateProcess & ShellExecuteEx Together)

    BTW, If you want to do vice versa, open document/sheet in single-instance, when default behavior for system set to multi-intance for every doc/xls to be open,
    you can use ROT or AccessibleObjectFromWindow method.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width