How can I detect if a file is being used by another application, without writing to the file?
DO
STREAM("MyFilename", "C", "UPDATE")
notaccessed = 0
CATCH NOTREADY
notaccessed = 1
STREAM("MyFilename", "C", "CLOSE")
END
IF notaccessed THEN SAY "No app is using the file."
Is there a way to save a stem array into a file (with its values) when you do not know the tails? For example, I have:
MyVar.143 = 'This one' MyVar.Abc = 'Another one' MyVar.Last = 'Finally this one'
And what I need to do is create a file that looks like:
MyVar.143 This one MyVar.Abc Another one MyVar.Last Finally this one
You can do that using a DO OVER loop. DO OVER lists all the existing tail names of a particular stem. For more information, see the help page "The REXX Language -> Loops -> Repeat until a condition is met" (the DO OVER section).
This simple loop should do what you need:
DO i OVER myvar.
LINEOUT("MyFileName.txt", "MyVar." || i myvar.i)
END
I'm running into the same problem with the charout function as I had with lineout. If I write a file using charout, close it and then rewrite it with fewer characters, the file length does not get shorter. This script demonstrates it:
ofile = "otest.txt" ptext = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" CHAROUT(ofile,ptext,1) CHAROUT(ofile) SAY STREAM(ofile,'C','FSTAT') ptext = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi" /* deletefile(ofile) */ CHAROUT(ofile,ptext,1) CHAROUT(ofile) SAY STREAM(ofile,'C','FSTAT')
It produces this output which shows that the file is still 78 chars after being written a second time with a shorter string.
2 0 066 1 user group 78 2 0 066 1 user group 78
Removing the comment around the deletefile statement makes the program do what I was hoping for:
2 0 066 1 user group 78 2 0 066 1 user group 61
This time the file is the correct length.
Here, you're seeing some "default behavior" of REXX. When you don't explicitly open a file with STREAM, then REXX opens it for you when you do a file I/O operation, and uses a default "open mode" depending upon what file I/O you do.
Here, you're doing only a CHAROUT. So first of all, REXX uses an open mode that corresponds to "OPEN WRITE". You'll note that this means "if the file exists, retain its existing data". So, the second time you open the file, it still has all that old data in it. You're overwriting the data. Secondly, it should be noted that closing a file with CHAROUT does not truncate the data. That feature pertains only to LINEOUT. So you're also not truncating the data. You're merely overwriting some chars at the start of the file.
Conclusion: It's working as REXX is designed.
Solution: Use STREAM to open the file yourself so you can determine exactly what you want. You'll use "OPEN WRITE REPLACE" if you want all of the previous data deleted. (And note that when you open a file in this mode, the file position is automatically at 1. So you don't need to pass that position in your first call to CHAROUT).
I found the EDITNAME() built-in, but that only 'transforms' a name. It doesn't change the name of a file on disk. Any way to do the latter without deleting the file and creating a new one with the new name?
Reginald has a MOVEFILE function which does what you need.
I'm getting an error on a COPYFILE call. I'm enclosing the filenames in quotes because they have embedded spaces.
COPYFILE("'"||next_file||"'", "'"||usb_file||"'")
You don't need extra quotes around file names at all when using the Reginald built-in functions. It doesn't matter if the filenames have spaces. Remove the quotes (and your problem will go away).
I wrote subroutine that works on a file. It first gets the file's size with CHARS() and then reads it in with CHARIN().
I noticed that the first time I call the subroutine on a given file, it works. But if I then call the subroutine a second time on the same file, it doesn't work. I get a NOTREADY condition.
I discovered that adding a call to:
STREAM(file, 'C','CLOSE')
...at the end of my subroutine does the trick. So it seems that CHARS/CHARIN don't close the file properly, so that I can't re-open it a second time. I have to close it myself.
Right. If you don't explicitly close a file, it remains "open". If you've read to the end of the file, it remains open and "stuck" at the end (until you close it or reset the position). And that's the problem you're encountering.
You don't necessarily need to explicitly open the file with STREAM. But when you're done reading it, you need to either explicitly close it (with STREAM's CLOSE, or LINEOUT), or reset the position (if you want to leave the file open).
How can I query the version of any DLL/EXE?
The RxFuncQuery() built-in function can return the version of any DLL/EXE (that has version information). Pass the name of the file first, and a 'V' second. For example, to query the version of REXX GUI:
SAY "REXX GUI version =" RXFUNCQUERY('rexxgui', 'V')
You can query any of the DLL versions the same way:
SAY "REXX Sock version =" RXFUNCQUERY('rexxsock', 'V')
One caveat: If you happen to have a file named "rexxgui" in the current directory, it would try to read the version of that instead. So, to be on the safe side, you can add a ".DLL" extension:
SAY "REXX GUI version =" RXFUNCQUERY('rexxgui.dll', 'V')
Also, if you have several versions in different directories, then specify the full path to the version you want:
SAY "REXX GUI version =" RXFUNCQUERY('C:\MyDir\rexxgui.dll', 'V')
How can determine the name of the program (EXE) that opens a given type of data file (based upon its file association)?
FUNCDEF and call the FindExecutable() function as so:
/* Register FindExecutable() */
FUNCDEF('FindExecutable', 'void, str, void, str[260] stor', 'shell32')
/* Find the EXE that opens the file "C:\My Dir\File.txt" */
filename = "C:\My Dir\File.txt"
rc = FindExecutable(filename, , exename)
IF rc < 33 THEN OO
SELECT rc
WHEN 31 THEN SAY "No executable associated with this file."
WHEN 2 THEN SAY "The specified file was not found."
WHEN 3 THEN SAY "The system can't find the path specified."
WHEN 11 THEN SAY "The .EXE file is invalid (non-Win32 .EXE or error in .EXE image)."
OTHERWISE SAY "FindExecutable error"
END
END
ELSE SAY exename "is used to open" filename || "."
Assuming the "string" that you want to match doesn't span more than one line, and assuming a file will completely fit into memory, then your best bet is to do something like the following:
Here's an example:
/* This example searches for a string within some
* files in a directory. Note: The string can't
* span more than one line.
*/
/* Here's the string we search for. */
SAY "Enter the string to search for >";PULL searchfor
/* Here is the directory we search. The user is expected
* to type a directory and specify a filename
* pattern to filter. For example, he can type C:\MyDir\*.* to
* search all files in that directory. Or he can type
* C:\MyDir\*.rex to search only files that end in a .REX
* extension in that directory.
*/
SAY "Enter the directory to search >";PULL searchdir
/* You may prefer to use a GUI to get the above information.
* Typically, you'll present a dialog that will have an ENTRY
* box (labeled "Search for:") into which he can enter his
* search string. You'll have a TEXT control, labeled "Search
* in" displaying the last directory he searched (or the current
* directory if this is his first search). Next to this, you'll
* have a Browse button to click on, which presents a directory
* browser to pick out a different directory. And finally,
* you may wish to have a combobox for him to enter any
* filename filtering, for example "*.rex". You may have
* common filters such as *.TXT and *.* as items in the dropdown
* list he can conveniently choose, or let him enter his
* own choice in the combo box's ENTRY. Normally, you'd select
* *.* initially.
*/
/* Use MATCHNAME to iterate the files in the directory.
* Repeat until MATCHNAME tells us to stop.
*/
DO UNTIL err \= ""
/* Have MATCHNAME return the next filename */
err = MATCHNAME(, 'fn', searchdir,,'FN')
/* Did MATCHNAME return a name? */
IF err = "" THEN DO
/* Open the file. */
STREAM(fn, 'C', 'OPEN READ')
/* Get the size of the file. */
total = CHARS(fn)
/* Anything to read? */
IF total > 0 THEN DO
/* Read those characters. */
data = CHARIN(fn, , total)
/* Search for the string in the file */
end = POS(searchfor, data)
IF end \== 0 THEN DO
/* Found the string. Found is how far into the
* file it appears. You can search backward for
* either a '0D'X or '0A'X char to find the start
* of the line. And search forward for the same,
* to find the end of the line. If you want to
* display further lines, search backward/forward
* in a similiar fashion.
*/
start = end
DO WHILE start > 1
chr = SUBSTR(data, start - 1, 1)
IF chr == '0A'x | chr == '0D'x THEN LEAVE
start = start - 1
END
DO WHILE end < total
chr = SUBSTR(data, END + 1, 1)
IF chr == '0A'x | chr == '0D'x THEN LEAVE
end = end + 1
END
data = SUBSTR(data, start, end)
SAY data
END /* found \== 0 */
END /* total > 0 */
CATCH NOTREADY
CONDITION('M')
FINALLY
/* Close the file. */
STREAM(fn, "C", "CLOSE")
END /* MATCHNAME */
CATCH NOTREADY
CONDITION('M')
END
How can I print some files?
Reginald's IterateExe function allows performing "open" or "print" commands on just about any data file. For a "print" operation, you omit the first arg to IterateExe as so:
/* Print the text file "readme.txt" in the current directory */ err = ITERATEEXE(, "readme.txt") /* Print all text files in "C:\Windows" */ err = ITERATEEXE(, "C:\Windows\*.txt")
Note: Not all data files support a print command. For example, an audio file would not typically support a print command.
Virtually all files support an open command. When you issue an open command on a file, if the file is an EXE, then that EXE is run. If the file is a data file, then whatever EXE is associated with that data file is run, and the EXE typically loads that data file automatically. For example, if you perform an open command on a text file, it will typically open up in Notepad.
Reginald's IterateExe() uses the Windows OS function named ShellExecute to perform this "open" or "print" command. But there may be other commands that you can perform on a particular data file. And since Reginald's FUNCDEF allows you to register and call ShellExecute() yourself, then you may be able to perform these additional, special commands on a data file.
To see what other commands some object supports, move the mouse pointer over that Object's icon, and click the right mouse button once to display its pop up menu. The commands that object supports are listed first in the menu. For example, you'll typically see "Open" listed first, since most every object supports an open command. If the object supports the print command, then you'll see the word "Print" in the menu too. Some objects may support additional commands. For example, an audio or video file may support a "Play" command, which when issued will cause Media Player to run and play the file. Or a zip archive may have an "Unzip" command, which when issued will cause some utility to unzip the contents to the same directory as the zip archive. A folder icon has an "Explore" command which will cause an explorer window to open showing the contents of that folder.
And other objects may have other commands.
You can use ShellExecute to issue those commands yourself. For example, let's say you have an object named "C:\MyFile.zzz" that offers a "Play" command which you want to issue:
/* This example shows how to issue a command to an
* object. It uses Reginald's FUNCDEF feature to call
* the Windows operating system function ShellExecute.
*/
/* ============== FUNCDEF stuff ============ */
DO
/* Register ShellExecute() */
FUNCDEF('ShellExecute', 'void, void, str, str, str, str, 32u', 'shell32')
CATCH FAILURE
CONDITION('M')
RETURN
END
/* ************** Issue the command ************** */
/* We call ShellExecute() and specify a "play" operation.
*
* The first arg is the handle to some open window that you
* want ShellExecute to use if it needs to display a message
* box. Omit this arg to use the desktop.
*
* The second arg is the operation (ie, the command to issue).
* Here we are doing a "play" operation.
*
* The third arg is the name of the object to which to issue
* that command.
*
* The fourth arg is used only if you specified the name of an
* executable (ie, not some other object) as the third arg.
*
* The fifth arg is the name of the directory you want to be
* used as the default directory. Omit this if you wish the
* same directory as the object.
*
* The sixth arg is used only if you specified the name of an
* executable (ie, not some other object) as the third arg.
*
* If ShellExecute() succeeds, it returns the handle to the
* new process. If it fails, then it returns a value of 32 or less.
*/
result = ShellExecute(, 'play', 'C:\MyFile.zzz')
IF result <= 32 THEN DO
SELECT result
WHEN 0 THEN SAY "The operating system is out of memory or resources."
WHEN 8 THEN SAY "The operating system is out of memory or resources."
WHEN 2 THEN SAY "The specified file was not found."
WHEN 3 THEN SAY "The specified path was not found."
WHEN 5 THEN SAY "The operating system denied access to the specified file."
WHEN 26 THEN SAY "A sharing violation occurred."
WHEN 27 THEN SAY "The filename association is incomplete or invalid."
WHEN 28 THEN SAY "The DDE transaction could not be completed because the request timed out."
WHEN 29 THEN SAY "The DDE transaction failed."
WHEN 30 THEN SAY "The DDE transaction could not be completed because other DDE transactions were being processed."
WHEN 31 THEN SAY "There is no application associated with the given filename extension."
WHEN 32 THEN SAY "The specified dynamic-link library was not found."
OTHERWISE SAY "ShellExecute failed."
END
END
RETURN
I took a REXX script and created a self-running executable out of it, using RPC. Now I can drag an icon for some data file over the icon for my exe, and drop the icon there. My exe will start up, but how do I get the name of the data file?
Take the following script, make it into an exe with RPC, and drop an icon on it. (Then read the section of the REXX book entitled Getting input).
/* A script to check arguments passed to it */ numargs = ARG() SAY 'There are' numargs 'arguments passed to this script.' DO i = 1 TO numargs SAY 'Argument ' i '= "' || ARG(i) || '"' END
LoadText() is a very handy function. Is there a complementary Savetext() function which could save a stem variable to some file?
LoadText has an additional 'S' option you can use to save a stem variable to a text file. (So SaveText() is sort of built into LoadText() itself).
I find that I have to manually close the stream after a LoadText().
LoadText won't close a file that you left open by some previous call you made to Linein, Lineout, Charout, Charin, or Stream. You'll have to explicitly close the file yourself with STREAM's CLOSE command (or Lineout with no args), after calling LoadText.
But you do not have to open the file before you call LoadText. Just like Lineout, Linein, etc, LoadText will open the file for you if the file is not already open. And then LoadText will close the file after reading it in (if it wasn't previously open).
In conclusion, the only reason you would explicitly open the file before calling LoadText is if you planned to do several calls to LoadText in order to write/read several stems to/from the same file. And then, you'd need to manually close the file when done.
Is there any way to check whether a named process is running? Can I get at the list of processes shown in the task manager?
You can get a list of running tasks like this:
FUNCDEF("EnumProcesses", "32, 32[256] stor, 32, 32 * stor", "PSAPI")
FUNCDEF("GetModuleFileNameEx", "32u, void, void, char[260] stor, 32u", "PSAPI")
FUNCDEF("EnumProcessModules", "32, void, void stor, 32u, 32u * stor", "PSAPI")
FUNCDEF("OpenProcess", "void, 32u, 32u, 32u", "KERNEL32")
FUNCDEF("CloseHandle", "32u, void", "KERNEL32")
success = EnumProcesses(process_ids, 256 * 4, num_processes)
IF success == 0 THEN DO
SAY "Error enumerating processes:" success
RETURN
END
num_processes = num_processes / 4
DO i = 1 TO num_processes
process = OpenProcess(16 + 1024, 0, process_ids.i)
EnumProcessModules(process, module, 4, num_modules)
num_modules = GetModuleFilenameEx(process, module, file_name, 260)
IF num_modules \== 0 THEN SAY file_name
CloseHandle(process)
END
How can I capture the output from a DOS command (in this case NETSTAT) to process. The output is written to the command window and can't be written to a file directly.
For capturing the output of a console mode program, use the RXCMD add-on DLL, and pass a stem variable name for the OutputVariable arg, such as:
Command('netuser.exe', 'MyVar', , 'HIDE')
DO i = 1 TO myvar.0
SAY myvar.i
END