1

I am trying to write text to Win32 resources, but I have failed with it.

Here it is after writing the text: img1 And here is how it should look like: img1

Here's my code:

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        WriteResourceStr(Target.Text, "hello")
        End Sub

#Region "Second"
    <DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)> _
    Private Shared Function UpdateResource(ByVal hUpdate As IntPtr, ByVal lpType As String, ByVal lpName As String, ByVal wLanguage As UShort, ByVal lpData As IntPtr, ByVal cbData As UInteger) As Boolean
    End Function
    <DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)> _
    Private Shared Function BeginUpdateResource(ByVal pFileName As String, <MarshalAs(UnmanagedType.Bool)> ByVal bDeleteExistingResources As Boolean) As IntPtr
    End Function
    <DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)> _
    Private Shared Function EndUpdateResource(ByVal hUpdate As IntPtr, ByVal fDiscard As Boolean) As Boolean
    End Function

    Public Function WriteResourceStr(ByVal filename As String, ByVal bytes As String) As Boolean

        Try
            Dim handle As IntPtr = BeginUpdateResource(filename, False)
            Dim file1 As String = bytes
            Dim fileptr As IntPtr = ToPtr(file1)
            Dim res As Boolean = UpdateResource(handle, "RCData", "CONFIG", 1, fileptr, System.Convert.ToUInt16(file1.Length))
            EndUpdateResource(handle, False)
        Catch ex As Exception
            Return False
        End Try
        Return True

    End Function

    Private Function ToPtr(ByVal data As Object) As IntPtr
        Dim h As GCHandle = GCHandle.Alloc(data, GCHandleType.Pinned)
        Dim ptr As IntPtr
        Try
            ptr = h.AddrOfPinnedObject()
        Finally
            h.Free()
        End Try
        Return ptr

    End Function
#End Region

So seems like it doesn't write ANSI, but in Unicode. How to change that?

Hopefully somebody replies.

Community
  • 1
  • 1
user2404495
  • 195
  • 1
  • 6
  • 17
  • *"So seems like it doesn't write UTG, but in Unicode"* - That doesn't make sense. Read [The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)](http://www.joelonsoftware.com/articles/Unicode.html). – IInspectable Oct 17 '13 at 13:40
  • The resource spy utility you use is not very good, it gives you the wrong idea of what the resource actually looks like. It doesn't seem capable of distinguishing strings from numbered resource types, RCData is type number 10, not a string. It also does weird things with the language identifier. The best tool to use is Visual Studio, use File + Open + File and select the executable. Retail edition required, doesn't work on Express. Use, say, the Encoding.Default.GetBytes() method to convert the string to bytes. – Hans Passant Oct 17 '13 at 14:40
  • @Hans You are correct, but ResourceHacker/CFF Explorer is very good tool for viewing/editing/manipulating native resources. Also, the RT_RCData is just a test section, I have tested with different sections and languages, and it doesn't affect to it in any way as it is a custom resource(?) Anyhow, still looking for help. I've tried to do it whole last night and this day, but just can't store the text as pure ANSI string. It always stores the text as UNICODE and therefore the text has _dots_ between the letters, that should be fixed but I have no idea how to do it. Somebody please help me.. :) – user2404495 Oct 17 '13 at 16:56
  • There are no *"dots between the letters"* - it's just what ResourceHacker uses to display ANSI strings. If you want to display UNICODE strings there's nothing holding you back. In fact, it's more troublesome **not** using UNICODE strings. As for storing strings in resources, this is not quite as straight forward as it may appear (see [this](http://stackoverflow.com/questions/14088057/updating-a-string-table-with-updateresource)). – IInspectable Oct 17 '13 at 17:10

1 Answers1

0

The simplest way to get this is to just overload UpdateResource and let Windows make the Unicode to ANSI conversion for you:

<DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Ansi)> _
Private Shared Function UpdateResource(ByVal hUpdate As IntPtr, _
    ByVal lpType As String, ByVal lpName As String, ByVal wLanguage As UShort, 
    ByVal lpData As String, ByVal cbData As Integer) As Boolean
End Function

Note the changed lpData type and the changed CharSet. The call now simply becomes:

    Dim handle As IntPtr = BeginUpdateResource(filename, False)
    If handle = IntPtr.Zero Then Throw New Win32Exception
    Dim res As Boolean = UpdateResource(handle, "RCData", "CONFIG", 1, _
       bytes, bytes.Length)
    If Not EndUpdateResource(handle, False) Then Throw New Win32Exception

I'll have to restate the nonsensical nature of the call. RCData is a numbered resource type and not a string. Using a language ID of 1 makes little sense, that's Arabic so you wouldn't expect a Latin string in the resource. Whatever app reads this resource is unlikely to find it back.

Doing this correctly would require an overload that declares lpType as IntPtr so you can pass CType(10, IntPtr) for the RT_RCData resource type. The ToPtr() function is extremely evil, it returns a dangling pointer that will cause random data corruption. Just let the pinvoke marshaller generate the pointer by declaring the lpData argument as Byte(). You'd then use Encoding.GetBytes() to use the proper ANSI conversion. Thus:

<DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)> _
Private Shared Function UpdateResource(ByVal hUpdate As IntPtr, _
    ByVal lpType As IntPtr, ByVal lpName As String, ByVal wLanguage As UShort, 
    ByVal lpData As Byte(), ByVal cbData As Integer) As Boolean
End Function

With an additional overload required if lpName is a numbered instead of a named resource, use IntPtr.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536