Excel VBA TextStream.writeline with Chinese characters - excel

I am fairly new to VBA and am stumped on how to resolve the "Run-time error '5': Invalid procedure call or argument" error that I am receiving when executing this code. The cell in question has chinese characters and the code seems to work fine on the english alphabet. The stream is outputting to a text file. (should be an xml file in the future, but I still don't have all the correct formatting implemented)
Dim fso As New FileSystemObject, stream As TextStream
Set stream = fso.createTextFile("C:\Users\username\XMLs\" _
& WS_Src.Cells(c.Row, 5).Value & "_" & WS_Src.Cells(c.Row, 4).Value & "_Feature.xml", True)
...
stream.WriteLine "<title>" & vbCrLf & "<![CDATA[ " & WS_Src.Cells(c.Row, 6).Value & "]]>" & vbCrLf & "</title>" 'error is on this line
stream.Close
Thanks!
Chris

Sytax for using CreateTextFile method is something like object.CreateTextFile(filename[, overwrite[, unicode]])
. Where:
filename: Required. String expression that identifies the file to create.
overwrite Optional. Boolean value that indicates if an existing file can be overwritten. The value is True if the file can be overwritten; False if it can't be overwritten. If omitted, existing files are not overwritten.
unicode Optional. Boolean value that indicates whether the file is created as a Unicode or ASCII file. The value is True if the file is created as a Unicode file; False if it's created as an ASCII file. If omitted, an ASCII file is assumed.
And you have omitted the last param here, but incoming text, being Chinese is not just ASCII. Rather you have to provide a True value for that, I mean for unicode param. This would definitely solve the problem.
BTW! There are still some factors I can see in the code might cause other run-time errors.
As you generating filename by joining cell values, make sure no invalid characters is not present in the path string,
Furthermore, only setting overwrite value to true is not enough, but also make sure that the folder already exist. Otherwise the procedure would again caught by run-time errors.
Hope this helps.

Related

Search txt and csv files for string using Excel VBA without opening files

I have a text file that is automatically generated from a machine. The machine writes the txt file in "chunks" (sorry I don't know the exact terminology). I need to pull data from this txt file, but I need the txt file to be finished before pulling data from it. I found a solution to verify that the machine has finished writing to the file... It is not as elegant as i had hoped, but seems to do the trick. Excel VBA opens a command prompt, the command prompt uses a Find command to find the string "End of Report"... This is basically one of the last lines of the txt file and pretty safe to assume the txt file is finished after this is found. This code runs in a loop 1000 times, every 10 seconds, until it finds this string or reaches 1000 tries...
The issue is that "result" returns some other characters besides just "End of Report" this is further complicated by the fact that I am attempting to run this on some csv files too... and "result" returns some additional characters also, but different from the ones returned from the txt files. For example, if I check the length of "result"... The length comes back as 43 on one file and 48 on another file... I think it is counting the file path + "End of Report" + a few more characters?
Anyways, I don't really need the "result"... I really only need a "true" / "false" if "Find" found "End of Report" or not... How can I accomplish this? Is there a different better way to do this? I am not familiar with command prompt programming.
Note: It is important that I search these files without opening them.
Sub test()
Dim SearchStr As String
Dim cmdLine As Object
Dim result As String
Dim FilePath As String
FilePath = "D:\test2.txt"
SearchStr = """End of Report"""
Set cmdLine = CreateObject("WScript.Shell")
result = cmdLine.Exec("%comspec% /C Find " & SearchStr & " " & Chr(34) & FilePath & Chr(34)).STDOut.ReadAll
Debug.Print (result)
End Sub
I am not really an expert in command line, but what I would do is export the result of the FIND command to a file, like here
Then I would check in your VBA code how many rows are in the file (either clean the file before, or check the number of rows before the export is done).
If the number of rows meets the criteria (probably 2 or more rows instead of 1), then you can set the flag to True.

Processing a Byte Order Mark

I've been writing code to process xml downloaded via a webservice API. I was going ok until one query had some mysterious characters before the root element.
After contacting the support, I got the following message...
"The ABS.Stat APIs resultant XML output are UTF-8 compliant. These characters are a UTF-8 Byte Order Mark designed to identify the xml as UTF-8. Hope this helps."
Whilst waiting for their reply I continued with my programming by simply starting my DOM processing at the opening tag (first "<") with the following code...
Dim lgRootElementStart As Long
lgRootElementStart = InStr(1, hReq.ResponseText, "<")
Dim sgResponse As String
sgResponse = Mid(hReq.ResponseText, lgRootElementStart)
Dim xmlDoc As New MSXML2.DOMDocument
If Not xmlDoc.LoadXML(sgResponse) Then
etc. etc. etc.
All seems to be well, the data is deciphered and displayed ok.
But now that I know what those characters are, is there anything I should do with those characters?
Or to put it another way, is there anything I can do with those characters to make my excel application more reliable? i.e. now that I know the XML is UTF-8, how should I process it differently?
What should I do if the BOM gives UTF-16?
Well it seems that the BOM is more a nuisance than helpful, but I placed code in my application to check that it is a UTF8 BOM if any characters before the xml root element are received. If it's not a UTF8 BOM then an error is thrown. I'm not expecting this to be a problem any more, but if I ever see the error then I will have to re-analyse what's going on. Hopefully that will never happen.
Code is...
Public Const BOM_UTF8 As String = ""
and
If lgRootElementStart > 1 Then
If Left(hReq.ResponseText, lgRootElementStart - 1) = BOM_UTF8 Then
Else
Err.Raise ERROR_SHOULD_NEVER_HAPPEN, sFunctionName, _
"Non UTF8 BOM found. " _
& "BOM is ..." & ConvertToHex(Left(hReq.ResponseText, lgRootElementStart - 1)) _
& ", correct BOM is ... " & ConvertToHex(BOM_UTF8)
End If
End If
One quote from a link in the comments says..."Encodings should be known, not divined". Well with this code I know it's UTF8 if I get it.

VB .Net when exporting to CSV issue when viewing in MS Excel

I have encountered something really weird. When exporting to CSV my top line shows the quotation marks yet the lines below down.
I use UTF8 encoding and manually add the double quotation marks to the value so that it is encased with quotation marks.
the code being used is
Dim fs As New IO.FileStream(GenericValueEditorExportFilename.Value, IO.FileMode.Create)
Dim writer As New IO.StreamWriter(fs, Encoding.UTF8)
fs.Write(Encoding.UTF8.GetPreamble(), 0, Encoding.UTF8.GetPreamble().Length)
....
....
....
While reader.Read
If reader("TargetLanguageID") = targetLanguageID Then
writer.WriteLine(Encode(reader("SourcePhrase")) & ", " & Encode(reader("TargetPhrase")))
End If
....
....
....
Friend Shared Function Encode(ByVal value As String) As String
Return ControlChars.Quote & value.Replace("""", """""") & ControlChars.Quote
End Function
the result when displayed in excel is shown as (https://ibb.co/ntMYdw)
when i open the file in Notepad++ the text is shown as below. But each line is displayed differently. Why is it that the 1st row displays them and the 2nd does not. Notepad++ result is displayed as (https://ibb.co/fMkWWG)
Excel is treating the first line as headers.
https://stackoverflow.com/a/24923167/2319909
So the issue was being caused by the BOM that was created to manually set the encoding for the file as a start writing to the file.
fs.Write(Encoding.UTF8.GetPreamble(), 0, Encoding.UTF8.GetPreamble().Length)
Removing this resolves by issue and the file remains in the desired UTF8 encoding as it is set on the stream writer. so there is no need to add the BOM to set the encoding.
Something like this should work for you.
Dim str As New StringBuilder
For Each dr As DataRow In Me.NorthwindDataSet.Customers
For Each field As Object In dr.ItemArray
str.Append(field.ToString & ",")
Next
str.Replace(",", vbNewLine, str.Length - 1, 1)
Next
Try
My.Computer.FileSystem.WriteAllText("C:\temp\testcsv.csv", str.ToString, False)
Catch ex As Exception
MessageBox.Show("Write Error")
End Try

After a WMI search in VBScript, can I create my search filter BEFORE my "For Each" statement?

I've created an alternative search utility to the Windows search utility with VBScript using a WQL search, but, as it turns out, it's quite slow. I would like to speed it up and I think I can do it, but I would need to place my search filter AFTER my WQL search and BEFORE my For Each statement. Is this even possible?
I've already tested by filtering in the WQL search, but it's about 40% faster if I filter after the WQL search. I've also tested with and without iFlags, but they tend to slow the search quite a bit, even though MS seems to believe otherwise.
Since the user can search by filename, creation date, last modified date and/or file size, if the filter is after the For Each statement then the script has to create the search filter each time it enumerates a file. I'd like to create the filter once in the hope of shaving some time off the search.
This will probably make better sense when you take a look at the snippet of code I've posted. Note that the sub subCreateSearchString will have calls to other search options and functions (ie: convert from UTC to local time, format file sizes, etc.)
Dim strSearchName, strComputer, objSWbemServices, objFile, colFiles
Dim strFileName, strReturnedFileName, strQueryDriveAndPath
strSearchName = "test" 'Text being searched for - change as needed
strQueryDriveAndPath = "PATH = '\\Drop_RW\\' AND DRIVE = 'D:'" 'Path and drive in which to search - change as needed
strComputer = "."
Set objSWbemServices = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colFiles = objSWbemServices.ExecQuery("Select * from CIM_DataFile WHERE " & "" & strQueryDriveAndPath & "")
'* I'd like to place the call to "subCreateSearchString" here
On Error Resume Next
For Each objFile in colFiles
strReturnedFileName = objFile.Name
subCreateSearchString ' Search filter - it works when placed here
If strSearchForString Then
MsgBox "File matches:" & vbCrLf & strReturnedFileName
Else
MsgBox "File DOES NOT match" & vbCrLf & strReturnedFileName
End If
Next
Sub subCreateSearchString
'* Set Filename Variable for search:
strFileName = InStr(LCase(strReturnedFileName), LCase(strSearchName))
strSearchForString = strFileName
End Sub
Since you depend on the names of the files you're iterating over in the For Each loop: no, not possible.
I'd strongly recommend making some adjustments, though.
Use a Function rather than a Sub if you want to return something from a subroutine.
Avoid using global variables. They have a nasty tendency of introducing undesired side effects and also make debugging your code a pain in the rear. Pass values into your subroutines via parameters, and return values as actual return values.
The returned value is an integer (or Null), but you use it like a boolean and named your variables (and sub) as if it were a string. Don't do that. Name your functions/procedures after what they're doing, and name your variables after what they contain. And if you want to use a boolean value make your function actually return a boolean value.
Avoid Hungarian Notation. It's pointless code-bloat the way most people use it. Even more if your naming doesn't even match the actual type.
Do not use global On Error Resume Next. Ever. It simply makes your code fail silently without telling you anything about what actually went wrong. Keep error handling as local as possible. Enable it only for single commands or short code blocks, and only if there is no other way to avoid/handle the error.
Function IsInFilename(searchName, fileName)
IsInFilename = InStr(LCase(fileName), LCase(searchName)) > 0
End Function
For Each objFile in colFiles
If IsInFilename(strSearchName, objFile.Name) Then
MsgBox "..."
Else
MsgBox "..."
End If
Next

Write Chinese chars to a text file using vbscript

I'm trying to write some Chinese characters to a text file using
Set myFSO = CreateObject("Scripting.FileSystemObject")
Set outputFile = myFSO.OpenTextFile(getOutputName(Argument, getMsiFileName(Wscript.Arguments)), forWriting, True)
outputFile.WriteLine(s)
The variable s contains a Chinese character that I read from the other file. I echo s value and I can see the s correctly in the screen. However, for some reason the script stops running after outputFile.WriteLine(s) without returning any error message.
Am I missing something?
Maybe it's got something to do with character encoding. Try directly specifying the Unicode format for the file in the last parameter of the OpenTextFile method:
Const Unicode = -1
Set outputFile = myFSO.OpenTextFile(getOutputName(Argument, getMsiFileName(Wscript.Arguments)), forWriting, True, Unicode)
Also, you need to close the file after writing to it:
outputFile.Close
If this doesn't help, try error handling like AnthonyWJones suggested.
Try this:-
MsgBox "Writing Line"
On Error Resume Next
outputFile.WriteLine s '' # Removed ( ) that shouldn't be there.
MsgBox "Err " & Err.Number & ": " & Err.Description
On Error GoTo 0
What do you get?
Things had changed along win versions. This works on Win10:
Set outputfile = myFSO.CreateTextFile(filename,True,True)
The 3rd arg is bool true/false for unicode/ascii 2. Surprisingly, when using myFSO.OpenTextFile the arg is int with 1 (not -1) for unicode 1.
Documentation:
https://msdn.microsoft.com/en-us/library/aa265018(v=vs.60).aspx
https://msdn.microsoft.com/en-us/library/aa265347(v=vs.60).aspx

Resources