 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
Laszlo
Joined: 14 Feb 2005 Posts: 4002 Location: Pittsburgh
|
Posted: Tue Mar 14, 2006 12:46 am Post subject: |
|
|
| 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 |
|
 |
evl
Joined: 24 Aug 2005 Posts: 1238
|
Posted: Tue Mar 14, 2006 1:25 am Post subject: |
|
|
| 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 |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4002 Location: Pittsburgh
|
Posted: Tue Mar 14, 2006 1:57 am Post subject: |
|
|
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 |
|
 |
corrupt
Joined: 29 Dec 2004 Posts: 2393
|
Posted: Tue Mar 14, 2006 2:08 am Post subject: |
|
|
| Laszlo wrote: | | It still works, almost perfectly (version 1.05). A few questions: | Good to hear . Thanks for testing
| 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...
| 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 . 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 . 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 |
|
 |
corrupt
Joined: 29 Dec 2004 Posts: 2393
|
Posted: Tue Mar 14, 2006 2:10 am Post subject: |
|
|
| 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 |
|
 |
corrupt
Joined: 29 Dec 2004 Posts: 2393
|
Posted: Tue Mar 14, 2006 2:48 am Post subject: |
|
|
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 |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4002 Location: Pittsburgh
|
Posted: Tue Mar 14, 2006 5:29 am Post subject: |
|
|
| 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 |
|
 |
shimanov
Joined: 25 Sep 2005 Posts: 612
|
Posted: Tue Mar 14, 2006 7:12 am Post subject: |
|
|
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 |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4002 Location: Pittsburgh
|
Posted: Tue Mar 14, 2006 5:34 pm Post subject: |
|
|
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 |
|
 |
shimanov
Joined: 25 Sep 2005 Posts: 612
|
Posted: Tue Mar 14, 2006 8:27 pm Post subject: |
|
|
| Laszlo wrote: | | your suggestions did not help |
To each their own. I have not experienced any problems with the code. |
|
| Back to top |
|
 |
toralf
Joined: 31 Jan 2005 Posts: 3842 Location: Bremen, Germany
|
Posted: Tue Mar 14, 2006 8:33 pm Post subject: |
|
|
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 |
|
 |
shimanov
Joined: 25 Sep 2005 Posts: 612
|
Posted: Tue Mar 14, 2006 10:47 pm Post subject: |
|
|
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 |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4002 Location: Pittsburgh
|
Posted: Wed Mar 15, 2006 12:24 am Post subject: |
|
|
" 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 |
|
 |
shimanov
Joined: 25 Sep 2005 Posts: 612
|
Posted: Wed Mar 15, 2006 4:09 am Post subject: |
|
|
| 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 |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4002 Location: Pittsburgh
|
Posted: Wed Mar 15, 2006 4:19 am Post subject: |
|
|
| shimanov wrote: | | It is left to you to deduce the cause and solution. |  |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|