Search code examples
excelvbapowershellencoding

How can I solve this special character encoding problem between VBA and Powershell?


For a larger vba application I need to use powershell scripts to give me the filenames on a share. As I live in Germany, these filenames contain a lot of german special characters like ü, ä, and so on.

The output of the powershell script, that should (and on the PS Console does) display those characters correctly is not displayed correctly in VBA. I've made up this minimal example to demonstrate this.

Any solution that gives back für instead of fr is appreciated.

Option Explicit
Const HK = """"

Sub ScanDrive()
    Dim s As String, command As String
   
    command = "echo 'für'"
    Debug.Print (command)
    'Method1: start PS in CMD with /u parameter
    s = CreateObject("Wscript.Shell").exec("cmd /u /c powershell.exe -command " & HK & command & HK).StdOut.ReadAll
    Debug.Print (s)
    'Method2: start PS directly (preferred)
    s = CreateObject("Wscript.Shell").exec("powershell.exe -command " & HK & command & HK).StdOut.ReadAll
    Debug.Print (s)
End Sub

My output looks like this (though vba doesn't display the `````` character at all):

echo 'für'
fr

fr

I've already learned that I can change the PS output encoding when I'm writing to a file-output. Unfortunately this will not be an option.

In my understanding I'd have to make Excel read UTF-8 (I guess, that's what it is) or make the PS Console write out Win-1252 (also just a guess, that this is, what Excel uses).

As I have to deploy this script in a company I cannot rely on any global switches for the PS Console.

Update

According to JosefZ 's comment I put the command as [System.Console]::OutputEncoding and got the following in VBA:

IsSingleByte      : True
BodyName          : ibm850
EncodingName      : Westeurop„isch (DOS)
HeaderName        : ibm850
WebName           : ibm850
WindowsCodePage   : 1252
IsBrowserDisplay  : False
IsBrowserSave     : False
IsMailNewsDisplay : False
IsMailNewsSave    : False
EncoderFallback   : System.Text.InternalEncoderBestFitFallback
DecoderFallback   : System.Text.InternalDecoderBestFitFallback
IsReadOnly        : True
CodePage          : 850

as opposed to running it directly in the powershell where it states:

IsSingleByte      : True
BodyName          : iso-8859-1
EncodingName      : Westeuropäisch (Windows)
HeaderName        : Windows-1252
WebName           : Windows-1252
WindowsCodePage   : 1252
IsBrowserDisplay  : True
IsBrowserSave     : True
IsMailNewsDisplay : True
IsMailNewsSave    : True
EncoderFallback   : System.Text.InternalEncoderBestFitFallback
DecoderFallback   : System.Text.InternalDecoderBestFitFallback
IsReadOnly        : True
CodePage          : 1252

The CreateObject("Wscript.Shell") seems to work on Dos encoding and CP850.


Solution

  • Luckily, I found the solution.

    JosefZ's comment put me on the right track and led me to this underrated answer from Garric to another question.

    Adapted his solution to my original minimal example code gives this:

    Option Explicit
    Const HK = """"
    
    Sub ScanDrive()
        Dim s As String, command As String
        Dim codepage As String
        codepage = "windows-1252"
        command = "$OutputEncoding = [Console]::outputEncoding = [System.Text.Encoding]::GetEncoding('" + codepage + "'); echo 'für'"
        Debug.Print (command)
        s = CreateObject("Wscript.Shell").Exec("powershell.exe -command " & HK & command & HK).StdOut.ReadAll
        Debug.Print (s)
    End Sub
    

    Et Voilá - We have our german Umlauts correctly :)