Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

CMDret - return output from console progs [DLL version]


  • Please log in to reply
175 replies to this topic
Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004

Updated to version 3.1.1
- Added another function to the dll (RunReturn) to allow returning output to a variable.

This is nicely compact. Your continuing improvements are making this irresistible :)

Eventually, a syntax will be devised to have CmdRet as a built-in feature: either a direct extension of the Run command or a separate helper command. Perhaps simply adding an OutputVar parameter to the end of the Run command is workable.

Thanks.

JBensimon
  • Members
  • 167 posts
  • Last active: May 08 2017 10:25 PM
  • Joined: 16 Nov 2004
Great stuff, corrupt!!

Until Chris made the following comment in a different thread, ...

By the way, if you have the DLL copy its result into a string provided by the caller, you can avoid the need for LoadLibrary(), FreeLibrary() and lstrcpy().


... the fact that DllCall makes it possible for a DLL to modify the contents of an AHK variable had somehow eluded me (despite the fact that it's clearly stated in the docs! :oops:). Once it finally hit me yesterday, I realized I am now be in a position to actually contribute code to this community (remember I'm the C/Assembler guy who can't do C++), so until your latest release, I had starting planning something along the lines of

@ := "str"     ; For readability
ExitCode := DllCall("AHKEx\RunWaitEx", @, "executable", @, "parameters", @, "workingdir", @, InVar, @, OutVar, @, ErrVar, "int")

for quietly launching console programs with fully redirected I/O (unlike AHK's RunWait, I was thinking of separating the executable from its parameters in order to avoid having to reproduce AHK's no-doubt complicated parsing rules for figuring out where one ends and the other begins). InVar above could be replaced by an explicit string, but OutVar and ErrVar would be appropriately sized variables. The programmer would initially assume the risk of insufficiently sized output variables, unless I can come up with a simple way to pass the current size of a variable to the call without burdening the syntax with additional parameters, like maybe making the initial contents of a variable reflect its size?

VarSetCapacity(OutVar, 10240)
OutVar = 10240

(Hmm, I wonder whether I can talk Chris into a VarSetCapacity option to place the variable's new size as a DWORD in its first 4 bytes? :wink: Would save the conversion work...)

In any case, now that you've made such progress with CmdRet, I'd like to put the question whether I should proceed (when time allows) or whether you might be planning a similar more complete implementation of your new RunReturn call? [BTW, may I ask in what language you wrote CmdRet.dll? I was surprised by its tiny size, almost Assembler-like!)

Again, congratulations on a job well done!

Jacques.

JBensimon
  • Members
  • 167 posts
  • Last active: May 08 2017 10:25 PM
  • Joined: 16 Nov 2004
Chris,

Based on the various discussions of DllCall that have sprung up since you released the feature [with due credit of course to Marcus Sonntag (Ultra)] and thinking now about using it to add AHK capabilities without bugging you for every little thing, can I assume that the internal format of an AHK variable is a straight null-terminated string (within a pre-allocated buffer the size of which you keep track of separately and expand as needed?) I guess what I'm trying to confirm is that AHK variables don't have some sort of "header" structure containing their size, type, etc., as is sometimes the case in the implementation of other interpreted languages (anybody out there still remember my all-time favorite language, APL?)

[Bonus question, for pure curiosity: if that is in fact the case (variables = null-terminated strings), how were you planning to implement the reading/writing of binary files which I think I've seen you mention as a future possibility?]

Jacques.

  • Guests
  • Last active:
  • Joined: --

Great stuff, corrupt!!

Until Chris made the following comment in a different thread, ...


By the way, if you have the DLL copy its result into a string provided by the caller, you can avoid the need for LoadLibrary(), FreeLibrary() and lstrcpy().



Thanks:)

The latest version of CMDret (3.11) does copy the result back into a variable provided by the caller (RunReturn function) :) .

CMDret has been designed using BCX.

corrupt

  • Guests
  • Last active:
  • Joined: --

Updated to version 3.1.1
- Added another function to the dll (RunReturn) to allow returning output to a variable.

This is nicely compact. Your continuing improvements are making this irresistible :)


Thanks :)

corrupt

JBensimon
  • Members
  • 167 posts
  • Last active: May 08 2017 10:25 PM
  • Joined: 16 Nov 2004

The latest version of CMDret (3.11) does copy the result back into a variable provided by the caller (RunReturn function)

Yes, that's the version I was congratulating you about! :-) The functions in the previous versions were certainly very nice (e.g. RunRedirect), but their syntax wasn't sufficiently streamlined to provide a "clean" alternative to RunWait.

Jacques.

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004

a VarSetCapacity option to place the variable's new size as a DWORD in its first 4 bytes? Would save the conversion work...)

I see that it would be useful, but the uses seem too obscure to justify cluttering the command with such an option. If after more thought you're convinced that the value would be substantial and have a broad range of applications, please let me know.

can I assume that the internal format of an AHK variable is a straight null-terminated string (within a pre-allocated buffer the size of which you keep track of separately and expand as needed?)

Yes. When you pass a variable by reference to a function (by means of a "str" argument type), the address of the variable's null-terminated buffer gets passed to the function. It's the function's responsibility (if it changes the string) to terminate the buffer and not to exceed the buffer size. After the function completes, the program updates the variable's length attribute in case the function changed the string.

I guess what I'm trying to confirm is that AHK variables don't have some sort of "header" structure containing their size, type, etc.

They do, but this info is not passed to the function, only the buffer is.

how were you planning to implement the reading/writing of binary files which I think I've seen you mention as a future possibility?

I'm not sure yet but it might expand on the special ClipboardAll variable, whose contents can be stored in ordinary variables as binary data. Also, it is already possible to read and write binary files using the example in this post: <!-- m -->http://www.autohotke... ... 2389#22389<!-- m -->

JBensimon
  • Members
  • 167 posts
  • Last active: May 08 2017 10:25 PM
  • Joined: 16 Nov 2004

If after more thought you're convinced that the value would be substantial and have a broad range of applications...

Probably not. If you think scripts should make no assumption about the contents of a variable after VarSetCapacity has been applied to it, then you might consider my suggestion as a default behavior. If on the other hand a variable's value should be expected to be an empty string after VarSetCapacity has been applied, then please ignore the suggestion.

Also, it is already possible to read and write binary files using the example in this post ...

Thanks, I'll check it out ... but I'm probably sticking to text files for now.

Jacques.

corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004

In any case, now that you've made such progress with CmdRet, I'd like to put the question whether I should proceed (when time allows) or whether you might be planning a similar more complete implementation of your new RunReturn call?


@ := "str" ; For readability
ExitCode := DllCall("AHKEx\RunWaitEx", @, "executable", @, "parameters", @, "workingdir", @, InVar, @, OutVar, @, ErrVar, "int")


A similar, more complete function could be implemented in the near future. With version 3.1.1 I was trying to simplify (and reduce the amount of) the code necessary to call the function. I am also planning to release the source for CMDret soon (BCX, C). You're welcome to write your own if you'd like though :).

:oops: Could you give an example that would use InVar?

Thanks Again :)

corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004

Eventually, a syntax will be devised to have CmdRet as a built-in feature: either a direct extension of the Run command or a separate helper command. Perhaps simply adding an OutputVar parameter to the end of the Run command is workable.

Adding an OutputVar parameter to the end of RunWait sounds workable. Any thoughts on syntax to stream into a control?

jonny
  • Members
  • 2951 posts
  • Last active: Feb 24 2008 04:22 AM
  • Joined: 13 Nov 2004
My two monetary units of low value:

Since most utilities that this would used for would, in theory, return a value, and since that value is often only needed once, I think this would be a good opportunity to implement a RunWait function. This would also make the syntax for assigning output to a variable more readable:

cmdOutput:=RunWait("tail.exe test.txt")

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004

If on the other hand a variable's value should be expected to be an empty string after VarSetCapacity has been applied, then please ignore the suggestion.

I think it's best that the variable be empty after the change (which it currently is). This allows the script to pass a string that is known to be empty and initialized, which at the very least adds peace of mind, and in some cases saves the extra step of having to make the variable blank. Now that I think about it, it's fortunate that the variable is made blank because manually setting it to blank afterward would free the memory you just allocated!

Any thoughts on syntax to stream into a control?

I haven't been paying enough attention to cmdret. I'll assume it has stream-to-control capability and I've made a note to consider how the syntax might work for that. Hopefully this can be an entirely separate task so that the integration into Run/RunWait isn't postponed by it. Thanks.

JBensimon
  • Members
  • 167 posts
  • Last active: May 08 2017 10:25 PM
  • Joined: 16 Nov 2004

Since most utilities [...] return a value [...]

cmdOutput:=RunWait("tail.exe test.txt")

Your suggestion is certainly compact, but I think the intuitive "result" of a console utility is its exit code, not its console output (because you often need to check the exit code before you can interpret the meaning of the output), as in
If (RunWait("command", OutVar)=0)
  MsgBox, 0, Output, %OutVar%
Else
  MsgBox, 48, Failure, An error was encountered.
Then there's the question of being able to set the working directory (often very important), of being able to distinguish between normal and error output (well designed console utilities make a careful distinction between the two output handles), of providing for the ability to also provide the utility's standard input (e.g. sort.exe, findstr.exe, etc.), and pretty soon we're back to a function (or AHK command) with multiple parameters. [I'm assuming here that we don't need to offer the usual Min, Max & Hide choices since we probably always want to hide the launched utility when we're trying to capture its output in a variable].

From the standpoint of readability, given than console utilities often take arguments that may require double-quotes (e.g. filenames), an extended version of the RunWait command seems "cleaner" than a new function. As we've been discussing recently, it would then be a simple matter for any AHK programmer who wants it to define a custom RunWait-like function that "wraps" the RunWait command, takes just the needed parameter(s), and returns exactly what the programmer wishes.

Just giving back some change on those two cents! :-)

Jacques.

JBensimon
  • Members
  • 167 posts
  • Last active: May 08 2017 10:25 PM
  • Joined: 16 Nov 2004

Could you give an example that would use InVar?

Happy to! Almost any so-called "filter" utility (a console utility that takes input from StdIn and sends output to StdOut) is a candidate. There are hundreds of filters in the scripting arsenal of Unix programmers (most of which have been ported to Windows by somebody or other), and many non-trivial tasks can be carried out by stringing together several filters, each one taking its input from the output of the previous one. You are probably already familiar with many simple filters, among which are more, head, tail, sort, find, grep, gsar (GNU Search And Replace), etc. One of the more useful filters found in all versions of Windows starting with NT is FindStr.exe, a much more powerful alternative to Find.exe because of its support for regular expressions (i.e. pattern matching).

So, just as a simple example, let's say you have an AHK variable called Requests, a line-feed separated list of strings typed by a user, and you only want to proces the strings in which the user said "please", i.e. strings in which the word "please" appears anywhere (the words "pleased" or "displease" would not be acceptable). In the hypothetical scenario where RunWait accepts new InVar and OutVar parameters, you could use the command
RunWait, FindStr.exe /i "\<please\>",, Hide,, Requests, PoliteRequests
and the PoliteRequests variable would now contain only those strings that contain the word "please" (case-insensitive). 8)

Jacques.

corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004
It seems that the download link is currently down for a few people so here's an Alternate Download :) .