How do I read cells of another program's TStringGrid control? - vb.net

How can I extract items from other application's gridview? The class name of the control is TStringGrid.
I can get the handle of the TStringGrid window with FindWindowEx using these declarations:
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function FindWindow( _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function FindWindowEx(ByVal parentHandle As IntPtr, _
ByVal childAfter As IntPtr, _
ByVal lclassName As String, _
ByVal windowTitle As String) As IntPtr
End Function
code:
Dim TheMainForm As Integer = FindWindow("form", "fname")
Dim GV As Integer = FindWindowEx(TheMainForm, 0, "TStringGrid", "")
How can I extract the items from GV (TStringGrid handle)?
(I have to finish this project by tomorrow.)

A Delphi string grid is not a windows control. It's a custom Delphi control. As such it doesn't respond to windows messages asking for its content. Without the source of the app you would need to reverse engineer the app to work out where the content is stored.
Realistically the most effective way to do this will be to inject a thread into the target application. That thread can then do the work of reading the information and can then use some IPC to get the data back to your VB process.
In order to do this you will, ideally, need:
Knowledge of the exact version of Delphi used to build the app.
A deep understanding of the Delphi compiler and RTL.
The Delphi VCL source code for TStringGrid.
I've no idea how you'll be able to synchronize your reading thread with the Delphi app.
Anyway, whilst what you ask for is, in theory possible, in reality it is completely impractical. The sensible solution is to ask the authors of the Delphi program to provide an automation interface.

Related

VB: ShowWindow focus issue

I'm attempting to send automated keystrokes to an application that does not support copy+paste via a small VB form. The form loads data from a text file and uses SendKeys to fire it over once I click a button.
Everything appears to work except for the ShowWindow portion. I'm currently testing using Notepad and, with one exception, I can't seem to get ShowWindow to kick focus to Notepad. Obviously I'm worried it will do the same to the application I'll eventually be running this against (I don't currently have access to it). The only ShowWindow parameter that makes Notepad active is SW_SHOWMAXIMIZED. SW_SHOW and SW_SHOWNORMAL don't appear to do anything while SW_RESTORE will restore Notepad if minimized but my VB form remains the active window.
I'm not a programmer but I had made the mistake of telling my boss I dabbled in Pascal Turbo in high school (over a decade ago) so I'm the one stuck with trying to make this work. My current code cobbled together from S.O. and other sources:
(I'm running Windows 7 and using MVSE2013)
Imports System.Runtime.InteropServices
Public Class Form1
Private Declare Function FindWindow _
Lib "user32" _
Alias "FindWindowA" _
(ByVal lpClassName As String, _
ByVal lpWindowName As String) As IntPtr
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function ShowWindow(ByVal hwnd As IntPtr, ByVal nCmdShow As ShowWindowCommands) As Boolean
End Function
Enum ShowWindowCommands As Integer
SW_SHOWNORMAL = 1
SW_SHOWMAXIMIZED = 3
SW_RESTORE = 9
End Enum
Private Sub Form1_Load
[form]
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim lHwnd As IntPtr = FindWindow("Notepad", vbNullString)
If lHwnd <> IntPtr.Zero Then
ShowWindow(lHwnd, ShowWindowCommands.SW_SHOWNORMAL)
SendKeys.Send(TextBox1.Text)
Else
[blah blah error handling]
End If
End Sub
I'd try another technique like SetForegroundWindow but I read it doesn't play nice with Windows 7.
Found what I hope will be a passable workaround from PInvoke. I ended up swapping this block:
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function ShowWindow(ByVal hwnd As IntPtr, ByVal nCmdShow _
As ShowWindowCommands) As Boolean
End Function
For this:
Public Declare Function BringWindowToTop Lib "user32" (ByVal hwnd As IntPtr) As Boolean
And then this line:
ShowWindow(lHwnd, ShowWindowCommands.SW_SHOWNORMAL)
For this:
BringWindowToTop(lHwnd)
I realize there are functional differences between the two but the change works in my specific instance so I'm happy.

How do I get Process ID from HWND using managed VB.net code?

Is there a managed VB.net way to get the Process ID from the HWND rather than using this Windows API call.
Private Declare Auto Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hwnd As IntPtr, _
ByRef lpdwProcessId As Integer) As Integer
Sub GetProcessID()
'start the application
Dim xlApp As Object = CreateObject("Excel.Application")
'get the window handle
Dim xlHWND As Integer = xlApp.hwnd
'this will have the process ID after call to GetWindowThreadProcessId
Dim ProcIdXL As Integer = 0
'get the process ID
GetWindowThreadProcessId(xlHWND, ProcIdXL)
'get the process
Dim xproc As Process = Process.GetProcessById(ProcIdXL)
End Sub
No, this isn't wrapped by .NET. But there's absolutely nothing wrong with calling the native API functions. That's what the framework does internally, and that's why P/Invoke was invented, to make it as simple as possible for you to do this yourself. I'm not really sure why you're seeking to avoid it.
Of course, I would recommend using the new-style declaration, which is the more idiomatic way of doing things in .NET (rather than the old VB 6 way):
<DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, _
ByRef lpdwProcessId As Integer) As Integer
End Function
Your other option, if you absolutely cannot get over the irrational compulsion to stay with managed code, is to make use of the Process class. This can be used to start an external process, and has a property (Id) that can be used to retrieve the process's ID. I'm not sure if that will work for you. You specifically avoid telling us why you're using CreateObject in the first place.

Get focused window title and use it in if statement

I found this code around the net but I don't know how to use it. I just want to build a code that will execute if a desired window is active.
This is for API as i read about my research,
<DllImport("USER32.DLL", EntryPoint:="GetActiveWindow", SetLastError:=True,
CharSet:=CharSet.Unicode, ExactSpelling:=True,
CallingConvention:=CallingConvention.StdCall)>
Public Shared Function GetActiveWindowHandle() As System.IntPtr
End Function
<DllImport("USER32.DLL", EntryPoint:="GetWindowText", SetLastError:=True,
CharSet:=CharSet.Unicode, ExactSpelling:=True,
CallingConvention:=CallingConvention.StdCall)>
Public Shared Function GetActiveWindowText(ByVal hWnd As System.IntPtr, ByVal lpString As System.Text.StringBuilder, ByVal cch As Integer) As Integer
End Function
He used this to get the text,
Dim caption As New System.Text.StringBuilder(256)
Dim hWnd As IntPtr = GetActiveWindowHandle()
GetActiveWindowText(hWnd, caption, caption.Capacity)
MsgBox(caption.ToString)
I put that code in the form_load() but it doesn't display anything. Like nothing happens. Where should I put it or how to make it work. I want to get the window title and put it in an If statement. I also found some programmers that used this code:
Dim hWnd As IntPtr = GetForegroundWindow()
I just want to determine the window on focus, get the text and put it in an If statement. Can you please simplify this code more. What is the difference of a FOCUS window and ACTIVE window? i guess ACTIVE are the open windows but FOCUS is the only one that the user is using. I want to know the title of the FOCUS one.
For example,
If "title of the window on focus" = "notepad" Then
MsgBox("notepad")
Else
MsgBox("not notepad")
End If
And where there is 2 windows open, if the notepad is focused it will display the notepad on message box and when the user change its focus to the other window the message box will automatically display the not notepad message.

Vb.net: Send text to window via windows messages

I'm trying to make a program with vb.net (VS2008) for studio shooting with my Nikon D600.
I'm using a program called ControlMyNikon for tethered shooting and it's working perfectly.
It has this external control-feature with following instructions: http://i.jjj.fi/a9dAQ7z.png
Could someone give me hint what does 'send string to 'ControlMyNikon v4.1' window with windows messages' actually mean?
I tried with SendMessages.
I was able to change window title with WM_SETTEXT but that's all.
I'm able to get the window handle but don't know how to send any string to it.
Help? :)
Ugh, Nikon is pretty infamous for writing truly crappy shovelware. This does not disappoint. Here are some declarations that ought to work. Try the Unicode version first. If that produces Chinese text then use the Ansi version:
Imports System.Runtime.InteropServices
Class NativeMethods
Friend Const WM_SETTEXT As Integer = 12
<DllImport("user32.dll", EntryPoint:="SendMessageW", CharSet:=CharSet.Unicode)> _
Friend Shared Function SetWindowTextUnicode(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As IntPtr, ByVal lParam As String) As IntPtr
End Function
<DllImport("user32.dll", EntryPoint:="SendMessageA", CharSet:=CharSet.Ansi)> _
Friend Shared Function SetWindowTextAnsi(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As IntPtr, ByVal lParam As String) As IntPtr
End Function
End Class
Usage:
NativeMethods.SetWindowTextUnicode(handleYouFound, WM_SETTTEXT, IntPtr.Zero, "shoot")
If neither works then you are probably using the wrong window handle. Use Spy++ to double check that you located the window properly.

VB.NET How to send key 46 (Chr(46)) with SendMessage

I am Automating IBM iSeries Emulators using VB.net, created GUI that embeds the external iSeries Emulator Window in a WindowsForm Panel using the "SetParent" API .. To communicate with the iSeries Window I use the SendMessage API to send Key's to the Screen. I am aware of the sendkeys.keys in .net, but this way I dont have to SetForegroundWindow and reactivate my Form.
My program reads the output of a scale and validates if the received data is good, if so it will send the output to the iSeries Screen using this code (UserApi is my Lib):
For Each element As Char In CStr(txtto400.Text)
UserApi.SendMessage(as400WindowHandle, 256, CType(Convert.ToInt32(element), IntPtr), CType(0, IntPtr))
Next
I done this a few times on other projects and this method works good, but in my TextBox (txtto400.Text) my weight has a Value like "000.88 KG".
SendMessage will not send the key "46" which is a period (.) Instead it would be "00088 KG"
Does anyone have an idea why this dont work? Any suggestions what I could do?
EDIT:
Thanks to Paul B. which gave me the hint.
Here is how I resolved it.
My SendMessage function looks like this:
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Public Shared Function SendMessage( _
ByVal hwnd As IntPtr, _
ByVal wMsg As UInt32, _
ByVal wParam As IntPtr, _
ByVal lParam As IntPtr) As IntPtr
End Function
I created a variable that holds the VK_OEM_PERIOD:
Public Const VK_OEM_PERIOD = &HBE
Created a Function that will check the Chr:
Public Function vk_key(key As Char) As Integer
Select Case key
Case Is = Chr(46)
Return VK_OEM_PERIOD
Case Else
Return AscW(key)
End Select
End Function
here is how I pass it to the iSeries Window:
For Each element As Char In txtto400.Text
UserApi.SendMessage(as400WindowHandle, 256, vk_key(element), 0)
Next
The WM_KEYDOWN message expects "The virtual-key code of the nonsystem key" as wParam (see MSDN).
This happens to correspond to the key character's ASCII value for alphanumeric signs but not for all other keys. For a period I think you need to send VK_OEM_PERIOD.
You can find the table of virtual-key codes here: http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx