I am working in SAP (PR2) and I have a large report that usually takes a long time (2 or more hours) to run. I have to take the output of this report and drop it into Excel to be manipulated and cleansed before bringing it into Access.
The ideal result would be a script that could launch automatically around 4am, login in to SAP, run the report, and have the results waiting for me when I come in. In short, I am missing parts 1 and 2, the automatic launch and automatic login to SAP.
I have pulled together a script that will start the report and then output the results as I want. The downside of this is that I don't get the results until about noon-ish each day, and that interrupts the workflow of those whom I support.
I have tried to run the necessary report in the background as suggested in other questions, but due to the size of the report and my limited access inside of SAP, it comes out in a way that is completely useless to me.
Thanks in advance for your help. Also, thanks for the help ya'll have given on prior questions :)
PS:As a bonus, if anyone knows how to encrypt a VBscript, that would be helpful as well.
Use the windows task scheduler to set a run daily at time task.
VBS cannot be encrypted but can be encoded. This will only stop the casual person fiddling with the code, decoding scripts are available online for anyone who really wants to get your code.
'ENCODE VBS TO VBE
Set oFilesToEncode = WScript.Arguments
Set oEncoder = CreateObject("Scripting.Encoder")
For i = 0 To oFilesToEncode.Count - 1
file = oFilesToEncode(i)
Set oFile = fso.GetFile(file)
Set oStream = oFile.OpenAsTextStream(1)
sSourceFile = oStream.ReadAll
oStream.Close
sDest = Encoder.EncodeScriptFile(".vbs",sSourceFile,0,"")
sFileOut = Left(file, Len(file) - 3) & "vbe"
Set oEncFile = fso.CreateTextFile(sFileOut)
oEncFile.Write sDest
oEncFile.Close
Next
WScript.quit
I use a software 'exescript' to convert to exe. Seems to work OK for me...
Related
I have a VB script that does row processing on a small Excel file (35 columns, 2000 rows, no hidden content). It takes about 3 seconds to process the file when applied to an .xls file. If I save the .xls as a .xlsx file, the same script takes about 30 to 60 seconds, completely freezing Excel several times in the process (although it finishes and the results are correct).
I suspect some loop parts of the code are responsible, but I need to find out which. I have found scripts like https://stackoverflow.com/a/3506143/5064264 which basically amount to timing all parts of the script by hand - similar to printf-debugging, which is not very elegant and also consumes much time for large scripts.
Is there a better alternative to semi-manual profiling, like performance analyzers in most IDEs, or exist any other approaches to this problem? I do not want to post the code as it is rather long and I also want to solve it for other scripts in the future.
yes - kind of. I use this to test how fast things are running and experiment with different approaches to get better timings. I stole this from somewhere here on SO.
sub thetimingstuff()
Dim StartTime As Double
Dim SecondsElapsed As Double
StartTime = Timer
'your code goes here
SecondsElapsed = Round(Timer - StartTime, 2)
MsgBox "This code ran successfully in " & SecondsElapsed & " seconds", vbInformation
end sub
Another EDIT
you could bury debug.print lines sporadically through your code to see how much each chunk takes to execute.
The error in my case was as Comintern's comment suggested - a method was copying whole columns into a dictionary to reorder them, instead of just the cells with values in them. To preserve this for future readers, I am copying his/her excellent comment here, thanks again!
An xls file has a maximum of 65,536 rows. An xlsx file has a maximum of 1,048,576 rows. I'd start by searching for .Rows. My guess is that you have some code iterates the entire worksheet instead of only rows with data in them.
As for generic profiling or how to get there, I used a large amount of breakpoints in the editor (set/remove with left-click in the line number column of the code editor):
I first set them every 20 lines apart in the main method, ran the program with the large dataset, then after each press of continue (F5) manually measured if it was slow or not.
Then I removed the breakpoints in the fast regions and added additional ones in the slow regions to further narrow it down into methods and then into lines inside the methods.
At the end, I could verify if by commenting out the responsible line of code and fix it.
HTA calls external VBScript and after that it turns into suspend mode. At this point control is transferred to the VBScript. As a result of VBScript code execution it may happen that the machine would be rebooted. After the reboot HTA starts automatically and stays suspended; while the VBScript continues to work (let's assume they both start from Startup). Upon completion of VBScrip work I'd like control to be transferred back to the HTA and a HTA subroutine to be executed. Let's say this subroutine is Window_OnLoad. How can I do this? Is there an instruction to open HTA and after that execute a sub? If I launch to HTA from VBScript with Run method (or ShellExecute) I get only HTA window (you remember that HTA is still suspended) but my goal is to wake up HTA and make it running.
Some additional details
I didn't want to publish the code here as this is general question and the code might give more confusion... Primary task of this HTML Application is giving administrator of workgroup/domain a very smooth way for customization of just installed Windows 7 (change computer name, join to domain, set local computer description, set domain computer description, regional settings, etc.). HTA offers also opportunity to choose what steps to be done - this is realized as a list of checkboxes; so that administrator can change computer name but do not include computer into the domain and set up power settings for instance. Some steps requires computer reboot; but some steps are okay to pass control to the next step without reboot. Here is a general idea: I have HTA that allows user to enter some data; also there is XML file. XML is used to keep not only user data but also some instructions for steps to be done; e.g. for user data XML contains
<computername/>
<PCdescription/>
<DomainName/>
<OUName/>
whilst the step is described in this manner:
<step>
<description>Include PC into Domain<description/>
<filetorun>/scripts/domain.vbs</filetorun>
<arguments></arguments>
<completion>reboot</completion>
</step>
Here description is the step name which is shown in HTA window, filetorun gives the VBScript (or batch file) to be executed to complete the step, arguments are compiled from the user data and saved to XML, and complete shows whether reboot is required after the step is completed.
VBScript is executed by these instructions:
strFile = oNode.SelectSingleNode("filetorun").Text
strArgs = oNode.SelectSingleNode("arguments").Text
stringtorun = strFile & " " & strArgs
result = Createobject("WScript.Shell").Run(stringtorun,,true)
where oNode is XML node for the current step.
All instructions are nested to the HTA like this
<html>
<head>
'...........
<HTA:APPLICATION
APPLICATIONNAME="MyHTMLapplication"
VERSION="1.0"/>
'...............
</head>
<script language="VBScript">
Sub Window_OnLoad
'......................
' Populate form, save XML, other operations
'......................
'--------------execute step command---------------
strFile = oNode.SelectSingleNode("filetorun").Text
strArgs = oNode.SelectSingleNode("arguments").Text
stringtorun = strFile & " " & strArgs
result = oShell.Run(stringtorun,,true)
'-------------------------------------------------
If Reboot_is_not_required Then
'computer restart not required, proceeding with the next step...
Window_OnLoad
End If
End Sub
</script>
<body>
........................
</body>
</html>
Up to the present day completion was allowed to take two values: Reboot and NoReboot. The latter parameter causes recurrent call of Window_OnLoad when current step is completed and we are about to make next step.
Now there is a need to introduce parameter Delay. I'm going to use this parameter for such external scripts that require system reboot without passing control back to HTA.
HTA starts automatically after system reboot. So, if the current step is marked as Delay then HTA will only populate the forms. I want that external script which executes the current step in such model, would somehow pass control back to HTA. But this is not enough because at this stage HTA work is suspended. To wake HTA up and force to perform next step I need to call a subroutine. How to do this from VBScript?
Do not be too hard on me. This is my first steps in programming.
Thank you in advance.
I have a system that launches 50 or so VBS scripts via WSF, that need to stay there until another part of the system connects to them, then they act as servers for a bit until the peer disconnects, then they exit and get restarted.
For initialization purposes, they all use an EXCEL.EXE to read a large number of parameters from a spreadsheet, via
Set objExcel = CreateObject("Excel.Application")
We can't afford to have 50 EXCEL.EXEs running at once, so the restarts are sequentialized, so that there should never be more than one EXCEL.EXE running: usually zero, as they are only used for 15-20 seconds and then released.
However sometimes things go wrong, the WSF scripts exit, and the EXCEL.EXE that it starts stays there. So we do see up to a dozen EXCEL.EXE processes.
My question is about using GetObject() instead of CreateObject(). Would it be possible to use GetObject() so that if there already was an EXCEL.EXE running, it would use that one instead of starting a new one? And if so what other steps are necessary?
There is also a supplementary question here about why the EXCEL.EXEs persist after the VBS that started them has exited, but I can imagine ways in which the VBS could exit (or be killed) that would allow that.
Note that the question is also partly about the re-entrancy of EXCEL.EXE, which I have no information about.
I'm not the author of these scripts, and I'm not very strong in VBS as far as external objects go, so it is is entirely possible that I'm asking a trivial question here.
Usage of GetObject() is documented in this old KB article. Error handling is required to get the first instance created. Like this:
Dim excel
On Error Resume Next
Set excel = GetObject(, "Excel.Application")
If Err.number = 429 Then
Set excel = CreateObject("Excel.Application")
End If
If Err.number <> 0 Then
WScript.Echo "Could not start Excel: " & err.Description
End
End If
'' etc
However, seeing zombie Excel.exe processes surviving is a broad concern, it strongly suggests that the scripting runtime is not exiting normally. Perhaps error handling in your existing scripts is less than ideal, that's not likely to get better when you slam a single instance with multiple scripts. Excel does get pretty cranky when it cannot keep up. Using the OpenXML api or Excel Services are the better way to go about it.
How do you request Windows to spin down a hard disk programmatically? Is there any user-mode function I can call (or kernel-mode function to call or IRP to send) in order to make this happen?
I've tried making a program to send an ATA STANDBY command directly to the hard disk, but the problem is that this method doesn't inform the system, and hence whenever the system needs to flush the cache, it'll wake up the hard disk again. How do I tell the system to do this for me? (If the system does it, it'll save up the cache and "burst" the data when it gets too large, instead of writing in small increments.)
(The entire point here is to do this directly, not by changing the system-wide spin-down timeout to a 1-second period and waiting for the disk to spin down. I need a function I can call at a specific moment in time when I'm using my laptop, not something generic that doesn't suit 95% of situations.)
How far I've gotten so far:
I have a feeling that PoCallDriver and IRP_MJ_POWER might be useful for this, but I have very limited kernel-mode programming experience (and pretty much zero driver experience) so I really have no idea.
Please read:
Update:
People seem to be repeatedly mentioning the solutions that I have already mentioned do not work. Like I said above, I've already tried "hacky" solutions that change the timeout value or that directly issue the drive a command, and the entire reason I've asked this question here is that those did not do what I needed. Please read the entire question (especially paragraphs 2 and 3) before repeating what I've already said inside your answers -- that's the entire difficulty in the question.
More info:
I've found this document about Disk Idle Detection to be useful, but my answer isn't in there. It states that the Power Manager sends an IRP to the disk driver (hence why I suspect IRP_MJ_POWER to be useful), but I have no idea how to use the information.
I hope this helps:
This: http://msdn.microsoft.com/en-us/library/aa394173%28VS.85%29.aspx
Leads to this:
http://msdn.microsoft.com/en-us/library/aa394132%28VS.85%29.aspx#properties
Then, you can browse to this:
http://msdn.microsoft.com/en-us/library/aa393485(v=VS.85).aspx
This documentation seems to outline what you are looking for I think.
P.S. Just trying to help, don't shoot the messanger.
Have you tried WMI? Based on MSDN documentation, you should be able to send spindown command to HDD via WMI:
http://msdn.microsoft.com/en-us/library/aa393493%28v=VS.85%29.aspx
uint32 SetPowerState(
[in] uint16 PowerState,
[in] datetime Time
);
EDIT:
This code lists all drives in system and drives that support this API:
strServer = "."
Set objWMI = GetObject("winmgmts://" & strServer & "/root\cimv2")
rem Set objInstances = objWMI.InstancesOf("CIM_DiskDrive",48)
Set objInstances = objWMI.ExecQuery("Select * from CIM_DiskDrive",,48)
On Error Resume Next
For Each objInstance in objInstances
With objInstance
WScript.Echo Join(.Capabilities, ", ")
WScript.Echo Join(.CapabilityDescriptions, ", ")
WScript.Echo .Caption
WScript.Echo .PNPDeviceID
WScript.Echo "PowerManagementCapabilities: " & .PowerManagementCapabilities
WScript.Echo "PowerManagement Supported: " & .PowerManagementSupported
WScript.Echo .Status
WScript.Echo .StatusInfo
End With
On Error Goto 0
Next
Just save this code as a .vbs file and run that from command line.
I do not have an answer to the specific question that Mehrdad asked.
However, to help others who find this page when trying to figure out how to get their disk to standby when it should but doesn't:
I found that on a USB disk, MS PwrTest claims that the disk is off, but actually it is still spinning. This occurs even with really short global disk timeouts in win 7. (This implies that even if the system thinks it has turned the disk off, it might not actually be off. Consequently, Mehrdad's original goal might not work even if the correct way to do it is found. This may relate to how various USB disk controllers implement power state.)
I also found that the program HDDScan successfully can turn off the disk, and can successfully set a timeout value that the disk honors. Also, the disk spins up when it is accessed by the OS, a good thing if you need to use it, but not so good if you are worrying about it spinning up all the time to flush 1kB buffers. (I chose to set the idle timeout in HDDScan to 1 minute more than the system power manager timeout. This hopefully assures that the system will not think the disk is spun up when it is not.)
I note that powercfg has an option to prevent the idle clock from restarting from small infrequent disk writes. (Called "burst ignore time.")
You can get HDDScan here: HDDScan.com and PwrTest here: Windows Driver Kit. Unfortunately, the PwrTest thing forces you to have a lot of other MS stuff installed first, but it is all free if you can figure out how to download it from their confusing web pages.
While there is no apparent way to do what you're asking for (i.e. tell power management "act as if the timer for spinning down the disk has expired"), there may be a couple ways to simulate it:
Call FlushFileBuffers on the drive (you need to be elevated to open \\.\C), then issue the STANDBY command to the drive.
Make the API call that sets the timeout for spinning down the disk to 1 second, then increase it back to its former value after 1 second. Note that you may need to ramp up to the former value rather than immediately jump to it.
I believe the Devcon Command line utility should be able to accomplish what you need to do. If it does - the source code is available in the Windows Ddk.
Is their any way for a windows process to block another process from printing?
Basically we are trying to cut costs in the office and we want to block people from printing their emails. So I was asked if it was possible to write a program to block or cancel any print jobs comming from Outlook.
Can use a solution in any language or API, whatever works.
We have Win XP and Win 7 users.
There really isn't a good way to do this on Windows.
Besides, this doesn't seem like a technology problem.
Supply the printer with only one ream of paper per month. Let everyone know that the paper supply is limited.
The obvious work around is that users will bring in their own paper, but that will greatly reduce the operating cost, so it's not really a problem.
Are you using a network printer? If it is so, you can simply audit the printer's log (or the print server's) and "educate" your users accordingly, it will save you the hassle of writing such a program and deploy it on every pc (and make sure no user can override it).
Basically, this is the closest I could find for doing this. If basically waits for a print Job to show up with a particular job name and then cancel that job.
Technically though, I think the answer to my questing is no, since i asked if I could do this by process. Although the below comes close. It just prints out jobs that are happening and cancels any print job that says MS Outlook in its title. The dictionary thing just keeps it from listing the same jobs a bunch of times.
Imports System.Printing
Module Module1
Sub Main()
Using ps As New PrintServer("\\server")
Using Pq = ps.GetPrintQueue("printer")
Dim DetectedJobs As New Dictionary(Of DateTime, String)
Do
Dim Pj = Pq.GetPrintJobInfoCollection
For Each job In Pj
If Not (DetectedJobs.ContainsKey(job.TimeJobSubmitted) AndAlso DetectedJobs(job.TimeJobSubmitted) = job.Name) Then
DetectedJobs.Add(job.TimeJobSubmitted, job.Name)
Console.WriteLine(job.Name & "," & job.JobIdentifier & "," & job.TimeJobSubmitted)
End If
If job.Name.Contains("Microsoft Office Outlook") Then
job.Cancel()
Console.WriteLine(job.Name & "," & job.JobIdentifier & "," & job.TimeJobSubmitted & " - Cancelled")
End If
job.Dispose()
Next
Pj.Dispose()
Threading.Thread.Sleep("200")
Loop
End Using
End Using
Console.ReadKey()
End Sub
End Module