Passing a C# string to VBscript - c#

I am using a windows form and am trying to pass a string to a vbscript. The program is asking the user to select a folder, I am trying to take the folder selection and pass it through to the vbscript.
C# Code:
String SelectedFolder = #"C:\Users";
folderBrowserDialog1.SelectedPath = SelectedFolder;
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
//Set selectedFolder equal to the folder that was choosen
SelectedFolder = folderBrowserDialog1.SelectedPath;
//Call VBScript
System.Diagnostics.Process.Start(".vbsPath");
VBScript:
TargetFolder = Request.QueryString("SelectedFolder")
Set objShell = CreateObject("Shell.Application")
Set objFolder = objShell.Namespace(TargetFolder)
Set colItems = objFolder.Items
For Each objItem in colItems
objItem.InvokeVerbEx("Print")
Next
Any help would be greatly appreciated. Thank you

C# side
Use the Process.Start overload that accepts command-line parameters:
System.Diagnostics.Process.Start("C:\path\to\my.vbs", selectedFolder);
If the selectedFolder can contain spaces (which is likely to happen), you should enclose the argument in quotes.
System.Diagnostics.Process.Start("C:\path\to\my.vbs",
"\"" + selectedFolder + "\"");
In fact, if the path can contain quotes and/or trailing backslashes, escaping gets a lot more complicated, see these questions (and others) for details: Escape command line arguments in c#, Passing command-line arguments in C#.
VBScript side
In your VBScript, read the first command line parameter:
targetFolder = WScript.Arguments.Item(0)

You could write the string to a file which both sets of code can access, or a database. That way it won't matter which programming language you are using at all e.g. could be C# to PHP.
Write file:
string content = "folder=" + folder;
System.IO.StreamWriter file = new System.IO.StreamWriter(#"c:\config.txt");
file.WriteLine(content);
file.Close();
Read file:
System.IO.StreamReader file = new System.IO.StreamReader(#"c:\config.txt");
string content = file.ReadToEnd();
// extract value of folder setting here
file.Close();
(Of course the reading would need to be in VB, but would be very similar. Note: code based on: MSDN Example)

Going back and forth from VBScript to C# is adding a lot of complexity. If possible, it would really be easier to choose one or the other.
You can do anything in C# that you can in VBScript. (How you do it is probably different, but you can do all the same tasks - file access, database access, etc.) If it's feasible, you may be better off just working in C#.
I'm doing a bit of a guess based on the context of the question here, but I'm trying to answer in my own head why you'd want to do this, and the only thing I can think of is that you don't know how to display a folder dialog box in VBScript, so you're resorting to trying to do it in C#. Is that correct?
If so, you can show a folder dialog in VBScript as shown here: http://www.robvanderwoude.com/vbstech_ui_browsefolder.php

Related

Can you run an embedded .vbs file from a C# Windows Form?

I have been looking all over but cannot find a definitive answer/solution, or any solution I try fails. I am making a WinFormApp that calls an embedded .vbs file to script a program I use at work. This is what my Project Solution looks currently:
Project Solution
The test.vbs file is set as an embedded resource in its file properties. Here is the different code I have tried to use to run the script from the form:
private void button1_Click(object sender, EventArgs e)
{
string path = Path.Combine(Path.GetTempPath(), "test.vbs");
File.WriteAllBytes(path, Properties.Resources.Test);
Process.Start(path);
string path2 = Path.Combine(Path.GetTempPath(), "test.vbs");
System.Diagnostics.Process.Start(#"cscript //B //Nologo " + path2 + "");
}
Here is what my test.vbs file is:
dim thing
thing = "It did something!"
Wscript.Echo thing
The test.vbs file is primarily meant as a proof of concept to make sure I can at least run a .vbs file. The test.vbs file compiles fine outside of the WinForm.
Most of the time I receive 'The system cannot find the file specified' as the error message. I have read that it may be easier to just convert my .vbs file to C# but they are GUI scripting for SAP and all of SAP's libraries seem primarily set us for .vbs files.
I am still relatively new to C# so I may be way off with this so please tell me if I am. If there is another question that fixes my issue please link it.
Thank you for your time!
EDIT #1 Code compiles and seems to run.
string path2 = Path.Combine(Path.GetTempPath(), "test.vbs");
var startInfo = new ProcessStartInfo
{
WorkingDirectory = "" + path2 + "",
FileName = #"cscript"
};
However the script does not seem to be outputting to the cmd...
Your process start command is incorrect. Here is the correct version
System.Diagnostics.Process.Start("cscript", #"//B //Nologo " + path2 + "");

C# and ExtendScript for batch processing of .incx files

I have a ton of .incx text documents clustered into their own individual subfolders that I need to iterate through and convert to plaintext as part of a C# winform app I've created. I have the latest version of InCopy and the ExtendScript Toolkit, and a .jsx script that works great to quietly and quickly create my plaintext files.
My problem/question is that there isn't much guidance on how to best launch this from within a C# class in a running 3rd party app, sending in relevant info. When I run my .jsx script, I need to send it a target folder from my app where it can find the .incx files.
The target folder(s) will be dynamic depending on other previous actions in my app.
I've found a few vague hints to solutions on Adobe's forums involving additional .vbs files and/or external temp files to hold arguments, but they're all pretty dated, so I thought I'd ask and see if anyone knew of a contemporary method. If anything is unclear, I'll respond right away to clarify.
Through a lot more Googling and my own trial and error, I have found my answer.
The best way I can find is to do all of my InCopy scripting in VBS and then use a Process instance to send in my arg(s) with cscript.
Example C#:
Process myScriptProc = new Process();
myScriptProc.StartInfo.FileName = #"cscript";
myScriptProc.StartInfo.WorkingDirectory = rootDir + "\\"; // rootDir being the path where my vbs lives
myScriptProc.StartInfo.Arguments = "MyScript.vbs " + filesPath; // filesPath is the arg sent to the script
myScriptProc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
myScriptProc.Start();
myScriptProc.WaitForExit();
myScriptProc.Close();
MyScript.vbs
main
Function main()
Set myInCopy = CreateObject("InCopy.Application.CC.2015")
Set obj = CreateObject("Scripting.FileSystemObject")
myInCopy.ScriptPreferences.UserInteractionLevel = 1699640946
myFormat = 1952412773
myExtension = ".txt"
Set objFSO = CreateObject("Scripting.FileSystemObject")
objStartFolder = WScript.Arguments(0)
Set objFolder = objFSO.GetFolder(objStartFolder)
Set colFiles = objFolder.Files
For Each x In colFiles
If LCase(objFSO.GetExtensionName(x.name)) = "incx" Then
thisDoc = x
Set myDoc = myInCopy.open(thisDoc)
Set myStory = myInCopy.ActiveDocument.Stories.Item(1)
parts = split(x.Name, ".")
myFilePath = objStartFolder & "/" & parts(0) & myExtension
myStory.Export myFormat, myFilePath
myDoc.close()
obj.DeleteFile(thisDoc)
End If
Next
myInCopy.ScriptPreferences.UserInteractionLevel = 1699311169
End Function
I rewrote my JavaScript file in VBScript because judging from the tumbleweeds blowing through the Adobe forums, I was never going to get any answers as to why their documentation examples for calling DoJavaScriptFile produce object missing method errors.
The biggest hurdle I ran into after redoing my script in VB was that you have to use the super-secret enumerated decimal values for Adobe-specific things if you run the scripts externally. If you look at MyScript.vbs you'll see a few instances of what look like random 10 digit values. Those come from here:
http://jongware.mit.edu/idcs5js_html_3.0.3i/idcs5js/index_Enum%20Suite.html
Bless the guy who created that resource, because I couldn't find that information in any of Adobe's documentation to save my life.
TL;DR: If you're trying to automate using processes and scripts that run outside an Adobe app, do everything in VBScript, and beware the mystery decimal enumerations.
useless footnote:
MyScript.vbs here reads all *.incx files from the passed in directory, exports as plain .txt (with the same filename, into the same dir), and deletes the original.

C# programmatically creating shortcut to directory does not always work

I am trying to programatically create a shortcut to a directory. I have found numerous examples, but none appear to work realiably.
I observe three different results in the produced shortcut's properties:
The Shortcut Type of File is assigned as "Shortcut(.lnk)" which cause the Open With dialog box to pop up asking me to attach an extension to it.
The Shortcut Type of File property is assigned as "File" which does absolutely nothing when double clicked.
Or lastly which is of course my favorite... the Shortcut Type of File property is assigned as: "File Folder" which works like it should.
Here is the code I am currently using... I've tried a few variations of this.
bool IsExists = false;
string icon = appPath + "Welcome.ico";
// Their is a difference to local and ClickOnce App locations... this compensates for it
IsExists = System.IO.File.Exists(icon);
if (!IsExists)
{
icon = appPath + #"bin\Debug\Welcome.ico";
}
var desktop = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
var target = (Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + #"\Artronix\Welcome To FreightWare Online\").Replace(#"\","/");
IWshShortcut shortcut;
var wshShell = new WshShellClass();
shortcut = (IWshShortcut)wshShell.CreateShortcut(Path.Combine(desktop, #"FreightWare Resources.lnk"));
shortcut.IconLocation = icon;
shortcut.TargetPath = Path.GetFullPath(target);
shortcut.Save();
Thank you everyone for your help... I figured it out. I didn't want to post it as an answer, but thought just in case someone else happened to come across this same problem... Although I feel sheepish about my oversight.
It turns out there was nothing wrong with the code. Panhandel gave me a clue to where to find the solution when he made the statement: " I only achieved the first result when the target path didn't exist." Since he was always getting the correct result and he only got the results I was getting when the directory did not exist... I realized the problem may be that I create the directory programatically in one line then in the next create the icon... I needed to give the system more time for the directory to be fully created
Try ShellLink:
using (ShellLink shortcut = new ShellLink())
{
shortcut.Target = Application.ExecutablePath;
shortcut.WorkingDirectory = Path.GetDirectoryName(Application.ExecutablePath);
shortcut.Description = "My Shorcut Name Here";
shortcut.DisplayMode = ShellLink.LinkDisplayMode.edmNormal;
shortcut.Save("%HOMEPATH%/Desktop/");
}
I had a slight variant on this issue...
I eventually discovered an additional condition:
The target path has to point at a folder when the shortcut is created (as discussed above)
But also - It appears the target path that's provided also has to be normalized - eg via Path.GetFullPath(possiblyUnNormalizedPath) see
How can one get an absolute or normalized file path in .NET?
Hope this might help someone in the distant future avoid wasting an hour of their life.

Managing absolute path and full path

I've created a small program wich can read a .txt file.
This file contains a link to another file in this format new_file.txt
The goal is to return the path of the new file, so basically I'm doing this :
String newFileName = getFileName();
int index = oldFilePath.lastIndexOf('\\');
String path = oldFilePath.substring(0, index + 1);
String newFilePath = path + newFileName;
return newFilePath;
For example :
The first file I opened is : C:\a\b\c\oldFile.txt
In this file I found newFile.txt
So the new path will be : C:\a\b\c\newFile.txt
Nice, but what If I find something like this :
..\ or .\.\ or ...
Is there any way to automate this mess ?
Thanks
In C#/.Net you have the rather cool Path class.
You can use Path.GetFullPath( string pathname ) to resolve paths e.g. with \..\ etc in them.
Use Path.GetDirectory(), Path.GetFileName(), Path.GetFileNameWithoutExtension() & Path.GetExtension() to pull names apart and Path.Combine() to put them back together again.
You've tagged this as java as well as c#
In java look at FileNameUtils http://commons.apache.org/io/apidocs/org/apache/commons/io/FilenameUtils.html
The normalize method should help

Securely enforcing user-input file paths within subdirectories

I know the solid security recommendation of avoiding accepting user input that you then use to choose a path to read/write a file. However, assuming you have a base directory you want to keep within (such as the root of an ftp folder), how do you best ensure that a given user input keeps us within that folder?
For instance,
Path.Combine(_myRootFolder, _myUserInput)
could still take us outside of _myRootFolder. And this could also be dodgy
newPath = Path.Combine(_myRootFolder, _myUserInput)
if (newPath.StartsWith(_myRootFolder))
...
given something like "/back/to/myrootfolder/../../and/out/again" from the user. What are the strategies for this? Am I missing a blindingly obvious .NET method I can use?
Within ASP.NET applications you can use Server.MapPath(filename) which will throw an exception if the path generated goes outside of your application root.
If all you want is a safe file name and you just want all files in there it becomes simpler;
FileInfo file = new FileInfo(
Server.MapPath(
Path.Combine(#"c:\example\mydir", filename)));
If you're outside of ASP.NET like you indicate then you could use Path.GetFullPath.
string potentialPath = Path.Combine(#"c:\myroot\", fileName);
if (Path.GetFullPath(potentialPath) != potentialPath)
// Potential path transversal
Or you call Path.GetFullPath and then check the start of it matches the directory you want locked to.
I know, that this thread is quiet old, but to prevent following readers from writing code with potential security errors, I think I should point out, that using Path.Combine(arg1, arg2) isn't save when arg2 is directly based on user input.
When arg2 is for example "C:\Windows\System32\cmd.exe" the arg1 parameter will be completely ignored and you grant the users of your API or server application full access to the whole file system.
So please be very careful with using this method!
I came up with this solution that should (afaik) be secure:
public static string SecurePathCombine(params string[] paths)
{
string combinedPath = "";
foreach (string path in paths)
{
string newPath = Path.Combine(combinedPath, path);
if (!newPath.StartsWith(combinedPath))
return null;
combinedPath = newPath;
}
if (Path.GetFullPath(combinedPath) != combinedPath)
return null;
return combinedPath;
}
Edit: There is a new Path.Join() method now. Please use that one instead of the code above.
I believe Path.FullPath will do what you need (I didn't test this though):
string newPath = Path.Combine(_myRootFolder, _myUserInput);
string newPath = Path.FullPath(newPath);
if (newPath.StartsWith(_myRootFolder)) ...
Well, in your example of an FTP server, you should set the users home-directory, and permissions appropriately, such that they can't navigate out of the folder. Any reason you can't do that?
You can parse input string and cut ../ with regex.

Categories

Resources