Run SAS from VBA with full access - vba

I hope someone can help. I'm trying to build a subroutine to launch SAS from VBA. I've been doing so with SAS Workspace Manager. As I'm strolling pretty far from my comfort zone I've followed mostly what I found in these two sources. Here's what I came up with so far in VBA:
Public Sub SubmitSasProg(usrid As String, passid As String, path As String, sasprog As String, varinput As String, Optional logreturn)
Dim obWsMgr As New SASWorkspaceManager.WorkspaceManager
Dim obSAS As SAS.Workspace
Dim xmlInfo As String
Set obSAS = obWsMgr.Workspaces.CreateWorkspaceByServer("Local", VisibilityProcess, Nothing, usrid, passid, xmlInfo)
Dim obStoredProcessService As SAS.StoredProcessService
Set obStoredProcessService = obSAS.LanguageService.StoredProcessService
obStoredProcessService.Repository = "file:" & path
obStoredProcessService.Execute sasprog, varinput
If IsMissing(logreturn) Then logreturn = 100000
MsgBox obSAS.LanguageService.FlushLog(logreturn)
End Sub
And I have myself a little SAS program, let's call it "Test.sas":
%let loopTimes=3;
*ProcessBody;
data a;
do x= 1 to &loopTimes;
y=x*x*x;
output;
end;
run;
Now this line will work juste fine:
Call SubmitSasProg("myuserid", "mypassword", "somepath", "Test", "loopTimes=10")
But whenever I try to execute a SAS proceedure that modifies files/libnames etc. I'll get a either a "Invalid operation for this SAS session" or "User does not have access".
Please note that I'm using SAS locally, not on a server.
So I'm guessing that I'm not correctly logged in with my SAS worksession and lack permission. I thought that the userId and password parameters in CreateWorkspaceByServer are supposed to log me in.
So my question would be how to succesfully start the SAS session with my credentials on my local computer and have all the normal access I'd have by opening the windowed environement.
Just to clarify, this SAS process would work fine in the windowed environement:
Data _NULL_;
*x del C:\WINDOWS;
x mkdir C:\Users\Myname\Desktop\NewFolder;
run;
But it would fail with an "Invalid operation for this SAS session" code if started from VBA.
Something similar happens if I try to write SAS datasets.
I've been looking for some time now but most threads are about SAS server sessions.
Any help would be appreciated.

Well, it looks like most of my problems stem from the fact that I'm not an admin on my system.
I have managed to write datasets with the above technique given that I have write privileges on the directory with my account (duuh). Allas, the x command is stubborn and won't work lest I get myself an admin account. So, two choices to «solve» this problem:
1. Get administrator privileges.
or
2. Forget the workspace manager. Use OleObjects.
While less elegant and perhaps more time consuming upon execution (not tested) OleObject will let me use SAS to its full extent. Here is the VBA code:
Dim OleSAS As Object
Set OleSAS = CreateObject("SAS.Application")
OleSAS.Visible = True
OleSAS.Top = 1
OleSAS.Title = "Automation Server"
OleSAS.Wait = True
OleSAS.Submit(yourSAScode)
OleSAS.Quit
Set OleSAS = Nothing
If you want to run a specific process and change some macro variables as with *ProcessBody; just do OleSAS.Submit("%let "& variable_name & "=" & "yourValue") and OleSAS.Submit("%include" & your_program).
Anyway, I'm pretty sad of loosing the Log report return that I had with the Worspace Manager, it was really great for fast debugging.
Hope this was usefull.

I know that previously I've had experienced issues with SAS/Intrnet not having the required security policies setup. I can't guarantee that this will fix it, but it's somewhere to start looking:
Control Panel->Administrative Tools->Local Security Policy
Security Settings->Local Policies->User Rights Assignments->Log on as a batch job
Make sure that the account that SAS will be using to run the jobs has this right.
Also, are you saying that your test.sas program fails because it tries to write a dataset?
Also, you may be experiencing some of the same issues I've had here:
Using SAS and mkdir to create a directory structure in windows

Related

JSR 352 : How do you write to a MVS Dataset from a Java Batch program?

I need to write to a non-VSAM dataset in the mainframe. I know that we need to use the ZFile library to do it and I found how to do it here
I am running my Java batch job in the WebSphere Liberty on zOS. How do I specify the dataset? Can I directly give the DataSet a name like this?
dsnFile = new ZFile("X.Y.Z", "wb,type=record,noseek");
I am able to write it to a text file on the server itself using Java's File Writers but I don't know how to access a mvs dataset.
I am relatively new to the world of zOS and mainframe.
It sounds like you might be asking more generally how to use the ZFile API on WebSphere Liberty on z/OS.
Have you tried something like:
String pdsName = ZFile.getSlashSlashQuotedDSN("X.Y.Z");
ZFile zfile = new ZFile(pdsName , ...options...)
As far as batch-specific use cases, you might obviously have to differentiate between writing to a new file that's created for the first time on an original execution, as opposed to appending to an already-existing one on a restart.
You also might find some useful snipopets in this doctorbatch.io repo, along with the original link you posted.
For reference, I'll copy/paste from the ZFile Javadoc:
ZFile dd = new ZFile("//DD:MYDD", "r");
Opens the DD namee MYDD for reading
ZFile dsn = new ZFile("//'SYS1.HELP(ACCOUNT)'", "rt");
Opens the member ACCOUNT from the PDS SYS1.HELP for reading text records
ZFile dsn = new ZFile("//SEQ", "wb,type=record,recfm=fb,lrecl=80,noseek");
Opens the data set {MVS_USER}.SEQ for sequential binary writing. Note that ",noseek" should be specified with "type=record" if access is sequential, since performance is greatly improved.
One final note, another couple useful ZFile helper methods are: bpxwdyn() and getFullyQualifiedDSN().

Linked SQL table in Access 2003 (!) not updatable

I'm working in a legacy app for the moment, upgrading Access 2003 to link to SQL Server tables (2008 R2 or later). With tables linked by code, I can insert, but not update or delete. I've tried everything on the web, no dice. Details below.
Being terse so not tl;dr.
Tables first created using upsizing wizard. In use, app has to connect to different ones in same schema, so can't just set and forget. Can't do local DSN's, many installs, though DSN file is possible. But problems there too, DSN not found. Details later.
Before the rest: Soon I'm further updating this app to Access 2016 or so. If this is different enough / easier there, I'll wait a few days. Maybe someone could suggest the best refsite for that.
* problem details follow *
Using a DSN and the UI to link a table, I get an editable table. Hurray.
But when I use the code below (found on every refsite), link is made but only selecting and inserting work. Everything else fails fails fails, no matter what.
Public Function LinkToSqlTable(sqlInstance As String, sqlDb As String,
sqlTableName As String, localTableName As String)
Dim linked As New TableDef
' ***factored-out functionality, known to work: reader can ignore*** '
DeleteTable localTableName
' connection-string steps, placeholders replaced by args '
Dim sCnx As String
sCnx = "ODBC;Driver=SQL Server;Server=_instance_;" & _
"Database=_db_;Integrated Security=SSPI"
sCnx = Replace(sCnx, "_instance_", sqlInstance)
sCnx = Replace(sCnx, "_db_", sqlDb)
' linked-table steps '
Set linked = CurrentDb.CreateTableDef(localTableName)
linked.Connect = sCnx
linked.SourceTableName = sqlTableName
CurrentDb.TableDefs.Append linked
' ui '
RefreshDatabaseWindow
End Function
* ID column or permissions? *
I thought the problem was lack of identity column originally, I added one, but no change. At least now I have a PK field like I should. ;-)
When I manually link table, UI demands to know the ID column. So could it still be it? Fine, but how do I set that in code? Searches revealed nothing.
I assume then it's permissions as sites etc. say. I also took all the steps I could think of to fix that. No dice.
* things I've tried *
Aside from the ID-column stuff I said before, these things (not in order):
Since DSN saved as a file, tried using it as exampled, in cnx string. Fail.
Used DSN contents, carefully winnowed & translated, in cnx string. Fail.
Used connection string from the table that I had connected manually with DSN. Fail.
Changed driver in cnx string across all major options, even omitted it. Fail.
Changed security in cnx to Integrated Security=SSPI and other options, and omitted entirely. Fail.
I added my actual local user as exampled, with and without password. Fail.
(Previous few options tried across earlier options, though not 100% coverage.)
In SQL Server, using SSMS, I tried security power:
Added SQS-authentication login to the instance
Matching user to the default db seen here
Gave that login-user read and write permissions in db here (plus others, sometimes)
Added matching id & pw to the cnx string. Fail.
I tried setting up this db in SQS to have let-everyone-do-everything "security" temporarily. Fail.
This, that, and the other thing. Everything fail!!
So a permissions issue? Some way to use DSN file after all? Mismatched permission settings in my cnx string? Boneheaded oversight? Something else that I've missed? I'm pretty good at both SQL Server and Access, but only at a basic level in their security stuff and connection strings are the devil.
* retrieved table properties *
Just in case they help, I retrieved these (after objects added to TableDefs collection).
** This one, done in UI and with DSN and this-is-ID-field, worked with editing: **
Name = dbo_tblSendTo
Updatable = False
DateCreated = 4/19/2016 11:11:40 AM
LastUpdated = 4/19/2016 11:11:42 AM
Connect = ODBC;Description=SQL Server tables for TeleSales 5;DRIVER=SQL Server Native Client 10.0;SERVER=(local)\sqlexpress;Trusted_Connection=Yes;APP=Microsoft Office 2003;WSID=CMSERVER;DATABASE=TS5_General;
Attributes = 536870912
SourceTableName = dbo.tblSendTo
RecordCount = -1
ValidationRule =
ValidationText =
ConflictTable =
ReplicaFilter =
** And this one, from table linked via code, didn't: **
Name = tblSendTo
Updatable = False
DateCreated = 4/19/2016 11:17:51 AM
LastUpdated = 4/19/2016 11:17:51 AM
Connect = ODBC;Description=SQL Server tables for TeleSales 5;DRIVER=SQL Server Native Client
> 10.0;SERVER=(local)\sqlexpress;Trusted_Connection=Yes;APP=Microsoft Office 2003;WSID=CMSERVER;DATABASE=TS5_General;
Attributes = 536870912
SourceTableName = dbo.tblSendTo
RecordCount = -1
ValidationRule =
ValidationText =
ConflictTable =
ReplicaFilter =
* my plea *
So..... Please someone help me out. I don't like feeling stupid like this, and regrettably I need to do this instead of replacing it with .NET code or similar.
Thanks, anyone who can...
Ed.
Alas, I am able to answer my own question.
edited a little since first posted in reply to HansUp's comments
I had added an identity column to the table that I couldn't edit. However, I had not set it up as a primary key. It turns out that using identity doesn't make something a primary key automatically.
But the latter, making it primary key using either of the 2 possible DDL syntaxes, is crucial. Since I thought I had dealt with the no edits without unique key problem, I focused on permissions.
All of the permissions things here, then, are just a sideshow.
The upshot of this is to be sure to add an identity column and make it a primary key if for some reason your original table schema didn't have that.
If I have the time, I will be trimming the question to reflect what I've discovered.

Trying to run a Directory.Exists() with a system variable vs drive letter

I am trying to run a C# program to determine if a directory exists on multiple servers, so I need to run it as %system Variable%, rather than making a drive letter call, since not every server will have the same drive letter. This is what I have:
If My.Computer.FileSystem.DirectoryExists("D:\backup") Then
This code will work, as I define the drive
If My.Computer.FileSystem.DirectoryExists("%BCK_DRV%\backup") Then
This will not, I get my else error when running it. The %BCK_DRV% is defined in the environment variables, and I can navigate to the folder without issue using %BCK_DRV%\backup. Is there a special way to set and define a %drive% in C#?
Environment.GetEnvironmentVariable?
Code sample:
Dim backupDrive As String = Environment.GetEnvironmentVariable("BCK_DRV") & "\backup"
If My.Computer.FileSystem.DirectoryExists(backupDrive) Then
Try Environment.ExpandEnvironmentVariables():
string raw = #"%BCK_DRV%\backup" ;
string expanded = Environment.ExpandEnvironmentVariables( path_raw ) ;
Naturally, it's up to you to ensure that your process inherits the correct environment.
To expand "%BCK_DRV%\backup" to it's real value you need
Environment.ExpandEnvironmentVariables();
Example:
Environment.ExpandEnvironmentVariables("%winDir%\test")
will expand to "C:\Windows\test" (on my system).

Exchange - Programmatically Created Mailbox Can't Access Users' Calendars

I've been using a web application I've written to remotely create users/mailboxes on my Exchange server. However, users created using this method have been unable to see other users' calendar information within the same organization. Did I forget some important parameter?
I'm almost certainly not following best practices, but I essentially craft a command and send that to the Exchange powershell for my organization, like so:
Dim runspace As Runspace
runspace = ConnectToExchange()
Dim command = New Command("New-Mailbox")
command.Parameters.Add("Name", firstName & " " & lastName)
command.Parameters.Add("Alias", userName)
command.Parameters.Add("OrganizationalUnit", ou)
command.Parameters.Add("UserPrincipalName", userName & "#" & domainName)
command.Parameters.Add("SamAccountName", userName)
command.Parameters.Add("FirstName", firstName)
command.Parameters.Add("Initials", "")
command.Parameters.Add("LastName", lastName)
command.Parameters.Add("Password", securePassword)
command.Parameters.Add("ResetPasswordOnNextLogon", True)
command.Parameters.Add("Database", db)
command.Parameters.Add("AddressBookPolicy", abp)
runspace.Open()
Dim pipeline = runspace.CreatePipeline()
pipeline.Commands.Add(command)
' Execute the command
Dim results = pipeline.Invoke()
runspace.Dispose()
I should mention that I've reviewed the docs, but the only thing that stands out to me is the Sharing Policy parameter. Reviewing now..
EDIT: I have the same Sharing Policy applied as a test user I've just created. I can view others calendars, but the test user cannot.
SECOND EDIT: I ran the following cmdlets for both my normal account (which can see calendars) and the test account (which cannot see calendars):
Get-Mailbox identity.here | Format-List * | Out-File C:\Users\MyName\Desktop\file.txt
I then ran these through a diff tool and found the differences. There was nothing notable that would cause this issue! I don't know where to turn to next.
please run the following CMDlet on both accounts:
Get-MailboxFolderPermission -Identity john#contoso.com:\Calendar
Please note that the "Calendar" part seems to depend on your Exchange installation language (in German its "Kalender" for example).
If i get you right your account probably has AccessRights:{PublishingEditor} set.
If so run the following CMDlet on the account that doesnt have access:
Set-MailboxFolderPermission -Identity john#contoso.com:\Calendar -User Default -AccessRights Reviewer
Hope that helps
Paul

"The directory name is invalid" error on Process.Start?

I am writing a launcher program, and when I go to start the process I get the "The directory name is invalid" error. Here is the code that is launching the process:
Const DEBUG_ROOT = _
"Z:\Kiosk_JC\KioskSignIn.root\KioskSignIn\KioskSignIn\KioskSignIn\bin\Debug"
Dim oKiosk As New System.Diagnostics.Process
oKiosk.StartInfo.UserName = oEnc.Decrypt(Username)
oKiosk.StartInfo.Password = oEnc.DecryptSecure(Password)
oKiosk.StartInfo.Domain = oEnc.Decrypt(Domain)
''// The AddBS function appends a '\' to the passed string if it is not present
oKiosk.StartInfo.WorkingDirectory = AddBS(DEBUG_ROOT)
oKiosk.StartInfo.FileName = "KioskSignIn.exe"
oKiosk.StartInfo.UseShellExecute = False
Dim proc As Process = Nothing
proc = System.Diagnostics.Process.Start(oKiosk.StartInfo)
I saw on another question here that I needed to set the WorkingDirectory (before I started searching I was getting the error). Even though I have this property set, I am still getting the error. Any thoughts?
More info
I should also note that my Z:\ is a on my network. I have a function that resolves a path to UNC. When I ran this function on DEBUG_ROOT, I get the same error.
I tried moving the application to c:\kiosk. Same result. I am logged in as the user I am impersonating, so I have access to all shares and files.
Here is the link, for some reason the URL formating wants to consume all the text after the link is designated:
Referred Post
Mapped drives are per-user. You are likely starting the process with a different user.
Sounds like the process can't see the Z: drive or doesn't have security access. What user context does the app run under? Perhaps the Z: drive is not available in that context.
I got the same error as you do. most likely the user you use to run the process does not have access to specified resource (exe file)
try to move your exe to some other location and/or give your user access rights to the file.