How to convert DM-script files (*.s) into a plugin file (*.gtk) - dm-script

I have written some DM-script files (.s), and thus I would like to convert (compile?) them into plugin files (.gtk). In this case, should I prepare some kind of compiler and Gatan Software Development Kit (SDK) package, or others? If there is a good way, please teach the procedure in detail. I will be really grateful, if you share some wisdom to create *.gtk files.

.GTK files are not compiled - you will not gain any speed benefit of having them in a .gtk (or .gt1, .gt2 ) file. They are only packed into those files for convenience of sharing and distributing them easily without sharing source code.
That said, the way to "pack" scripts into those files is to use a script-command itself, i.e. write a script which build these files from a set of .s files on the hard drive.
The command to add a script-file to a script package is AddScriptFileToPackage with the syntax:
void AddScriptFileToPackage( String file_path, String packageName, Number packageLevel, String packageLocation, String command_name, String menu_name, String sub_menu_name, Boolean isLibrary )
void AddScriptFileToPackage( String file_path, String packageName, Number packageLevel, String command_name, String menu_name, String sub_menu_name, Boolean isLibrary )
The command to add a script (string) to a script package is AddScriptToPackage with the syntax:
void AddScriptToPackage( String script, String packageName, Number packageLevel, String packageLocation, String command_name, String menu_name, String sub_menu_name, Boolean isLibrary )
void AddScriptToPackage( String script, String packageName, Number packageLevel, String command_name, String menu_name, String sub_menu_name, Boolean isLibrary )
The paramters in the two commands are:
Note, that the created plugin-file will by default appear in the user_plugin location:
C:\Users\USERNAME\AppData\Local\Gatan\Plugins
The second syntax allows specifying the path, where the packageLocation parameter might be any of the names also accepted in the command GetApplicationDirectory, most often either being user_plugin (see above) or just plugin referring to the current run DigitalMicrograph.exe relative plugin folder, i.e.
.\Plugins relative to where the DigitalMicrograph.exe sits, typically
C:\Program Files\Gatan\Plugins\
Note, that one can append scripts to an existing file, but can not "unistall" one from it. In that case, one has to delete the file and re-create it anew.
Also, if an error occurs in the to-be-added scripts, they will not install correctly and you might need to start over.
The F1 help documentation (of later GMS versions) has an example script showing how to typically use the command:
The difference between libary and command install is the same as when using the File-Menu command to "install a script". A library usually is a set of methods (or classes) which, when installed, remain available and in memory. The command installs a script as a menu-command to the UI. i.e. selecting the menu executes the scirpt (once).
While the "File/Install Script" install things into the general prefereces-file of DigitalMicrograph, the commands above create separate .gtk files which load on startup if found in the plugins-folder. Any code that is installed as "library" is run once on startup.

Related

How can I run a bat file with spaces in the filepath?

I want to run a bat file used to compile sass to css from within a Kotlin program, on a Windows machine. I had everything working using Runtime.exec until I switched to a Windows account that had a space in the username. From what I read, I read that using ProcessBuilder would make this easier. It seems that even with ProcessBuilder I still can't get it to work, no matter what I try.
Here is my code so far
val commands = mutableListOf(
"cmd",
"/c",
"C:\\Users\\John Doe\\VCS\\test\\tools\\sass\\windows\\dart-sass\\sass.bat",
"--no-source-map",
"C:\\Users\\John Doe\\VCS\\test\\src\\main\\sass\\global.scss",
"global.css"
)
val processBuilder = ProcessBuilder(commands)
val process = processBuilder.start()
...
The error I get is 'C:\Users\John' is not recognized as an internal or external command, operable program or batch file. It doesn't help if I surround the strings that have spaces with \".
If I remember correctly, all windows files and folders that have a space in the name have a matching short name in the old 8.3 format replacing additional space and other characters with a tilde (~) and a number.
So whatever is returning you the path for the .bat and .sscs files could return the full filename in that format?
Doesn't solve the problem but avoids it instead, I admit.
Also means you won't get busted when someone puts a space in the filename (OK, unlikely, but still something better to deal with from the start).
Consider something along the lines of the top 2 answers on this superuser thread
This is actually a Windows cmd issue. The question here shows that in cmd, in addition to quoting the file paths, you also have to quote the entire part of the command line text after the /c switch.
I don't know if this is the best way to do it in ProcessBuilder, but I was able to get it to work with the following code.
"cmd.exe",
"/c",
"\"\"C:/Users/John Doe/VCS/test/tools/sass/windows/dart-sass/sass.bat\" "
+ "--no-source-map "
+ "\"C:/Users/John Doe/VCS/test/src/main/sass/global.scss\" "
+ "\"global.css\"\""

How can i override a default variable from CMakeLists.txt on CMake command line?

I have a .cmake file containing default values for variables. The .cmake file is called from CMakeLists.txt using 'include'.
One of the variables is a version number.
What is the best practice or setup to overrule that variable version number from CMake command line?
In gnu make you could use
var ?= value
where you can set var value on make command line. I do not see something similar in C-Make.
For boolean values, you can use option:
option(CUSTOMIZABLE_VAR "This variable do stuff" "default-value")
Also GUI apps exposing options such as CMake GUI or QtCreator you'll get a the description and a field to edit it.
For value of type string, you can set a cache value with a help string:
set(CUSTOMIZABLE_VAR "8" CACHE STRING "This option is a string")

How To Compress Folder-Contents in 1 Statement on Windows?

I'm attempting to zip a folder containing subfolders and items, using Windows shell CopyHere command:
https://msdn.microsoft.com/en-us/library/windows/desktop/bb787866(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ms723207(v=vs.85).aspx
Update: Note, prefer a native solution-- this is for a distributed Excel VBA tool, so bundling 3rd-party files is not ideal. And, need synchronous compression.
I can easily add a folder and its contents to the zip:
oShell.Namespace(sZipPath).CopyHere "C:\My Folder"
So we know CopyHere can process multiple objects inside a folder in 1 statement.
The problem is, the above command puts the containing-folder at the root of the zip, and it's contents inside of it. But, i don't want the containing folder-- just its contents.
The doc mentions a wildcard (option 128), but when i use a wildcard, i get an error:
oShell.Namespace(sZipPath).CopyHere "C:\My Folder\*"
The file name you specified is not valid or too long.
Perhaps there's a way to use my 1st command above, and then move the items in the zip to the root of the zip?
It would be acceptable to loop through each item in the source folder, adding one at a time to the zip. But, because CopyHere is asynchronous, each subsequent CopyHere fails if the previous CopyHere is not finished. None of the fixes work for this issue:
Comparing number of items in source-folder and destination-zip fail, because if the zip contains a folder, that counts as only 1 item (the items it contains are not counted. https://stackoverflow.com/a/16603850/209942
Waiting a while between each item works, but a timer is unacceptable: it's arbitrary. I cannot guess in advance the size or compress-time of each object.
Checking to see if the zip is locked for access failed for me. If I block my loop until the file is not locked, I still get a file-access error. https://stackoverflow.com/a/6666663/209942
Function FileIsOpen(sPathname As String) As Boolean ' true if file is open
Dim lFileNum As Long
lFileNum = FreeFile
Dim lErr As Long
On Error Resume Next
Open sPathname For Binary Access Read Write Lock Read Write As #lFileNum
lErr = Err
Close #lFileNum
On Error GoTo 0
FileIsOpen = (lErr <> 0)
End Function
Update: VBA can call shell commands synchronously (instead of creating a shell32.shell object in VBA), so if CopyHere works on command-line or PowerShell, that could be the solution. Investigating...
Automating Shell objects really isn't a viable approach as you have already discovered. The Explorer Shell doesn't really expose this capability in any other manner though, at least not before Windows Vista and then not in any fashion easily used from VB6 programs or VBA macros.
Your best bet is a 3rd party ActiveX library, but be careful about 64-bit VBA hosts where you'll need a 64-bit version of such a library.
Another option is to acquire a later copy of the zlibwapi.dll and use some VB6 wrapper code with it. This is also a 32-bit solution.
That's what Zipper & ZipWriter, Zipping from VB programs does. Considering your requirements (which for some reason includes a fear of the Timer control) you could use the synchronous ZipperSync Class. See post #4 there. That code includes a simple AddFolderToZipperSync bundling up the logic to add a folder instead of just a single file.
The downside of the synchronous class is that a large archival operation freezes your program UI until it completes. If you don't want that use the Zipper UserControl instead.
You could also take the ideas from that to write your own wrapper class.
Solution:
Windows contains another native compression utility: CreateFromDirectory at a PowerShell prompt.
https://msdn.microsoft.com/en-us/library/system.io.compression.zipfile.createfromdirectory(v=vs.110).aspx
https://blogs.technet.microsoft.com/heyscriptingguy/2015/03/09/use-powershell-to-create-zip-archive-of-folder/
This requires .Net 4.0 or later:
> Add-Type -AssemblyName System.IO.Compression
> $src = "C:\Users\v1453957\documents\Experiment\rezip\aFolder"
> $zip="C:\Users\v1453957\Documents\Experiment\rezip\my.zip"
> [io.compression.zipfile]::CreateFromDirectory($src, $zip)
Note, you may have to provide the complete pathnames-- active directory was not implicit on my machine.
The above compression is synchronous at the PowerShell prompt, as the OP requests.
Next step is executing synchronously from VBA. The solution there is the .Run method in Windows Script Host Object Model. In VBA, set a reference to that, and do the following, setting the 3rd parameter of .Run command, bWaitOnReturn to True:
Function SynchronousShell(sCmd As String)As Long
Dim oWSH As New IWshRuntimeLibrary.WshShell
ShellSynch = oWSH.Run(sCmd, 3, True)
Set oWSH = Nothing
End Function
Now call SynchronousShell, and pass it the entire compression script.
I believe the only way for this process to work is if CreateFromDirectory is executed in the same session as Add-Type.
So, we must pass the whole thing as 1 string. That is, load all 4 commands into a single sCmd variable, so that Add-Type remains associated with the subsequent CreateFromDirectory. In PowerShell syntax, you can separate them with ;
https://thomas.vanhoutte.be/miniblog/execute-multiple-powershell-commands-on-one-line/
Also, you'll want to use single-quotes instead of double-quotes, else double quotes around the strings are removed when the daisy-chained commands are passed to powershell.exe
https://stackoverflow.com/a/39801732/209942
sCmd = "ps4 Add-Type -AssemblyName System.IO.Compression; $src = 'C:\Users\v1453957\documents\Experiment\rezip\aFolder'; $zip='C:\Users\v1453957\Documents\Experiment\rezip\my.zip'; [io.compression.zipfile]::CreateFromDirectory($src, $zip)"
Solved. The above constitutes the complete solution.
Extra info: Additional comments below are for special circumstances:
Multi-version .Net environments
If a .NET < 4.0 is the active environment on your OS, then System.IO.Compression does not exist-- the Add-Type command will fail. But if your machine has the .NET 4 assemblies available, you can still do this:
Create a batch file which runs PowerShell with .Net 4. See https://stackoverflow.com/a/31279372
In your Add-Type command above, use the exact path to the .Net 4 Compression assembly. On my Win Server 2008:
Add-Type -Path "C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.IO.Compression.FileSystem\v4.0_4.0.0.0__b77a5c561934e089\System.IO.Compression.FileSystem.dll"
Portability
Turns out, on my machine, I can copy the compression dll to any folder, and make calls to the copy and it works:
Add-Type -Path "C:\MyFunnyFolder\System.IO.Compression.FileSystem.dll"
I don't know what's required to ensure this works-- it might require the full .Net 4.0 or 2.0 files to be located in their expected directories. I assume the dll makes calls to other .Net assemblies. Maybe we just got lucky with this one :)
Character Limit
Depending on the depth of our paths and filenames, character-count may be a concern. PowerShell may have a 260-character limit (not sure).
https://support.microsoft.com/en-us/kb/830473
https://social.technet.microsoft.com/Forums/windowsserver/en-US/f895d766-5ffb-483f-97bc-19ac446da9f8/powershell-command-size-limit?forum=winserverpowershell
Since .Run goes through the Windows shell, you also have to worry about that character limit, but at 8k+, it's a bit roomier:
https://blogs.msdn.microsoft.com/oldnewthing/20031210-00/?p=41553
https://stackoverflow.com/a/3205048/209942
Site below offers a 24k+ character method, but i've not studied it yet:
http://itproctology.blogspot.com/2013/06/handling-freakishly-long-strings-from.html
At minimum, since we can put the dll wherever we like, we can put it in a folder near C: root-- keeping our character-count down.
Update: This post shows how we can put the whole thing in a script-file, and call it with ps4.cmd. This may become my preferred answer:
.\ps4.cmd GC .\zipper.ps1 | IEX
-- depending on answer here.
CopyHere:
Re the question: can CopyHere command execute on command-line?
CopyHere can be executed directly at PowerShell prompt (code below). However, even in powershell it's asynchronous-- control returns to PowerShell prompt before the process is finished. Therefore, no solution for the OP. Here's how it's done:
> $shellapp=new-object -com shell.application
> $zippath="test.zip"
> $zipobj=$shellapp.namespace((Get-Location).Path + "\$zippath")
> $srcpath="src"
> $srcobj=$shellapp.namespace((Get-Location).Path + "\$srcpath")
> $zipobj.Copyhere($srcobj.items())

Launching exe with parameters containing whitespace from vb.net

What I need to do:
I have a path (copied from a working shortcut) to an .exe that I need to launch from my vb.net application. Following Path:
"C:\Program Files (x86)\Citrix\ICA client\pnagent.exe" /CitrixShortcut: (2) /QLaunch "Farm1:ADS # Citrix"
My problem:
I cannot figure out how to do this. My best guess was
Process.Start("cmd", "/k " & path)
But that always ends up with cmd giving back
'C:\Program' is not recognized as an internal or external ...
I know this is due to the path not being properly escaped, but I can't figure out how to do it properly. I know about using Double-Doubblequotes ("") but I couldn't get that work either.
If anyone can point me to a maybe better way to do this than Process.Start() I'd be very happy aswell!
I think the Process.Start() overload you want is
Process.Start(program, args).
... which would give you
Process.Start("C:\Program Files (x86)\Citrix\ICA client\pnagent.exe", "/CitrixShortcut: (2) /QLaunch ""Farm1:ADS # Citrix"" ")
Edit / Clarification:
cmd /k launches the command shell (aka "DOS prompt") but keeps it open after running the specified command (as opposed to cmd /c which executes the command and then closes the shell).
You typically only start cmd if you want to execute a shell built-in like DIR or COPY.
Dim MyProcess as string = "C:\Program Files (x86)\Citrix\ICA client\pnagent.exe"
Dim MyArgs as string = "/CitrixShortcut: (2) /QLaunch ""Farm1:ADS # Citrix"""
Process.Start(MyProcess, MyArgs)

Providing input files during compilation

To run a CUDA C program we build the program and then run the binary file created from the command line as
/.prgm_bin_file
If for example the program needs some input files like for programs to image processing, I want to supply the data files or the input files at the time of compilation.
How can I do that. How the above command can be edited to give the required files.
Thanks in advance.
If your program opens data files to use for input, it's using some file I/O API to do so. For example, one possible method is to use fopen.
Just to use it as an example, if you are using fopen, it expects a filename (a character string) passed as the first parameter.
Many programs will take this filename from a the command line used to invoke the program. But there's nothing that would prevent you from hard-coding the filename:
fp=fopen("mydata", "r");
In that case, the program would always attempt to open the file mydata
But if your program is already designed to use the filename as a command line parameter, it's not clear that this is any more useful than just invoking your program that way:
./prgm_bin_file mydata