Imports System
Imports System.Text
Imports System.ComponentModel
Imports DD_Clipboard_Watcher.ClassWindowsAPINativeMethods

Public Class ClassWindowAPIUtils
    Private Shared GetControlNameMessage As Integer = 0

    Sub New()
        GetControlNameMessage = ClassWindowsAPINativeMethods.RegisterWindowMessage("WM_GETCONTROLNAME")
    End Sub

    Public Function GetWinFormsId(ByVal hWnd As IntPtr) As String
        Return XProcGetControlName(hWnd, GetControlNameMessage)
    End Function

    Protected Function XProcGetControlName(ByVal hwnd As IntPtr, ByVal msg As Integer) As String
        Dim bytearray As Byte() = New Byte(65535) {}
        Dim bufferMem As IntPtr = IntPtr.Zero
        Dim written As IntPtr = IntPtr.Zero
        Dim retHandle As IntPtr = IntPtr.Zero
        Dim retVal As Boolean
        Dim processHandle As IntPtr = IntPtr.Zero
        Dim fileHandle As IntPtr = IntPtr.Zero

        If Not (Environment.OSVersion.Platform = PlatformID.Win32Windows) Then

            Try
                Dim size As UInteger
                size = 65536
                processHandle = ClassWindowsAPINativeMethods.OpenProcess(ClassWindowsAPINativeMethods.PROCESS_VM_OPERATION Or ClassWindowsAPINativeMethods.PROCESS_VM_READ Or ClassWindowsAPINativeMethods.PROCESS_VM_WRITE, False, GetProcessIdFromHWnd(hwnd))

                If processHandle.ToInt64() = 0 Then
                    Throw New Win32Exception()
                End If

                bufferMem = ClassWindowsAPINativeMethods.VirtualAllocEx(processHandle, IntPtr.Zero, New UIntPtr(size), ClassWindowsAPINativeMethods.MEM_RESERVE Or ClassWindowsAPINativeMethods.MEM_COMMIT, PageProtection.ReadWrite)

                If bufferMem.ToInt64() = 0 Then
                    Throw New Win32Exception()
                End If

                retHandle = ClassWindowsAPINativeMethods.SendMessage(hwnd, msg, New IntPtr(size), bufferMem)
                retVal = ClassWindowsAPINativeMethods.ReadProcessMemory(processHandle, bufferMem, bytearray, New UIntPtr(size), written)

                If Not retVal Then
                    Throw New Win32Exception()
                End If

            Finally
                retVal = ClassWindowsAPINativeMethods.VirtualFreeEx(processHandle, bufferMem, New UIntPtr(0), ClassWindowsAPINativeMethods.MEM_RELEASE)

                If Not retVal Then
                    Throw New Win32Exception()
                End If

                ClassWindowsAPINativeMethods.CloseHandle(processHandle)
            End Try
        Else

            Try
                Dim size2 As Integer
                size2 = 65536
                fileHandle = ClassWindowsAPINativeMethods.CreateFileMapping(New IntPtr(ClassWindowsAPINativeMethods.INVALID_HANDLE_VALUE), IntPtr.Zero, PageProtection.ReadWrite, 0, size2, Nothing)

                If fileHandle.ToInt64() = 0 Then
                    Throw New Win32Exception()
                End If

                bufferMem = ClassWindowsAPINativeMethods.MapViewOfFile(fileHandle, ClassWindowsAPINativeMethods.FILE_MAP_ALL_ACCESS, 0, 0, New UIntPtr(0))

                If bufferMem.ToInt64() = 0 Then
                    Throw New Win32Exception()
                End If

                ClassWindowsAPINativeMethods.MoveMemoryFromByte(bufferMem, bytearray(0), size2)
                retHandle = ClassWindowsAPINativeMethods.SendMessage(hwnd, msg, New IntPtr(size2), bufferMem)
                ClassWindowsAPINativeMethods.MoveMemoryToByte(bytearray(0), bufferMem, 1024)
            Finally
                ClassWindowsAPINativeMethods.UnmapViewOfFile(bufferMem)
                ClassWindowsAPINativeMethods.CloseHandle(fileHandle)
            End Try
        End If

        Return ByteArrayToString(bytearray)
    End Function

    Private Function GetProcessIdFromHWnd(ByVal hwnd As IntPtr) As UInteger
        Dim pid As UInteger
        ClassWindowsAPINativeMethods.GetWindowThreadProcessId(hwnd, pid)
        Return pid
    End Function

    Private Function ByteArrayToString(ByVal bytes As Byte()) As String
        If Environment.OSVersion.Platform = PlatformID.Win32Windows Then
            Return Encoding.[Default].GetString(bytes).TrimEnd(vbNullChar)
        Else
            Return Encoding.Unicode.GetString(bytes).TrimEnd(vbNullChar)
        End If
    End Function
End Class
