This question is related to Visual Basic .NET 2010
How do I enumerate the "native" resources (such as icon, version info, etc) of a win32 executable file?
I found little to no documentation on it, and what I found simply did not work. I've tried the example provided here so far http://www.pinvoke.net/default.aspx/kernel32.EnumResourceNames. It's all I could find on the subject.
Help is much appreciated.
It isn't that clear what your hangup might be. The declarations you found at pinvoke.net are only so-so, not an uncommon problem. The probable hangup is that both the resource type and the resource name can either be a number or a string. So the pinvoke declaration should declare them as IntPtr and, if they have the right value, convert them to a string dynamically.
Another thing you have to do is to first enumerate the resource types in the file, then enumerate the resources of that type.
Some code to play with:
Imports System.Runtime.InteropServices
Imports System.ComponentModel
Imports System.Linq
Module Module1
Sub Main()
Dim handle = LoadLibrary("user32.dll")
If handle = IntPtr.Zero Then
Throw New Win32Exception
End If
If Not EnumResourceTypes(handle, AddressOf EnumTypesCallback, IntPtr.Zero) Then
Throw New Win32Exception
End If
Console.ReadLine()
End Sub
Function EnumTypesCallback(hModule As IntPtr, lpszType As IntPtr, lParam As IntPtr) As Boolean
EnumResourceNames(hModule, lpszType, AddressOf EnumResourceCallback, IntPtr.Zero)
Return True
End Function
Function EnumResourceCallback(hModule As IntPtr, lpszType As IntPtr, ByVal lpszName As IntPtr, ByVal lParam As IntPtr) As Boolean
Dim type As String = lpszType.ToInt32().ToString()
If [Enum].GetValues(GetType(ResourceType)).Cast(Of Integer).Contains(lpszType.ToInt32()) Then
type = [Enum].GetName(GetType(ResourceType), lpszType.ToInt32())
ElseIf lpszType.ToInt32() > &HFFFF Then
type = Marshal.PtrToStringUni(lpszType)
End If
Dim name As String = lpszName.ToInt32().ToString()
If lpszName.ToInt32() > &HFFFF Then
name = Marshal.PtrToStringUni(lpszName)
End If
Console.WriteLine("Resource type={0}, id={1}", type, name)
Return True
End Function
Private Delegate Function EnumResNameProcDelegate(ByVal hModule As IntPtr, ByVal lpszType As IntPtr, ByVal lpszName As IntPtr, ByVal lParam As IntPtr) As Boolean
Private Delegate Function EnumResTypeProc(hModule As IntPtr, lpszType As IntPtr, lParam As IntPtr) As Boolean
<DllImport("kernel32.dll", CharSet:=CharSet.Unicode, SetLastError:=True)> _
Public Function LoadLibrary(ByVal lpFileName As String) As IntPtr
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Unicode, SetLastError:=True)> _
Private Function EnumResourceTypes(ByVal hModule As IntPtr, ByVal lpEnumFunc As EnumResTypeProc, ByVal lParam As IntPtr) As Boolean
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Unicode, SetLastError:=True)> _
Private Function EnumResourceNames(ByVal hModule As IntPtr, ByVal lpszType As IntPtr, ByVal lpEnumFunc As EnumResNameProcDelegate, ByVal lParam As IntPtr) As Boolean
End Function
Public Enum ResourceType
CURSOR = 1
BITMAP = 2
ICON = 3
MENU = 4
DIALOG = 5
[STRING] = 6
FONTDIR = 7
FONT = 8
ACCELERATOR = 9
RCDATA = 10
MESSAGETABLE = 11
GROUP_CURSOR = 12
GROUP_ICON = 14
VERSION = 16
DLGINCLUDE = 17
PLUGPLAY = 19
VXD = 20
ANICURSOR = 21
ANIICON = 22
HTML = 23
MANIFEST = 24
End Enum
End Module
Output on Windows 8.1:
Resource type=MUI, id=1
Resource type=WEVT_TEMPLATE, id=1
Resource type=CURSOR, id=92
Resource type=CURSOR, id=93
etc...
Resource type=BITMAP, id=36
Resource type=BITMAP, id=136
etc..
Resource type=ICON, id=1
Resource type=ICON, id=2
etc..
Resource type=MENU, id=1
Resource type=MENU, id=4
etc..
Resource type=DIALOG, id=9
Resource type=STRING, id=1
Resource type=STRING, id=44
etc..
Resource type=MESSAGETABLE, id=1
Resource type=GROUP_CURSOR, id=100
Resource type=GROUP_CURSOR, id=101
etc..
Resource type=GROUP_ICON, id=100
Resource type=GROUP_ICON, id=101
etc..
Resource type=VERSION, id=1
Resource type=ANICURSOR, id=32665
Resource type=ANICURSOR, id=32666
Resource type=ANICURSOR, id=32669
Compare to what you see when you use File + Open + File, select c:\windows\syswow64\user32.dll (won't work on Express).
Related
I'm working on a VB.Net project where in part of it I catch a pop up message box when it's displayed and handle it in some way.
My problem is that I have to know what buttons are included in this pop up window (their captions mainly).
Is this possible? Can someone please tell me how this can be done? A sample would be much appreciated.
Thanks.
Update:
Since I got a down vote, and Ed Cottrell told me that this is because of not adding any code related to my question. Now I have the answer to my question so I'm adding some code:
My app catches a pop-up window displayed by another windows application, I used EnumWindows API to know when a new pop-up window is displayed.
Public Declare Function EnumWindows Lib "User32.dll" (ByVal WNDENUMPROC As EnumWindowDelegate, ByVal lparam As IntPtr) As Boolean
Delegate Function EnumWindowDelegate(ByVal hWnd As IntPtr, ByVal Lparam As IntPtr) As Boolean
When I catch this window, I use its handle that I got from EnumWindows result and get its child windows using EnumChildWindows (which are the controls I'm looking for , since controls are kind of windows too):
APIs I used
<DllImport("User32.dll")> _
Public Function EnumChildWindows _
(ByVal WindowHandle As IntPtr, ByVal Callback As EnumWindowProcess, _
ByVal lParam As IntPtr) As Boolean
End Function
Public Delegate Function EnumWindowProcess(ByVal Handle As IntPtr, ByVal Parameter As IntPtr) As Boolean
' Get window text length signature.
Public Declare Function SendMessage _
Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Int32, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As Int32) As Int32
' Get window text signature.
Public Declare Function SendMessage _
Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Int32, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As StringBuilder) As Int32
Structure ApiWindow
Public Structure ApiWindow
Public MainWindowTitle As String
Public ClassName As String
Public hWnd As Int32
End Structure
Functions
Public Function GetChildWindows(ByVal ParentHandle As IntPtr) As IntPtr()
Dim ChildrenList As New List(Of IntPtr)
Dim ListHandle As GCHandle = GCHandle.Alloc(ChildrenList)
Try
EnumChildWindows(ParentHandle, AddressOf EnumWindow, GCHandle.ToIntPtr(ListHandle))
Finally
If ListHandle.IsAllocated Then ListHandle.Free()
End Try
Return ChildrenList.ToArray
End Function
Public Function EnumWindow(ByVal Handle As IntPtr, ByVal Parameter As IntPtr) As Boolean
Dim ChildrenList As List(Of IntPtr) = GCHandle.FromIntPtr(Parameter).Target
If ChildrenList Is Nothing Then Throw New Exception("GCHandle Target could not be cast as List(Of IntPtr)")
ChildrenList.Add(Handle)
Return True
End Function
Now when I have the handle of pop-up window (parentHandle), I can get its children windows:
Dim hndls() As IntPtr = GetChildWindows(parentHandle)
Dim window As ApiWindow
For Each hnd In hndls
window = GetWindowIdentification(hnd)
'Add Code Here
Next
Where GetWindowIdentification is:
''' <summary>
''' Build the ApiWindow object to hold information about the Window object.
''' </summary>
Public Function GetWindowIdentification(ByVal hwnd As Integer) As ApiWindow
Const WM_GETTEXT As Int32 = &HD
Const WM_GETTEXTLENGTH As Int32 = &HE
Dim window As New ApiWindow()
Dim title As New StringBuilder()
' Get the size of the string required to hold the window title.
Dim size As Int32 = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0)
' If the return is 0, there is no title.
If size > 0 Then
title = New StringBuilder(size + 1)
SendMessage(hwnd, WM_GETTEXT, title.Capacity, title)
End If
' Set the properties for the ApiWindow object.
window.MainWindowTitle = title.ToString()
window.hWnd = hwnd
Return window
End Function
I found this answer on the net on another website, I thought I should share it in case someone sees this question in the future:
Buttons are nothing but more windows. You just look for more child
windows of the message box window you found. You can NOT just get
the handle from a window and then try to cast it to a Form object and
expect all the properties to work. It just doesn't work that way.
I am working in vb with visual studio 2010.
The app AnyOrder is no longer supported and I am trying to make an app where our users can type in info and then have the info populate into AnyOrder (and also other sites - they have to populate a lot of redundant info) but I am having no luck at all getting the child windows of any order to populate once the data is in the VB window app.
I can get the parent window handle just fine but can't seem to get a child window handle to save my life. I can't even get the first level down and the blanks that I will need to populate are great grand-children of the parent window.
It won't let me post a screencap of the spy++ since I just signed up and don't have the 10 rep but here is a link to the capture.
Thank you in advance for any assistance that you can provide.
Figured out the answer somehow.
#Region "functions"
Private Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Int32
Public Delegate Function EnumWindowProcess(ByVal Handle As IntPtr, ByVal Parameter As IntPtr) As Boolean
<DllImport("User32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function EnumChildWindows(ByVal WindowHandle As IntPtr, ByVal Callback As EnumWindowProcess, ByVal lParam As IntPtr) As Boolean
End Function
Private Shared Function EnumWindow(ByVal Handle As IntPtr, ByVal Parameter As IntPtr) As Boolean
Dim ChildrenList As List(Of IntPtr) = CType(GCHandle.FromIntPtr(Parameter).Target, Global.System.Collections.Generic.List(Of Global.System.IntPtr))
ChildrenList = CType(GCHandle.FromIntPtr(Parameter).Target, Global.System.Collections.Generic.List(Of Global.System.IntPtr))
If ChildrenList Is Nothing Then Throw New Exception("GCHandle Target could not be cast as List(Of IntPtr)")
ChildrenList.Add(Handle)
Return True
End Function
Private Shared Function GetChildWindows(ByVal ParentHandle As IntPtr) As IntPtr()
Dim ListHandle As GCHandle = GCHandle.Alloc(ChildrenList)
Try
EnumChildWindows(ParentHandle, AddressOf EnumWindow, GCHandle.ToIntPtr(ListHandle))
Finally
If ListHandle.IsAllocated Then ListHandle.Free()
End Try
Return ChildrenList.ToArray
End Function
#End Region
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
iParentHandle = FindWindow(vbNullString, AOParentName)
GetChildWindows(FindWindow(vbNullString, AOParentName))
Dim Childlist As String = String.Join(". ", ChildrenList)
MsgBox("list of child windows: " & Childlist)
End Sub
I am trying to write text to Win32 resources, but I have failed with it.
Here it is after writing the text:
And here is how it should look like:
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.
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.
Lets say I have two windows form applications written in vb.NET, App1 and App2.
App1 and 2 are two completely separated solutions.
Is it possible to share a panel(or another container) in App1 so that App2 can control the content of this panel?
By control the content i mean add textboxs and buttons that will trigger events in App2.
We have perform that duty before at my enterprise and we use some WinAPI to communicate between windows:
You have to DLLImport the following user32.dll functions:
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Integer
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, _
ByVal lParam As IntPtr) As Integer
You use the SendMessage function from App1 to send the message to App2.
You can listen to Window messages overriding the WndProc Sub in the App2 application:
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_COPYDATA Then
'Read the message and perform some task,
'Create textboxes, modify controls, whatever
Else
MyBase.WndProc(m)
End If
End Sub
An example to send the Window Message through the Application 1 would be the following:
Dim hWnd As IntPtr
Dim mCopyData As COPYDATASTRUCT
hWnd = FindWindow(Nothing, App2WindowName)
Dim message As New System.Text.StringBuilder
If (hWnd <> 0) Then
message.Append(Mensaje)
Dim pCopyData As IntPtr = Marshal.AllocHGlobal(message.Length() + 40)
mCopyData.lpData = Marshal.StringToHGlobalAnsi(message.ToString)
mCopyData.cbData = message.Length
mCopyData.dwData = _messageID
Marshal.StructureToPtr(mCopyData, pCopyData, False)
SendMessage(hWnd, WM_COPYDATA, sender.Handle, pCopyData)
Marshal.FreeHGlobal(mCopyData.lpData)
Marshal.FreeHGlobal(pCopyData)
End If
You need to declare in your code the COPYDATASTRUCT to use the winapi:
<StructLayout(LayoutKind.Sequential)> _
Private Structure COPYDATASTRUCT
Public dwData As IntPtr
Public cbData As Integer
Public lpData As IntPtr
End Structure
and the WM_COPYDATE message integer:
Const WM_COPYDATA As Integer = 74
I am trying to add an 'About' button to the System menu of my app, but the code that I found is throwing an error -
Unable to find an entry point named 'AppendMenu' in DLL 'user32'.
I wonder if someone could please take a look at the code and advise on how what I would need to do to fix it? Thanks.
Private Declare Function GetSystemMenu Lib "user32" (ByVal hWnd As IntPtr, ByVal bRevert As Boolean) As IntPtr
Private Declare Function AppendMenu Lib "user32" (ByVal hMenu As IntPtr, ByVal uFlags As Int32, ByVal uIDNewItem As IntPtr, ByVal lpNewItem As String) As Boolean
Private Const MF_STRING As Integer = &H0
Private Const MF_SEPARATOR As Integer = &H800
Private Sub AddSysMenuItems()
'Get the System Menus Handle.
Dim hSysMenu As IntPtr = GetSystemMenu(Me.Handle, False)
'Add a standard Separator Item.
AppendMenu(hSysMenu, MF_SEPARATOR, 1000, Nothing)
'Add an About Menu Item.
AppendMenu(hSysMenu, MF_STRING, 1001, "About")
End Sub
Well, the message is accurate, there is no entry point named "AppendMenu" in user32.dll. It actually has two versions of it. One is named AppendMenuA, the A means Ansi. The legacy version that uses 8-bit encoded strings, commonly used in old C programs. And AppendMenuW, the W means Wide. It takes a Unicode string like all winapi functions do on modern Windows versions.
Your old-style Declare statement is using the legacy function. You should use the Alias keyword to give the proper entrypoint name:
Private Declare Function AppendMenu Lib "user32.dll" Alias "AppendMenuA" (ByVal hMenu As IntPtr, ByVal uFlags As Int32, ByVal uIDNewItem As IntPtr, ByVal lpNewItem As String) As Boolean
Or just plain call it AppendMenuA. Using the legacy function isn't very pretty, although it won't have a problem converting "About" to Unicode. But do favor the modern way to declare pinvoke functions, it has many advantages beyond automatically mapping to the A or W version:
Imports System.Runtime.InteropServices
Imports System.ComponentModel
...
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function AppendMenu(ByVal hMenu As IntPtr, ByVal uFlags As Int32, ByVal uIDNewItem As IntPtr, ByVal lpNewItem As String) As Boolean
End Function
...
If Not AppendMenu(hSysMenu, MF_STRING, IntPtr.Zero, "About") Then
Throw New Win32Exception()
End If