vbscript to add macro to MS Word - vbscript

I have the following VBscript ( based on this answer ):
Set wdApp = CreateObject( "Word.Application" )
Set wdDoc = wdApp.Documents.Open( "c:\docs_to_process\document1.docx" )
Set xlmodule = wdDoc.VBProject.VBComponents.Add( 1 )
strCode = _
"sub test()" & vbCr & _
" msgbox ""Inside the macro"" " & vbCr & _
"end sub"
xlmodule.CodeModule.AddFromString strCode
wdDoc.Save
wdDoc.Close
wdApp.Quit
The problem is: when I run it and stop the script before saving the document, I can see the macro in the opened Word file, but when I process futher, save the document and quit the script, after that if I open Word document, I see no macro in it.
What am I doing wrong?
p.s: i've provide fixed strings here for anybody else faced with same issue:
wdDoc.SaveAs "c:\docs_to_process\document1.docm", 13
wdDoc.Close
important note:
instead of just save, give document new name and point
document type as wdFormatXMLDocumentMacroEnabled it is 13 in enum list
I don't know why, but pointing enum type as
wdDoc.SaveAs "c:\docs_to_process\document1.docm", wdFormatXMLDocumentMacroEnabled
causes same error after opening file as if i just change extention

For macro the file extension should be docm i.e. macro enabled. So change the file name
wdDoc.SaveAs "c:\docs_to_process\document1.docm", wdFormatXMLDocumentMacroEnabled

Related

MS Access 2016 File Browse Button Issues

I am using the script listed below (I honestly stole this probably from this very site) for a browse button on a form. The task is simply to start up MS File Dialog box so that a file (in this case an image file) can be selected. Once you select the record and click ok it then pastes the file name and location into a field.
Viewing the table the file name and location is pasted just as it should be. The problem comes in with a report I built. I have an image set to display with the control source linked back to that file address field. It will not display the image though.
However, if I manually type the same address character for character or even “copy”, delete, and then “paste” the same exact entry into the field the image then displays just fine on the report.
I have checked to make sure there are no spaces or characters anywhere there shouldn’t be. I am at a loss here.
Any help would be greatly appreciated and I will gladly give you my first born. Ok maybe not the first I like him but you can have the second one, she’s hell.
Private Sub Command67_Click()
On Error GoTo SubError
'Add "Microsoft Office 14.0 Object Library" in references
Const msoFileDialogFilePicker As Long = 3
'Dim FD As Office.FileDialog
Dim FDialog As Object
Dim varfile As Variant
Set FDialog = Application.FileDialog(msoFileDialogFilePicker)
EmployeePicture = ""
' Set up the File Dialog
Set FDialog = Application.FileDialog(msoFileDialogFilePicker)
With FDialog
.Title = "Choose the spreadsheet you would like to import"
.AllowMultiSelect = False
.InitialFileName = "C:\Users\" 'Folder picker needs trailing slash
.Filters.Clear
.Filters.Add "All", "*.*"
If .Show = True Then
If .SelectedItems.Count = 0 Then
'User clicked open but didn't select a file
GoTo SubExit
End If
'An option for MultiSelect = False
'varFile = .SelectedItems(1)
'EmployeePicture = varFile
'Needed when MultiSelect = True
For Each varfile In .SelectedItems
EmployeePicture = EmployeePicture & varfile & vbCrLf
Next
Else
'user cancelled dialog without choosing!
'Do you need to react?
End If
End With
SubExit:
On Error Resume Next
Set FDialog = Nothing
Exit Sub
SubError:
MsgBox "Error Number: " & Err.Number & " = " & Err.Description, vbCritical + vbOKOnly, _
"An error occurred"
GoTo SubExit
End Sub

Loop all the rows of a worksheet and copy them into a blank sheet

I'm facing a serious problem while importing my script into UFT for more than 2 weeks, I tried everything. As a worarround, I'm cpying the workbook and then I import the new on but this sometimes doesn't work too.
this is my code:
DataTable.ImportSheet workbook1,"name1","sheet1"
this is my workarround:
On error resume next
DataTable.ImportSheet workbook_path,"name1","sheet1"
MsgBox "Error: " & Err.Number & " (" & Err.Source & ") - " & Err.Description
If Err.Number <> 0 Then
If err.number = 20012 Then
Set objExcel1 = CreateObject("Excel.Application")
objExcel1.Visible = False
objExcel1.DisplayAlerts=False
Dim RelativePath
RelativePath = "C:\xyz\new_workbook.xls"
Dim objSheet1
Set objWorkbook1= objExcel1.Workbooks.Open("workbook.xls")
Set filesys = CreateObject("Scripting.FileSystemObject")
If filesys.FileExists(RelativePath) Then
filesys.DeleteFile RelativePath
End If
Set objWorkbook2=objExcel1.Workbooks.Add
objWorkbook2.saveAs RelativePath
For each objsheet1 in objworkbook1
objworkbook2.AddSheet objsheet1.Name
objsheet1.copy objworkbook2.sheets(1)
Next
objWorkbook2.save
objworkbook1.close
objworkbook2.close
objExcel1.Quit
Set objSheet1 = Nothing
Set objWorkbook1 = Nothing
Set objWorkbook2 = Nothing
Set objExcel1 = Nothing
On error resume next
DataTable.ImportSheet RelativePath,"name1","sheet1"
MsgBox "Error: " & Err.Number & " (" & Err.Source & ") - " & Err.Description
End if
End If
I want to try looping all the rows of the sheets and copying them into the new ones instead of copying them directly. Any help please ? if anyone has other solution to solve this issue, pleeeeeeease help
Why loop through the rows if you want them all? Just copy the sheet. IF you need the code for that, fire up the macro recorder, copy the sheet and stop the macro recorder.
Change your DataTable.ImportSheet workbook1,"name1","sheet1" call to DataTable.ImportSheet workbook1,"name1","Action1" or to DataTable.ImportSheet workbook1,"name1","Global". Make sure that your path is correct for the workbook and name1 sheet exists in your workbook
Are you able to import manually into DataTable? Sometimes, the special characters from the spreadsheet throw error.
If you are receiving "Invalid file error", follow the steps.
1. Open UFT and Activate Data Table and Perform the below action
Perform
Choose the appropriate sheet to be imported.
Check if any "Invalid File Error Dialog". If yes Goto Step 5 else GoTo Step 2
Go back to actual spreadsheet and replace all of the special characters including spaces and clear all the Formatting of the cells

Vbscript type mismatch error apparently not justifiable

I'm trying to figure out why I would get this error for the code below. I've searched on the web for possible causes but I couldn't find. Please have a look:
Dim descr, code
If WScript.Arguments.Count = 0 Then
Set objShell = CreateObject("Shell.Application")
objShell.ShellExecute "wscript.exe", Chr(34) & Script.ScriptFullName Chr(34) & " Run", , "runas", 1
Else
descr = InputBox("restore point description","Crepo")
If (descr) Then
code = GetObject("winmgmts:\\.root default:Systemrestore").CreateRestorePoint (descr, 0, 100)
If (code) Then
Msgbox "System Restore Point not created (" & code & ") !", 48, "Crepo"
Else
Msgbox "System Restore Point successfully created", 64, "Crepo"
End If
End If
End If
At runtime, if I input anything, i.e. qwerty I get this error:
Line: 8
Char: 2
Error: Type mismatch: '[string: "qwerty"]'
Code: 800A000D
From my researches, return type of InputBox is string and CreateRestorePoint first argument is also string. In fact, it works if I call InputBox directly as the argument.
The If clause needs a boolean (not a string):
>> descr = "qwerty"
>> If (descr) Then WScript.Echo "nonsense"
>>
Error Number: 13
Error Description: Type mismatch
InputBox returns a string or an empty value (see here), but never Null.
Instead of If (descr) Then try If (Not IsNull(descr)) Then.
You may also want to check for empty strings too: If (Not IsNull(descr) and descr <> "") Then. In fact, as pointed out in the other answer, since the VBScript InputBox function docs note that If the user clicks Cancel, the function returns a zero-length string ("") you definitely need to check for an empty/zero-length string.

Win32_NTLogEvent message property FilSystemObject.Write procedure call issue

I am writing a script to write event log information to a csv. My script was working before. But I changed some stuff, which shouldn't have any effect on writing to the csv. I have tried writing to unicode and to ASCII, based on some research I did on the internet about this issue. Neither makes a difference. The odd thing is that I use the same code later in my script to write other logs (I first write system logs, then I write application logs, etc.), and it works perfectly there. The code I am using is temporary, as I have not got around to writing a way to delete carriage returns from messages (which causes issues with importing the CSV to Excel). So it might fix itself once I do that. But it seems like it is a larger issue than that. Here is the script up until it moves on to other logs:
Set wshShell = WScript.CreateObject( "WScript.Shell" )
strComputerName = wshShell.ExpandEnvironmentStrings( "%COMPUTERNAME%" )
strComputer = "."
strType = "Error"
strPath = "T:\IT resources\Event Logs\ErrorLog" & strComputerName & ".csv"
'Script to convert UTC to human readable. From Script Repository.
Function WMIDateStringToDate(dtmInstallDate)
WMIDateStringToDate = CDate(Mid(dtmInstallDate, 5, 2) & "/" & _
Mid(dtmInstallDate, 7, 2) & "/" & Left(dtmInstallDate, 4) _
& " " & Mid (dtmInstallDate, 9, 2) & ":" & _
Mid(dtmInstallDate, 11, 2) & ":" & Mid(dtmInstallDate, _
13, 2))
End Function
'ForWriting is to write to file from start. ForAppending is to write to file from end of file.
constForWriting = 2
constForAppending = 8
constTristate = 0
boolUnicode = False
chrCarriageReturn = chr(13)
chrNewLine = chr(10)
Set objFSO = CreateObject("Scripting.FileSystemObject")
'This is so that cscript won't encounter a runtime error if the file already exists. Also so that it will write to the already existing file.
If objFSO.FileExists(strPath)=False Then
Set objErrLog = objFSO.CreateTextFile(strPath,constForWriting,boolUnicode)
objErrLog.Write "Type,"
objErrLog.Write "Time Generated,"
objErrLog.Write "Source Name,"
objErrLog.Write "Event Code,"
objErrLog.Write "Category,"
objErrLog.Write "Message"
objErrLog.Writeline
strTimeMin = "01/01/1970/0:00:00"
'19700101000000.000000-480
Else Set objErrLog = objFSO.OpenTextFile(strPath,constForAppending,constTristate)
'Only need this if it writes from the line the file ends on, as opposed to starting on a new line (which I expect it will).
objErrLog.WriteLine
End If
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
'Querying Event Logs
Set colLoggedEvents = objWMIService.ExecQuery _
("SELECT * FROM Win32_NTLogEvent WHERE Logfile = 'system' AND "_
& "Type = 'Error'")
'Type='Error' instead of "1" because it is a WQL query, I think. I believe that it is searching the entries in a database that reference the Win32_NTLogEvent objects. So I am searching the values in the database as opposed to the properties of the objects they reference. Or perhaps not. WHen I echo the type property of every object in colLoggedEvents, cscript outputs "Error". So maybe the I'm reading the SDK wrong? At least it seems to be working.
'This is a comparison function which tells where string 2 occurs in string 1. Starts at 1.
constStart = 1
constCompareType = 0
'This loop writes the information to a .csv.
For Each objEvent In colLoggedEvents
If objEvent.Timegenerated > strTimeMin Then
strTimeMin = objEvent.TimeGenerated
Else
End If
objErrLog.Write objEvent.Type & ","
objErrLog.Write WMIDateStringToDate(objEvent.TimeGenerated) & ","
objErrLog.Write objEvent.SourceName & ","
objErrLog.Write objEvent.EventCode & ","
constExist=InStr(constStart,objEvent.Message,chrCarriageReturn,constCompareType)+InStr(constStart,objEvent.Message,chrNewLine,constCompareType)
If constExist = 0 Then
objErrLog.Write objEvent.Category & ","
objErrLog.Write objEvent.Message
Else
objErrLog.Write objEvent.Category
End If
objErrLog.WriteLine
Next
Any help would be greatly appreciated.
Loose the misconception that code 'might fix itself'
Give the full error details (number, description, line identified) when asking a question
Assuming that you got a "5 - Invalid procedure call or argument" error on a line starting with "objErrLog.Write" see here for an explanation.
You claim you have tested a variant of your code using Unicode; you didn't, because:
The prototype of .CreateTextFile is
object.CreateTextFile(filename:string[, overwrite:bool[, unicode:bool]])
This clashes with your
objFSO.CreateTextFile(strPath,constForWriting,boolUnicode)
The prototype of .OpenTextFile is
object.OpenTextFile(filename:string[, iomode:enum[, create:bool[, format:enum]]])
This clashes with your
objFSO.OpenTextFile(strPath,constForAppending,constTristate)
So fix these blunders (yourself!), test with the file really opened for Unicode, and hope that assumption (3) holds.
Update wrt comments:
Please reflect upon "Give the full error details (number, description, line identified) when asking a question" in the context of:
I get an invalid procedure error after 68 members of colLoggedEvents
when I have the file in ASCII.
vs
I get the error when I call the OpenTextFile method
The first statement implies that the 68th member contains characters that can't be written in ASCII/ANSI mode. Really/Correctly using Unicode output format will fix the problem, provided the error log does not contain invalid data.
The second statement indicates that the parameters to the .Open/CreateTextfile methods are still not correct. Did you change both invocations to Unicode?
Update II:
The docs define
TristateTrue -1 Opens the file as Unicode.
Your code wrongly uses:
constTristate = 1
Set objErrLog = objFSO.OpenTextFile(strPath,constForAppending,boolCreate,constTristate)
Evidence:
>> Set ts = goFS.OpenTextFile("error5.txt", 8, False, 1)
>>
Error Number: 5
Error Description: Invalid procedure call or argument
>> Set ts = goFS.OpenTextFile("error5.txt", 8, False, -1)
>>
>> <-- no news are good news
Update wrt comment concerning TriStateTrue:
The VBScript doc say:
TristateUseDefault -2 Opens the file using the system default.
TristateTrue -1 Opens the file as Unicode.
TristateFalse 0 Opens the file as ASCII.
The doc #Adam refered to concerns VBA; but I wouldn't trust it without a check.

How to extract context informationm the equivalent of __LINE__, __FILE__,etc., in VBSCRIPT

I'd like to know how to get the line number of a line in vbscript programmaticly either at the point of the code like __LINE__ or more ideally a way to get the line number of where the current function was called like python's stack module so I can write a reusable debugging function(and the file the code is located in) and no I don't want to know how to turn on line numbers in my editor.
Also I'd like to now any similar useful information that can be extracted such as calling function, variable type as string, etc.
Unfortunatly that doesn't work the way like in Ruby and Python. The next best thing i worked out is putting a call to a errorhandling function everywhere where things could go wrong. The numbers in the parameter of this function are adapted each time i execute a macro in my editor (i use textpad, the \i is autonumbering in a Regular Expression). If your editor doesn't support this you could write a script that does this. So when an error occurs, it is logged with the number the errorhandling function was called and you can easily find it back in the source by looking for #number#.
This is usable for both asp and vbs but for vbs there is an easier way.
Some editors like textpad or sublimle text let you execute a vbs script, show the output in a tab and if an error is produced let you double click the line with the errormessage which opens the script at that line. This is also done by a regular expression. Let me know if you need the one for textpad.
on error resume next
'initialize constants DEBUGLEVEL and LOGFILE
'initialize strHostName
'some code
oConn.execute(sql)
if not LogError("#1#") then
'do the things if successfull, otherwise log error with number
end if
'again some code
if not LogError("#2#") then
'do the things if successfull, otherwise log error with number
end if
'the debug and log functions
function LogError(errornumber)
'LogError\(\"#[0-9]+#\"\) replace by LogError("#\i#")
if err.number <> 0 then
call debug("<name of script>/Logerror","","","Errornumber:" _
& errornumber & " " & err.number & " " & err.description & " " _
& err.source)
LogError = True
err.clear
errors = errors+1
else
LogError = False
end if
end function
function Debug (pagina, lijn, varnaam, varinhoud)
if DEBUGLEVEL > 0 then
const forReading = 1, forWriting = 2, forAppending = 8, CreateFile = True
dim fs,f, var, strHostName
set fs=CreateObject("Scripting.FileSystemObject")
strHostName = fs.GetFileName(WScript.FullName)
if fs.FileExists(LOGFILE) then
set f=fs.OpenTextFile(LOGFILE, forAppending)
else
set f=fs.OpenTextFile(LOGFILE, forWriting,true)
end if
var = now & " " & pagina & ":" & lijn & ":" & varnaam & ":" & varinhoud
f.WriteLine var
if LCase(strHostName) = "cscript.exe" then 'debugging
if DEBUGLEVEL > 1 then
wscript.echo var
end if
end if
f.Close
set f=Nothing
set fs=Nothing
end if
debug = true
end function
VBScript doesn't expose that information, so you can't access it programmatically from within the script (edge cases notwithstanding). You're going to need a debugger for extracting this kind of information. Or you could have another script interpret the first one and keep track of line numbers (like this). I wouldn't recommend the latter for any kind of production environment, though.
As long as it's happening outside of a function, the following works.
Automatic error-handling is turned off at the start of the script by On Error Resume Next, so that the script doesn't just exit before you can do anything. BUT, you can then turn error-handling back on using On Error GoTo 0 and Raise an exception yourself. That will output the line number in addition to any of your debugging messages.
For example:
On Error Resume Next
server = WScript.Arguments(0)
If Err.Number <> 0 Then
WScript.Echo("Need to pass in an argument!")
On Error GoTo 0
Err.Raise(1)
End if
If you run this without any arguments, you get the following output:
Need to pass in an argument!
C:\script.vbs(6, 5) Microsoft VBScript runtime error: Unknown runtime error
The "6" refers to the line number where the exception was raised.
This way you can print custom output, and also you'll know what line the error happened at.
Yes!
There is a way to get the exact error line number, but it's HUGLY, as we are talking about an ancient programming tool....
And yes, it is worth it, especially if your code is going to run in front of many users. That way you can get past isolating and reproducing the bug, right to solving it.
Take a close look at the last variable "Erl" in the line of code below. It is an undocumented global variable the VB script processor holds.
Dim sErrorMsg as String
sErrorMsg = Err.Description & "(" & Err.Number & ")" & vbNewLine & "Source: " & Err.Source & vbNewLine & "At line number: " & Erl
In order to get anything from that global "Erl" variable you need to (manually)** set its value at the beginning of each line of code as shown below. Beware, you set the line number, if you forget to set the number for a specific line, Erl will report the last set value. See the division by zero error line, it reports the line number set above because I did not set a line number value at the beginning of the line that caused the error.
I have not figured out the inbuilt call stack, though I know there is one. Please let me know if you figure that one out, for now I use a module level variable to build the stack.
More tips at the very end, below this code sample
Sub WhatEverSub ()
2 Const iColIdxPageNbr As Integer = 2
3 Const iColIdxDefinition As Integer = 3
5 Dim oDoc_Source As Document
6 Dim oDoc_Target As Document
10 Dim oTable As Table
11 Dim oRange As Range
12 Dim n As Long
13 Dim strAllFound As String
14 Dim Title As String
15 Dim Msg As String
On Error GoTo PrepErrorHandler
Dim xyz As Long
xyz = Rnd(3) / 0
16
17 Title = "Evil Finder - This program is about doing something important for the world"
18
19 'Show msg - stop if user does not click Yes
20 Msg = "This macro finds all evil things consisting of 2 or more " & _
"uppercase letters and extracts the hex representation to a table " & _
"in a new document." & vbNewLine & vbNewLine & _
"Do you want to continue?"
21 If MsgBox(Msg, vbYesNo + vbQuestion, Title) <> vbYes Then
22 Exit Sub
23 End If
(... whatever code ...)
820 Application.ScreenUpdating = True
830 If n = 1 Then
840 Msg = "No evil things were found. Need to find better detection tool"
850 oDoc_Target.Close savechanges:=wdDoNotSaveChanges
860 Else
870 Msg = "Finished extracting " & n - 1 & " evil thing(s) to a new document."
880 End If
PrepErrorResumeLine:
890 MsgBox Msg, vbOKOnly, Title
'Clean up
1000 Set oRange = Nothing
1010 Set oDoc_Source = Nothing
1020 Set oDoc_Target = Nothing
1030 Set oTable = Nothing
Exit Sub
PrepErrorHandler:
Msg = Err.Description & "(" & Err.Number & ")" & vbNewLine & "Source: " & Err.Source & vbNewLine & "At line number: " & Erl
Resume PrepErrorResumeLine
End Sub
**Some more tips:
1)
As for setting the error line number values manually, I wrote a utility (more than a decade ago) to automate the addition or removal or renumbering of all lines in a module by working directly on the VB project files (or standalone .vbs files), but the below will take care of the basic, with a few manual adjustsments remaining...
Set up VB code line #s using MS Excel
a) paste code in column C
b) set column A's first cell value to 10, and second to 20 and drag copy down to auto increment until you reach the last line/row of code in column B
c) in column B paste in the following formula and drag copy down =A1 & REPT(" ", 8 - LEN(A1))
d) copy columns B and C back into the VB code pane et voila!
Strip out the line numbers to do major edits using Word
Paste the code in,
Hit CTRL + H and make sure wildcards is checked (click the "more" button)
Fill in the following settings
FIND
[^13][0-9 ]{4}
REPLACE
^p
Done!
2)
number each line in increments of 10 at least so you can wedge in a few lines at the last minute without having to renumber each line below your change
3) On Error Resume Next is evil and will cost you a lot of debugging hours!
At least 90% of the time, one should use a specific handler, or nothing. If you do not already know how to recover from an error, do not use RESUME NEXT to silence it, instead, log all the details (using Erl) and learn from the run time logs and use GoTo 0 'Zero, not the letter O to let the rest of the errors bubble up the call stack.
On Error GoTo MyErrorHandlerSection
(... write your risky code here ...)
On Error GoTo 0
'the line immediately above disables all error handling in the current function, if any error happens, it will be passed to the calling function
Nothing prevents you from adding another handling section in the same function if you have another chunk of risky code using
On Error GoTo MySecondErrorHandlerSection

Resources