AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CMDret - AHK functions
Goto page Previous  1, 2, 3, 4 ... 10, 11, 12  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
Laszlo



Joined: 14 Feb 2005
Posts: 4002
Location: Pittsburgh

PostPosted: Tue Mar 14, 2006 12:46 am    Post subject: Reply with quote

I assume you tried the original (v. 1.05), and it worked correctly. It looks like I oversimplified the code, although it works for me on two different XP SP2 machines, so I cannot find the problem. Others: does it suck on your system, too?
Back to top
View user's profile Send private message
evl



Joined: 24 Aug 2005
Posts: 1238

PostPosted: Tue Mar 14, 2006 1:25 am    Post subject: Reply with quote

Sorry, my fault, forgot I had commented out the "SetBatchLines -1" (was being cautious as an earlier version locked up a bit) - turns out that it can mess up both of the examples most of the time but fine when it's there. The SetBatchlines command should probably be inside of the CMDret_RunReturn function to make sure it doesn't get commented out or left off as it seems to be essential.
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4002
Location: Pittsburgh

PostPosted: Tue Mar 14, 2006 1:57 am    Post subject: Reply with quote

Evl is right! Without " SetBatchLines -1 " the script does not work right. This is quite surprising. Can anyone explain it?

Accordingly, we need the following in the beginning of the function code
Code:
  BatchLines0 = %A_BatchLines%
  SetBatchLines -1
and in my shorthand version of Corrupt's code, the following one just before the Return line
Code:
  SetBatchLines %BatchLines0%
In the original there are multiple Return commands, each have to be preceded by this line.
Back to top
View user's profile Send private message
corrupt



Joined: 29 Dec 2004
Posts: 2393

PostPosted: Tue Mar 14, 2006 2:08 am    Post subject: Reply with quote

Laszlo wrote:
It still works, almost perfectly (version 1.05). A few questions:
Good to hear Smile . Thanks for testing Smile
Laszlo wrote:
- What do you use the GLOBAL cmdretPID for? When the function returns, it is empty. Do you want to check its value in a timer routine?
In most cases I would likely start the function with a timer instead but, yes ,the general idea is to give the user a method of stopping the process if necessary. One of the features that a few people had asked for with the .DLL version was a way to abort if a button was pressed by accident, something went wrong, etc...
Laszlo wrote:
- StringTrimLeft, CMDout, CMDout, 2 cuts off the first two characters of the output. Did this instruction remain from an earlier version?
It was added to cut RR from the beginning of the string if Return output was chosen. Removing the initial R value would cause it to truncate the first 2 characters of data
Laszlo wrote:
- SetBatchLines, -1 in a function makes a global change. You probably want to save the old value beforehand and restore it before return, but it is easier to remove it, and let the user specify it outside of the function, if he needs that extra speed.
I had added it in for testing but forgot to remove it. Thanks for the reminder.
Laszlo wrote:
- Do you need VarSetCapacity(lpBuffer, 0)? This local variable is removed, when the function returns, anyway.
Good point.
Laszlo wrote:
- Do you need " OR bSize > 0" in the instruction IF (ErrorLevel OR bSize > 0)? The next line checks if bSize > 0, anyway.
Yes, this is currently necessary. This is how the function determines when to read data from the pipe and when to stop trying to read and quit. I'll try and explain a bit better after trying to answer your questions... Smile
Laszlo wrote:
- To preserve any leading or trailing spaces, don't we need AutoTrim Off?
I haven't seen a need for it with the testing that I've done so far. Do you have an example that I could try to reproduce an issue?
Laszlo wrote:
Here is a variant, without the ByRef parameter and with changes according to the list above. I also made it more flat, such that there are fewer levels of nested if's (it is easier to find the matching braces). I made it for myself, to be able to understand the logic of the 1.05 code.

Thanks for sharing your modifications Smile . I'll have a look. I likely won't change the structure of the code in the first post for a few more versions though. Although I definitely appreciate the time and effort taken to remove a lot of the unnecessary structural syntax, I prefer to have the code a bit more spread out while adding to the code and testing different ideas. It's more of a personal preference I guess Smile . I'm starting to lean more toward the idea of returning the output as the Return value also. Maybe in the next version...
Back to top
View user's profile Send private message Visit poster's website
corrupt



Joined: 29 Dec 2004
Posts: 2393

PostPosted: Tue Mar 14, 2006 2:10 am    Post subject: Reply with quote

Laszlo wrote:
Evl is right! Without " SetBatchLines -1 " the script does not work right. This is quite surprising. Can anyone explain it?

Although I haven't taken a close look yet I'm guessing that this is likely due to removing the second If statement in the read loop.
Back to top
View user's profile Send private message Visit poster's website
corrupt



Joined: 29 Dec 2004
Posts: 2393

PostPosted: Tue Mar 14, 2006 2:48 am    Post subject: Reply with quote

Although it might not be necessary at this point, I thought I'd take a minute and try and explain a bit of what the current code is doing before working on the next version. Here's the basic idea (significant parts):

1) Create a pipe. Retrieve Read and Write handles. This is used to redirect the output from the console and to buffer the data.

2) Create a process by executing a console based program and set the output to use the write end of the pipe

3) Start a loop that Peeks at the contents in the pipe.

4) Next we need to know 2 details. When the process has finished executing and when we have read all of the data from the pipe. When both of these are true we want to close our read and write handles and return the results.

4a)If the process is still running we need to keep checking the pipe to see if it has placed any output in the pipe. If it has we need to read the data from the pipe and add it to our output variable.

4b)If the process has finished we need to check the pipe for data. If there is still data in the pipe we need to read this data and add it to our output variable before leaving the function.

4c)If the process is still running but the pipe is empty we want to try and slow things down a bit to prevent the script from acting as if it is in an endless loop and using up our processor's precious time. This is the reason for the sleep value in the loop.

4d)If something goes wrong and the process is hung we want to provide a method of aborting and returning the output so far. A global variable is currently provided that contains the PID of the running process so that various methods can be used to force the function to quit without having to kill the script.
Back to top
View user's profile Send private message Visit poster's website
Laszlo



Joined: 14 Feb 2005
Posts: 4002
Location: Pittsburgh

PostPosted: Tue Mar 14, 2006 5:29 am    Post subject: Reply with quote

corrupt wrote:
Laszlo wrote:
Without " SetBatchLines -1 " the script does not work right...
Although I haven't taken a close look yet I'm guessing that this is likely due to removing the second If statement in the read loop.
Adding the condition back, by
Code:
        If (ErrorLevel = 0 and bsize = 0)
          break
insted of
Code:
        IfEqual ErrorLevel,0, break
does not completely fix the problem. Occasionally, the directory is shown empty, but the original 1.05 code seems to be always OK. I shall stop shortening these codes, before something really bad happens...
Back to top
View user's profile Send private message
shimanov



Joined: 25 Sep 2005
Posts: 612

PostPosted: Tue Mar 14, 2006 7:12 am    Post subject: Reply with quote

Try replacing:

Code:
VarSetCapacity(lpBuffer, bSize)


with

Code:
VarSetCapacity( lpBuffer, bSize, 0 )


The data read from the pipe is not necessarily null-terminated...

and remove:

Code:
DllCall("lstrcpyn", "UInt", &lpBuffer, "UInt", &lpBuffer, "Int", bRead)
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4002
Location: Pittsburgh

PostPosted: Tue Mar 14, 2006 5:34 pm    Post subject: Reply with quote

Thanks, Shimanov! (I did not want to hijack this thread, but...) your suggestions did not help. Without SetBatchLines -1, once in every half a dozen runs one of the outputs are still shown as empty. I moved the Sleep 0 command BEFORE the IF...Continue, and it improved the error rate to one in 50 runs. Not surprising! It did not slow down the loop before. Increasing the Sleep time to 10 gave a slight further improvement. With Sleep 50 I did not experience this error any more (tried over 1000 runs). Here is the code. I suspect, there is still something fishy in there. With SetBatchLines -1, Sleep 0 is enough.
Code:
AutoTrim Off         ; needed for leading/trailing spaces returned
MsgBox 48, CMDret test - Environment Vars, % CMDret_RunReturn("cmd /c set")
MsgBox 48, CMDret test – Directory Listing,% CMDret_RunReturn("cmd /c dir c:\")

CMDret_RunReturn(CMDin)
{
  Global cmdretPID   ; for external abort
  VarSetCapacity(lpBuffer,1024)
  VarSetCapacity(sui,68, 0)
  VarSetCapacity(pi, 16, 0)
  VarSetCapacity(pa, 12, 0)
  InsertInteger( 12, pa, 0)
  InsertInteger( 1,  pa, 8)

  IF (DllCall("CreatePipe", "UInt*",hRead, "UInt*",hWrite, UInt,&pa, Int,0) <> 0) {
    InsertInteger(68,    sui, 0 )
    DllCall("GetStartupInfo", "UInt", &sui)
    InsertInteger(0x101, sui, 44)
    InsertInteger(0,     sui, 48)
    InsertInteger(hWrite,sui, 60)
    InsertInteger(hWrite,sui, 64)

    IF (DllCall("CreateProcess",Int,0,Str,CMDin,Int,0,Int,0,Int,1,UInt,0,Int,0,Int,0,UInt,&sui,UInt,&pi)<>0) {
      cmdretPID := ExtractUInt(pi, 8)
      Loop {
        IF DllCall("PeekNamedPipe",uint,hRead, uint,0, uint,0, uint,0, "UInt*",bSize, uint,0) = 0
          break
        Process Exist, %cmdretPID%
        If (ErrorLevel = 0 and bsize = 0)
          break
        Sleep 50
        IfEqual bSize,0, Continue
        VarSetCapacity(lpBuffer, bSize, 0)
        IF (DllCall("ReadFile",UInt,hRead, Str,lpBuffer, Int,bSize, "UInt*",bRead, Int,0) > 0) {
          IFEqual bRead,0, Continue
          CMDout = %CMDout%%lpBuffer%
          }
      } ; Loop
    } ; IF CreateProcess

    DllCall("CloseHandle", UInt, hWrite)
    DllCall("CloseHandle", UInt, hRead)
  } ; IF CreatePipe

  cmdretPID =
  Return CMDout
}

InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4) {
   Loop %pSize%
      DllCall("RtlFillMemory", UInt,&pDest+pOffset+A_Index-1, UInt,1, UChar,pInteger >> 8*A_Index-8)
}

ExtractUInt(ByRef pSource, pOffset = 0, pSize = 4) {
   Loop %pSize%
      result += *(&pSource+pOffset+A_Index-1) << 8*A_Index-8
   Return result
}
Back to top
View user's profile Send private message
shimanov



Joined: 25 Sep 2005
Posts: 612

PostPosted: Tue Mar 14, 2006 8:27 pm    Post subject: Reply with quote

Laszlo wrote:
your suggestions did not help


To each their own. I have not experienced any problems with the code.
Back to top
View user's profile Send private message
toralf



Joined: 31 Jan 2005
Posts: 3842
Location: Bremen, Germany

PostPosted: Tue Mar 14, 2006 8:33 pm    Post subject: Reply with quote

If I want to get the list of files from a directory that has spaces in its path, how do I call RunReturn() and pass the needed ""?

Edit: Found it:

Code:
RunReturn("cmd /c dir " """" "C:\Dokumente und Einstellungen\toralf" """")

_________________
Ciao
toralf
Back to top
View user's profile Send private message Send e-mail Visit poster's website
shimanov



Joined: 25 Sep 2005
Posts: 612

PostPosted: Tue Mar 14, 2006 10:47 pm    Post subject: Reply with quote

Here is another function, which may improve the reliability of reading from the pipe:

Code:
}
success := DllCall( "FlushFileBuffers", "uint", hWrite )
DllCall( "PeekNamedPipe", "uint", hRead, "uint", 0, "uint", 0, "uint", 0, "uint*", bSize, "uint", 0 )
if ( bSize > 0 ) {
  DllCall("ReadFile", "UInt",hRead, "Str", lpBuffer, "Int",bSize, "UInt*",bRead, "Int",0)
  CMDout = %CMDout% %lpBuffer%
}
cmdretPID=


The most likely source of failure occurs when the child process terminates, but the final write operation has not been completed. This condition can be verified on systems where this issue is evident, by inserting the following code:

Code:
}
Sleep, 10000
;success := DllCall( "FlushFileBuffers", "uint", hWrite )
DllCall( "PeekNamedPipe", "uint", hRead, "uint", 0, "uint", 0, "uint", 0, "uint*", bSize, "uint", 0 )
if ( bSize > 0 )
  MsgBox, pipe is not empty
cmdretPID=
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4002
Location: Pittsburgh

PostPosted: Wed Mar 15, 2006 12:24 am    Post subject: Reply with quote

" DllCall( "FlushFileBuffers", "uint", hWrite ) " freezes the function, I can kill it only with the Task Manager. If I use instead " DllCall("FlushFileBuffers", "uint", hRead) ", it does not solve the problem, sometimes the return value is empty (No SetBatchLines, Sleep 0). I tested it with
Code:
Loop 1000
{
   If StrLen(CMDret_RunReturn("cmd /c set")) < 100
      MsgBox ERROR
   If StrLen(CMDret_RunReturn("cmd /c dir c:\")) < 100
      MsgBox ERROR
   TrayTip,,%A_Index%
}
Back to top
View user's profile Send private message
shimanov



Joined: 25 Sep 2005
Posts: 612

PostPosted: Wed Mar 15, 2006 4:09 am    Post subject: Reply with quote

Laszlo wrote:
" DllCall( "FlushFileBuffers", "uint", hWrite ) " freezes the function


You really have some system-specific issues. I cannot reproduce any of the problems you have described.

It is left to you to deduce the cause and solution.

Quote:
" DllCall("FlushFileBuffers", "uint", hRead) "


This should have no effect, since the function affects write buffers. Although, I noticed that your variant of corrupt's code differs in the placement of key function calls. Namely, the calls to CloseHandle occur before clearing "cmdretPID".
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4002
Location: Pittsburgh

PostPosted: Wed Mar 15, 2006 4:19 am    Post subject: Reply with quote

shimanov wrote:
It is left to you to deduce the cause and solution.
Crying or Very sad
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page Previous  1, 2, 3, 4 ... 10, 11, 12  Next
Page 3 of 12

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group