Results 1 to 5 of 5

Thread: Is wvnsprintf worth anything to us?

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Is wvnsprintf worth anything to us?

    I dug through some old code recently and saw some really strange stuff. One of these was use of the wvnsprintf() function in shlwapi.dll so I decided to play with making a wrapper class for it.

    There are a few nuisance things like the way C handles varying length argument lengths, the va_list. The macros they use are not clearly documented so a lot had to be reversed engineered through some trial and error.


    The wvnsprintf() function is basically a cut down STDCALL equivalent of _vsnwprintf() in CRT libraries. The major omission is floating-point formatting but there also seem to be a number of other differences and quirks.

    Here are a few formats that work:

    Code:
    Option Explicit
    
    Private Sub Prt(ParamArray Text() As Variant)
        Dim I As Long
    
        With Text1
            .SelStart = &H7FFF
            For I = 0 To UBound(Text)
                .SelText = Text(I)
            Next
            .SelText = vbNewLine
        End With
    End Sub
    
    Private Sub Form_Load()
        Dim LongLong As Variant
    
        With New SPrintF
            'Unicode char (c) format which ignores the upper word::
            Prt "[", .SPrintF(10, "%c", 34), "]"
            Prt "[", .SPrintF(10, "%c", &HFFFF0141), "]" 'The "L with stroke," shows ANSI L in the TextBox.
            'ANSI char (C) format which ignores the upper 3 bytes:
            Prt "[", .SPrintF(10, "%C", &HABCD141), "]" 'Produces "A" (&H41).
            Prt
            'Strings:
            Prt "[", .SPrintF(10, "%s", "Test"), "]"
            Prt "[", .SPrintF(10, "%5s", "Test"), "]"
            Prt "[", .SPrintF(10, "%-5s", "Test"), "]"
            Prt "[", .SPrintF(10, "%.3s", "Test"), "]"
            Prt "[", .SPrintF(30, "%s, %s, and %s", "Test", "test", "testing"), "]"
            'Note that a vbNullChar will terminate strings early:
            Prt "[", .SPrintF(30, "%s, %s, and %s", "Test", "test", "test" & vbNullChar & "ing"), "]"
            'Percent (%) insertion after format spec:
            Prt "[", .SPrintF(10, "%s%%", "39.2"), "]"
            Prt
            '"Decimal" (as in not hex) integers:
            Prt "[", .SPrintF(10, "%+d %+d", -678, 678), "]"
            Prt "[", .SPrintF(10, "%d", 888), "]"
            Prt "[", .SPrintF(10, "%5d", 666), "]"
            Prt "[", .SPrintF(10, "%*d", 5, 111), "]"
            Prt "[", .SPrintF(10, "%-*d", 5, 222), "]"
            'These give leading 0's two different ways:
            Prt "[", .SPrintF(10, "%.5d", 777), "]"
            Prt "[", .SPrintF(10, "%0*d", 5, 333), "]"
            'These use "invisible +" i.e. a space for + and - for -:
            Prt "[", .SPrintF(10, "% d", -123), "]"
            Prt "[", .SPrintF(10, "% d", 123), "]"
            Prt "[", .SPrintF(10, "% *d", 7, -123), "]"
            Prt "[", .SPrintF(10, "% *d", 7, 123), "]"
            Prt "[", .SPrintF(10, "% -*d", 7, -123), "]"
            Prt "[", .SPrintF(10, "% -*d", 7, 123), "]"
            'Use the (i) instead of the (d), same result:
            Prt "[", .SPrintF(10, "% -*i", 7, 123), "]"
            Prt
            'Use a "sHort" i.e. Integer, 16-bit (h) format which ignores the upper word:
            Prt "[", .SPrintF(10, "%hd", &H7FFF0020), "]"
            Prt
            Prt "[", .SPrintF(10, "%X", &HEEDDCC), "]"
            Prt "[", .SPrintF(10, "%x", &HEEDDCC), "]"
            Prt "[", .SPrintF(10, "%8x", &HEEDDCC), "]"
            Prt "[", .SPrintF(10, "%-8x", &HEEDDCC), "]"
            Prt "[", .SPrintF(10, "%08x", &HEEDDCC), "]"
            'Pass two Long values but use a LongLong (ll - two lowercase L) format:
            Prt "[", .SPrintF(20, "%016llx", &HFFEEDDCC, 0), "]"
            Prt "[", .SPrintF(18, "&H%016llX", &H12345678, &HFFEEDDCC), "]"
            Prt
            'Pass a 64-bit LongLong, use the alternate 64-bit size (I64) format:
            LongLong = (.CLongLong(&H7FFFFFFF) + 1) * &H10000 * &H10000 _
                    Or (.CLongLong(&HACBDCED) * &H10 Or &HF&)
            Prt "[", .SPrintF(22, "%I64x", LongLong), "]"
            Prt "[", .SPrintF(22, "%I64X", LongLong), "]"
            Prt
            'Pass some VB-native 64-bit types:
            Prt "[", .SPrintF(22, "%016I64x", 1.0001@), "]"
            Prt "[", .SPrintF(22, "%016I64x", 1#), "]"
            Prt "[", .SPrintF(22, "%016I64X", #1/31/2001 11:59:59 PM#), "]"
            Prt
            'Octal:
            Prt "[", .SPrintF(22, "%llo", &H12345678, &HFFEEDDCC), "]"
            Prt "[", .SPrintF(22, "%I64o", &H12345678, &HFFEEDDCC), "]"
        End With
    End Sub
    
    Private Sub Form_Resize()
        If WindowState <> vbMinimized Then
            Text1.Move 0, 0, ScaleWidth, ScaleHeight
        End If
    End Sub
    Results:

    Code:
    ["]
    [L]
    [A]
    
    [Test]
    [ Test]
    [Test ]
    [Tes]
    [Test, test, and testing]
    [Test, test, and test]
    [39.2%]
    
    [-678 +678]
    [888]
    [  666]
    [  111]
    [222  ]
    [00777]
    [00333]
    [-123]
    [ 123]
    [   -123]
    [    123]
    [-123   ]
    [ 123   ]
    [ 123   ]
    
    [32]
    
    [EEDDCC]
    [eeddcc]
    [  eeddcc]
    [eeddcc  ]
    [00eeddcc]
    [00000000ffeeddcc]
    [&HFFEEDDCC12345678]
    
    [80000000acbdcedf]
    [80000000ACBDCEDF]
    
    [0000000000002711]
    [3ff0000000000000]
    [40E2075FFFE7BA37]
    
    [1777566734602215053170]
    [1777566734602215053170]

    Even though it doesn't handle floating-point formatting you can still use it to dump 64-bit types in hex, so that might be useful I suppose. But we can do pretty much anything in VB that this function can do even if it can take a few lines of code.

    For example if we really want to dump a Double in hex we can always use UDTs, CopyMemory, etc. to convert it into two Longs.


    Rather than just throw it away I decided to post it here instead of the CodeBank. I don't think many people will find it very useful but opinions might be interesting to read.
    Attached Files Attached Files

  2. #2
    Frenzied Member
    Join Date
    Jun 2015
    Posts
    1,057

    Re: Is wvnsprintf worth anything to us?

    [...]
    Last edited by dz32; Apr 26th, 2019 at 11:20 AM.

  3. #3
    Hyperactive Member
    Join Date
    Aug 2017
    Posts
    380

    Re: Is wvnsprintf worth anything to us?

    Quote Originally Posted by dz32 View Post
    a system supported api version is surly more robust ...
    I'm not so sure about that...

    Quote Originally Posted by MSDN
    wvnsprintf function

    Note Do not use this function. See Remarks for alternative functions.



    Remarks

    Security Warning: Using this function incorrectly can compromise the security of your application. The copied string is not guaranteed to be null-terminated. Consider using one of the following alternatives. StringCbPrintf, StringCbPrintfEx, StringCbVPrintf, StringCbVPrintfEx, StringCchPrintf, StringCchPrintfEx, StringCchVPrintf, or StringCchVPrintfEx. You should review Security Considerations: Microsoft Windows Shell before continuing.

  4. #4

    Thread Starter
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: Is wvnsprintf worth anything to us?

    Those warnings are accurate but they are also plastered over tons of APIs we use all of the time. They are really disclaimers, advising you to be very careful.

    The "alternatives" are macros that call bounds checking routines and finally end up calling "unsafe" entrypoints themselves. To be safe you'd want to add similar logic to a wrapper class.

    But for that matter calling CopyMemory is far more unsafe and people blindly copy/paste that willy-nilly.

  5. #5
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Is wvnsprintf worth anything to us?

    RE: Those warnings. If using VB strings/property values vs string pointers from elsewhere, that specific warning doesn't apply. VB strings are always null terminated. I'd imagine anyone using this will be using VB-related strings a vast majority of the time.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width