I've worked with VBA for a few years, but I'm switching to VB to utilize more powerful coding practices.
I have the following VB code to attach an Excel workbook to an open instance:
Dim FullFilePath as String = "C:\Temp"
Dim WrkBk = as Excel.Workbook
WrkBk = System.Runtime.InteropServices.Marshal.BindToMoniker(FullFilePath)
This seems to require the workbook's directory path.
I have the following code to cycle through Excel processes.
FileName = "ABCD.xlsm"
For Each P As Process In System.Diagnostics.Process.GetProcessesByName("EXCEL")
With P
If .MainWindowTitle.ToLower.Contains(FileName.ToLower) Then
'don't know the code to get the directory where the p process is located
WrkBk = System.Runtime.InteropServices.Marshal.BindToMoniker(FilePath)
Exit For
End If
End With
Next
The path to ABCD.xlsm is not always the same for my application. Everything I've googled for getting a directory of the process returns the EXCEL.EXE path or returns the path of the vb project's working directory. I really need it to return something like: "C:\Temp\Tools\ABCD.xlsm" so I can attach WrkBk to the process.
Any Ideas? Is there an easier way to do this?
Dim xl As Excel.Application
xl = GetObject(, "Excel.Application")
For Each wb As Excel.Workbook In xl.Workbooks
MsgBox(wb.FullName)
Next wb
xl = Nothing
I finally found it.
Windows API....
Private Delegate Function EnumWindowsProc(ByVal hwnd As IntPtr, ByVal lParam As IntPtr) As Boolean
Private Declare Function EnumChildWindows Lib "user32.dll" (ByVal hWndParent As IntPtr, ByVal lpEnumFunc As EnumWindowsProc, ByVal lParam As IntPtr) As Boolean
Private Declare Function GetClassName Lib "user32.dll" Alias "GetClassNameA" (ByVal hwnd As IntPtr, ByVal lpClassName As String, ByVal nMaxCount As Int32) As Int32
Private Const MAX_TITLE As Int32 = 256
Private Const S_OK As Int32 = &H0
Private Function GetClassName(ByVal hwnd As IntPtr) As String
Dim name As New String(" "c, MAX_TITLE)
Dim len = GetClassName(hwnd, name, MAX_TITLE)
If len = 0 Then Return Nothing
Return name.Remove(len)
End Function
Private Declare Function AccessibleObjectFromWindow Lib "OLEACC.dll" (ByVal hwnd As IntPtr, ByVal dwId As Int32, ByVal riid As Byte(), ByRef ppvObject As IntPtr) As Integer
Private Const OBJID_NATIVEOM As Int32 = &HFFFFFFF0
Private IID_IDispatch As New Guid("{00020400-0000-0000-C000-000000000046}")
function...
Function EnumCWindows(ByVal hwnd As IntPtr, ByVal lParam As IntPtr) As Boolean
Dim name = GetClassName(hwnd)
If name = "EXCEL7" OrElse name = "EXCEL10" Then
Dim ptr As IntPtr
If AccessibleObjectFromWindow(hwnd, OBJID_NATIVEOM, IID_IDispatch.ToByteArray(), ptr) = S_OK Then
Dim win = CType(Marshal.GetObjectForIUnknown(ptr), Excel.Window)
Dim sheet = CType(win.ActiveSheet, Excel.Worksheet)
Dim book = CType(sheet.Parent, Excel.Workbook)
If book.FullName.ToLower.Contains("wpp tools") Then
FoundTheFile = True
WPPFilePath = book.FullName
Exit Function
End If
End If
End If
Return True
End Function
then in your code....
For Each p In Process.GetProcesses
If p.ProcessName = "EXCEL" Then
EnumChildWindows(p.MainWindowHandle, AddressOf EnumCWindows, IntPtr.Zero)
If FoundTheFile = True Then Exit For
End If
Next
If FoundTheFile = False Then
MsgBox("Targeted file was not found.", MsgBoxStyle.OkOnly, "File Find Error")
End
End If
I hope this can help someone else as I've searched for a week now!
Related
I have a macro that copies rtf format word document to outlook email for sending it to many recipients. However, due to this a copy of the text is also saved on the clipboard and the code crashes if many recipients are there. I was using the below code for clearing the clipboard but the code is no longer working after the office 365 update. I tried changing the declare functions to include 'Ptrsafe' but still not able to run it. Any help would be greatly appreciated. Thanks
Declare Function AccessibleObjectFromWindow Lib "oleacc" ( _
ByVal hwnd As Long, ByVal dwId As Long, _
riid As tGUID, ppvObject As Object) As Long
Declare Function AccessibleChildren Lib "oleacc" _
(ByVal paccContainer As IAccessible, ByVal iChildStart As Long, _
ByVal cChildren As Long, rgvarChildren As Variant, _
pcObtained As Long) As Long
Declare Function FindWindow Lib "User32" Alias "FindWindowA" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Declare Function GetParent Lib "User32" (ByVal hwnd As Long) As Long
Declare Function EnumChildWindows Lib "User32" (ByVal hwndParent _
As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Declare Function GetClassName Lib "User32" Alias "GetClassNameA" (ByVal hwnd As Long, _
ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Declare Function SendMessage Lib "User32" Alias "SendMessageA" (ByVal hwnd As Long, _
ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As String) As Long
Declare Function FindWindowEx Lib "User32" Alias "FindWindowExA" (ByVal hWnd1 As Long, _
ByVal hWnd2 As Long, ByVal lpClass As String, ByVal lpCaption As String) As Long
Const CHILDID_SELF = 0&
Const ROLE_PUSHBUTTON = &H2B&
Const WM_GETTEXT = &HD
Type tGUID
lData1 As Long
nData2 As Integer
nData3 As Integer
abytData4(0 To 7) As Byte
End Type
Type AccObject
objIA As IAccessible
lngChild As Long
End Type
Dim lngChild As Long
Dim strClass As String
Dim strCaption As String
'Using Active Accessibility to clear Office clipboard
'Assumption:
'this is running within Word or Excel as a macro, thus the global Application object is available
Sub ClearOfficeClipboard()
Static accButton As AccObject
If accButton.objIA Is Nothing Then
Dim fShown As Boolean
fShown = CommandBars("Office Clipboard").Visible 'Office 2013+ version
If Not (fShown) Then
CommandBars("Office Clipboard").Visible = True 'Office 2013+ version
End If
accButton = FindAccessibleChildInWindow(GetOfficeClipboardHwnd(Application), "Clear All", ROLE_PUSHBUTTON)
End If
If accButton.objIA Is Nothing Then
MsgBox "Unable to locate the ""Clear All"" button!"
Else
accButton.objIA.accDoDefaultAction accButton.lngChild
End If
CommandBars("Office Clipboard").Visible = False
End Sub
'Retrieve window class name
Function GetWndClass(ByVal hwnd As Long) As String
Dim buf As String
Dim retval As Long
buf = Space(256)
retval = GetClassName(hwnd, buf, 255)
GetWndClass = Left(buf, retval)
End Function
'Retrieve window title
Function GetWndText(ByVal hwnd As Long) As String
Dim buf As String
Dim retval As Long
buf = Space(256)
retval = SendMessage(hwnd, WM_GETTEXT, 255, buf)
GetWndText = Left(buf, InStr(1, buf, Chr(0)) - 1)
End Function
'The call back function used by EnumChildWindows
Function EnumChildWndProc(ByVal hChild As Long, ByVal lParam As Long) As Long
Dim found As Boolean
EnumChildWndProc = -1
If strClass > "" And strCaption > "" Then
found = StrComp(GetWndClass(hChild), strClass, vbTextCompare) = 0 And _
StrComp(GetWndText(hChild), strCaption, vbTextCompare) = 0
ElseIf strClass > "" Then
found = (StrComp(GetWndClass(hChild), strClass, vbTextCompare) = 0)
ElseIf strCaption > "" Then
found = (StrComp(GetWndText(hChild), strCaption, vbTextCompare) = 0)
Else
found = True
End If
If found Then
lngChild = hChild
EnumChildWndProc = 0
Else
EnumChildWndProc = -1
End If
End Function
'Find the window handle of a child window based on its class and titie
Function FindChildWindow(ByVal hParent As Long, Optional cls As String = "", Optional title As String = "") As Long
lngChild = 0
strClass = cls
strCaption = title
EnumChildWindows hParent, AddressOf EnumChildWndProc, 0
FindChildWindow = lngChild
End Function
'Retrieve the IAccessible interface from a window handle
'Reference:Jean Ross,Chapter 17: Accessibility in Visual Basic,Advanced Microsoft Visual Basic 6.0, 2nd Edition
Function IAccessibleFromHwnd(hwnd As Long) As IAccessible
Dim oIA As IAccessible
Dim tg As tGUID
Dim lReturn As Long
' Define the GUID for the IAccessible object
' {618736E0-3C3D-11CF-810C-00AA00389B71}
With tg
.lData1 = &H618736E0
.nData2 = &H3C3D
.nData3 = &H11CF
.abytData4(0) = &H81
.abytData4(1) = &HC
.abytData4(2) = &H0
.abytData4(3) = &HAA
.abytData4(4) = &H0
.abytData4(5) = &H38
.abytData4(6) = &H9B
.abytData4(7) = &H71
End With
' Retrieve the IAccessible object for the form
lReturn = AccessibleObjectFromWindow(hwnd, 0, tg, oIA)
Set IAccessibleFromHwnd = oIA
End Function
'Recursively looking for a child with specified accName and accRole in the accessibility tree
Function FindAccessibleChild(oParent As IAccessible, strName As String, lngRole As Long) As AccObject
Dim lHowMany As Long
Dim avKids() As Variant
Dim lGotHowMany As Long, i As Integer
Dim oChild As IAccessible
FindAccessibleChild.lngChild = CHILDID_SELF
If oParent.accChildCount = 0 Then
Set FindAccessibleChild.objIA = Nothing
Exit Function
End If
lHowMany = oParent.accChildCount
ReDim avKids(lHowMany - 1) As Variant
lGotHowMany = 0
If AccessibleChildren(oParent, 0, lHowMany, avKids(0), lGotHowMany) <> 0 Then
MsgBox "Error retrieving accessible children!"
Set FindAccessibleChild.objIA = Nothing
Exit Function
End If
'To do: the approach described in http://msdn.microsoft.com/msdnmag/issues/0400/aaccess/default.aspx
' are probably better and more reliable
On Error Resume Next
For i = 0 To lGotHowMany - 1
If IsObject(avKids(i)) Then
If StrComp(avKids(i).accName, strName) = 0 And avKids(i).accRole = lngRole Then
Set FindAccessibleChild.objIA = avKids(i)
Exit For
Else
Set oChild = avKids(i)
FindAccessibleChild = FindAccessibleChild(oChild, strName, lngRole)
If Not FindAccessibleChild.objIA Is Nothing Then
Exit For
End If
End If
Else
If StrComp(oParent.accName(avKids(i)), strName) = 0 And oParent.accRole(avKids(i)) = lngRole Then
Set FindAccessibleChild.objIA = oParent
FindAccessibleChild.lngChild = avKids(i)
Exit For
End If
End If
Next i
End Function
Function FindAccessibleChildInWindow(hwndParent As Long, strName As String, lngRole As Long) As AccObject
Dim oParent As IAccessible
Set oParent = IAccessibleFromHwnd(hwndParent)
If oParent Is Nothing Then
Set FindAccessibleChildInWindow.objIA = Nothing
Else
FindAccessibleChildInWindow = FindAccessibleChild(oParent, strName, lngRole)
End If
End Function
'Retrieve the window handle of the task pane
Function GetOfficeTaskPaneHwnd(app As Object) As Long
GetOfficeTaskPaneHwnd = FindChildWindow(app.hwnd, _
"MsoCommandBar", Application.CommandBars("Task Pane").NameLocal)
End Function
'Retrieve the window handle of the clipboard child window inside task pane
'The window title of the clipboard window seems to be language independent,
'making it a better start point to searching our UI element than the task pane window
Function GetOfficeClipboardHwnd(app As Object) As Long
GetOfficeClipboardHwnd = FindChildWindow(app.hwnd, , "Collect and Paste 2.0")
End Function```
We can clear the clipboard using a MsForms.DataObject. The code below creates one without the need to reference the MsForms library.
Sub ClearClipBoard()
With CreateObject("new:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
.SetText ""
.PutInClipBoard
End With
End Sub
I've got an add-in that processes and then moves certain incoming emails. The only unfortunate consequence of this is that the "new mail envelope" on the taskbar:
remains lit when there isn't any mail for the user to look at.
There's a VBA solution to this issue here: http://www.outlookcode.com/d/code/clearenvicon.htm
It doesn't seem easily portable to VB though - after clearing up what were to me the obvious changes suggested I end up with:
Module RemoveNotification
' Code sample by Outlook MVP "Neo"
' Removes the New Mail icon from the Windows system tray,
' and resets Outlook's new mail notification engine.
' Tested against Outlook 2000 (IMO) and Outlook 2002 (POP Account)
' Send questions and comments to neo#mvps.org
' WARNING: Due to the use of AddressOf, code must
' go into a module and not ThisOutlookSession or
' a class module
' Entry Point is RemoveNewMailIcon.
Public Const WUM_RESETNOTIFICATION As Long = &H407
'Required Public constants, types & declares
'for the Shell_Notify API method
Public Const NIM_ADD As Long = &H0
Public Const NIM_MODIFY As Long = &H1
Public Const NIM_DELETE As Long = &H2
Public Const NIF_ICON As Long = &H2 'adding an ICON
Public Const NIF_TIP As Long = &H4 'adding a TIP
Public Const NIF_MESSAGE As Long = &H1 'want return messages
' Structure needed for Shell_Notify API
Structure NOTIFYICONDATA
Dim cbSize As Long
Dim hwnd As Long
Dim uID As Long
Dim uFlags As Long
Dim uCallbackMessage As Long
Dim hIcon As Long
Dim szTip As String * 64
End Structure
Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Long, ByVal wMsg As Long,
ByVal wParam As Integer, ByVal lParam As Any) As Long
Declare Function GetClassName Lib "user32" _
Alias "GetClassNameA" _
(ByVal hwnd As Long,
ByVal lpClassName As String,
ByVal nMaxCount As Long) As Long
Declare Function GetWindowTextLength Lib "user32" _
Alias "GetWindowTextLengthA" _
(ByVal hwnd As Long) As Long
Declare Function GetWindowText Lib "user32" _
Alias "GetWindowTextA" _
(ByVal hwnd As Long,
ByVal lpString As String,
ByVal cch As Long) As Long
Declare Function EnumWindows Lib "user32" _
(ByVal lpEnumFunc As Long,
ByVal lParam As Long) As Long
Declare Function Shell_NotifyIcon Lib "shell32.dll" _
Alias "Shell_NotifyIconA" _
(ByVal dwMessage As Long,
lpData As NOTIFYICONDATA) As Long
Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String,
ByVal lpWindowName As String) As Long
' This is the entry point that makes it happen
Sub RemoveNewMailIcon()
EnumWindows AddressOf EnumWindowProc, 0
End Sub
Public Function EnumWindowProc(ByVal hwnd As Long,
ByVal lParam As Long) As Long
'Do stuff here with hwnd
Dim sClass As String
Dim sIDType As String
Dim sTitle As String
Dim hResult As Long
sTitle = GetWindowIdentification(hwnd, sIDType, sClass)
If sTitle = "rctrl_renwnd32" Then
hResult = KillNewMailIcon(hwnd)
End If
If hResult Then
EnumWindowProc = False
' Reset the new mail notification engine
Call SendMessage(hwnd, WUM_RESETNOTIFICATION, 0&, 0&)
Else
EnumWindowProc = True
End If
End Function
Private Function GetWindowIdentification(ByVal hwnd As Long,
sIDType As String,
sClass As String) As String
Dim nSize As Long
Dim sTitle As String
'get the size of the string required
'to hold the window title
nSize = GetWindowTextLength(hwnd)
'if the return is 0, there is no title
If nSize > 0 Then
sTitle = Space$(nSize + 1)
Call GetWindowText(hwnd, sTitle, nSize + 1)
sIDType = "title"
sClass = Space$(64)
Call GetClassName(hwnd, sClass, 64)
Else
'no title, so get the class name instead
sTitle = Space$(64)
Call GetClassName(hwnd, sTitle, 64)
sClass = sTitle
sIDType = "class"
End If
GetWindowIdentification = TrimNull(sTitle)
End Function
Private Function TrimNull(startstr As String) As String
Dim pos As Integer
pos = InStr(startstr, Chr$(0))
If pos Then
TrimNull = Left(startstr, pos - 1)
Exit Function
End If
'if this far, there was
'no Chr$(0), so return the string
TrimNull = startstr
End Function
Private Function KillNewMailIcon(ByVal hwnd As Long) As Boolean
Dim pShell_Notify As NOTIFYICONDATA
Dim hResult As Long
'setup the Shell_Notify structure
pShell_Notify.cbSize = Len(pShell_Notify)
pShell_Notify.hwnd = hwnd
pShell_Notify.uID = 0
' Remove it from the system tray and catch result
hResult = Shell_NotifyIcon(NIM_DELETE, pShell_Notify)
If (hResult) Then
KillNewMailIcon = True
Else
KillNewMailIcon = False
End If
End Function
End Module
Which gives the below errors & warnings:
Severity Code Description Line
Error BC30205 End of statement expected. 35
Error BC30800 Method arguments must be enclosed in parentheses. 73
Error BC30828 'As Any' is not supported in 'Declare' statements. 40
Error BC30581 'AddressOf' expression cannot be converted to 'Long' because 'Long' is not a delegate type. 73
Error BC30277 Type character '$' does not match declared data type 'Char'. 129
Warning BC42104 Variable 'sIDType' is used before it has been assigned a value. A null reference exception could result at runtime. 85
Warning BC42104 Variable 'sClass' is used before it has been assigned a value. A null reference exception could result at runtime. 85
Warning BC42109 Variable 'pShell_Notify' is used before it has been assigned a value. A null reference exception could result at runtime. Make sure the structure or all the reference members are initialized before use 145
This is code to save untitled notepad to desktop or given path. when we run this code visual studio host stopping some times and some times marshal object throwing error, when we have more than one notepad opened some files are saving .. after that program stopping automatically.. we are passing path as Environment.SpecialFolder.Desktop
Private Const WM_GETTEXT As Integer = &HD
Private Const WM_GETTEXTLENGTH As Integer = &HE
Declare Auto Function SendMessage Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal msg As Integer, _
ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
<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
Declare Auto Function FindWindow Lib "user32.dll" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
Sub saveNotepad(pat As String)
Try
Dim processes() As Process
Dim procName As String = "notepad"
processes = Process.GetProcessesByName(procName)
If processes IsNot Nothing Then
For Each proc In processes
If Not proc.MainWindowTitle = "" Then
Dim Hwnd As IntPtr = FindWindow(Nothing, proc.MainWindowTitle)
Dim ChildHandle As IntPtr = FindWindowEx(Hwnd, IntPtr.Zero, "Edit", Nothing)
Dim size As Int32 = SendMessage(CInt(ChildHandle), WM_GETTEXTLENGTH, 0, 0).ToInt32()
Dim Hndl As IntPtr = Marshal.AllocHGlobal(size + 1)
Dim NumText As Integer = SendMessage(ChildHandle, WM_GETTEXT, size + 1, Hndl)
Dim Text As String = Marshal.PtrToStringUni(Hndl)
Dim savePath As String = System.IO.Path.Combine(pat, "Terminator")
Dim fs As FileStream = File.Create(IO.Path.Combine(savePath, proc.MainWindowTitle & DateTime.Now.ToString("yyyyMMdd_HHmmss")) & ".txt")
Dim info As Byte() = New UTF8Encoding(True).GetBytes(Text)
fs.Write(info, 0, info.Length)
fs.Close()
Marshal.FreeHGlobal(Hndl)
End If
Next
End If
Catch ex As Exception
Common.LogDebuggerData("Error: " & ex.Message & vbCrLf & ex.StackTrace)
Finally
GC.Collect()
GC.WaitForPendingFinalizers()
End Try
End Sub
Replacing
Marshal.AllocHGlobal(size + 1)
with
Marshal.AllocHGlobal(2 * (size + 1))
will solve the problem
This question is basically regarding to loop all workbooks in all excel instances!
Your main issue you are facing is you are not using any of the process's you come across. Therefore, you will not get anything that way. Inside of the loop for the process's you then create a new instance of ExcelApplication and then try to loop through the Workbooks. By default when you do this there is only 1 at that time, hence why you get only 1 Workbook and also why you will only ever see 1 Workbook.
Solution (Tried & Tested)
You need to look into Windows API calls to get what you need. A few of them are:
GetDesktopWindow()
EnumChildWindows()
GetClassName()
EnumWindowsProc()
AccessibleObjectFromWindow()
Imports Microsoft.Office.Interop
Imports System.Runtime.InteropServices
Public Class Form1
Private Declare Function GetDesktopWindow Lib "user32" () As Integer
Private Declare Function EnumChildWindows Lib "user32.dll" (ByVal WindowHandle As IntPtr, ByVal Callback As EnumWindowsProc, ByVal lParam As IntPtr) As Boolean
Private Declare Function GetClassName Lib "user32.dll" Alias "GetClassNameA" (ByVal hWnd As IntPtr, ByVal lpClassName As String, ByVal nMaxCount As Integer) As Integer
Private Delegate Function EnumWindowsProc(ByVal hwnd As IntPtr, ByVal lParam As Int32) As Boolean
Private Declare Function AccessibleObjectFromWindow Lib "oleacc" (ByVal Hwnd As Int32, ByVal dwId As Int32, ByRef riid As Guid, <MarshalAs(UnmanagedType.IUnknown)> ByRef ppvObject As Object) As Int32
Private Const OBJID_NATIVE = &HFFFFFFF0
'Required to show the workbooks. Used in function to add to.
Private lstWorkBooks As New List(Of String)
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
lstWorkBooks.Clear()
GetExcelOpenWorkBooks()
End Sub
Private Sub GetExcelOpenWorkBooks()
Try
'Get handle to desktop
Dim WindowHandle As IntPtr = GetDesktopWindow()
'Enumerate through the windows (objects) that are open
EnumChildWindows(WindowHandle, AddressOf GetExcelWindows, 0)
'List the workbooks out if we have something
If lstWorkBooks.Count > 0 Then MsgBox(String.Join(Environment.NewLine, lstWorkBooks))
Catch ex As Exception
End Try
End Sub
Public Function GetExcelWindows(ByVal hwnd As IntPtr, ByVal lParam As Int32) As Boolean
Dim Ret As Integer = 0
Dim className As String = Space(255) 'Return the string with some padding...
Ret = GetClassName(hwnd, className, 255)
className = className.Substring(0, Ret)
If className = "EXCEL7" Then
Dim ExcelApplication As Excel.Application
Dim ExcelObject As Object = Nothing
Dim IDispatch As Guid
AccessibleObjectFromWindow(hwnd, OBJID_NATIVE, IDispatch, ExcelObject)
'Did we get anything?
If ExcelObject IsNot Nothing Then
ExcelApplication = ExcelObject.Application
'Make sure we have the instance...
If ExcelApplication IsNot Nothing Then
'Go through the workbooks...
For Each wrk As Excel.Workbook In ExcelApplication.Workbooks
'If workbook ins't in the list then add it...
If Not lstWorkBooks.Contains(wrk.Name) Then
lstWorkBooks.Add(wrk.Name)
End If
Next
End If
End If
End If
Return True
End Function
End Class
OK, this problem has been bugging me for a long time.
I have the code for the service which communicates a string to the client via PostMessage:
Public Sub SendToClient(msgs As String, types As Integer, hwnd As Long)
postMessage(hwnd, 0, Nothing, msgs)
End Sub
Then I have the client that receives the string:
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If (m.Msg = 0) Then
Dim a as string
a = Marshal.PtrToStringAuto(m.LParam)
end if
MyBase.WndProc(m)
End Sub
However, the client sends an error, or some jumble of binary data or even just a blank string sometimes. By the way, the m.LParam is a number.
Can someone tell me what is the right way to send/receive strings via postmessage.
I do it like this:
Public Sub SendMessageToApp(ByVal NombreVentana As String, ByVal Mensaje As String, ByVal sender As Form)
Dim hWnd As IntPtr
Dim mCopyData As COPYDATASTRUCT
hWnd = CType(FindWindow(Nothing, NombreVentana), IntPtr)
Dim message As New System.Text.StringBuilder
If (CInt(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 = CType(_messageID, IntPtr)
Marshal.StructureToPtr(mCopyData, pCopyData, False)
SendMessage(hWnd, WM_COPYDATA, CInt(sender.Handle), pCopyData)
Marshal.FreeHGlobal(mCopyData.lpData)
Marshal.FreeHGlobal(pCopyData)
End If
End Sub
Receiver window:
Declarations and definitions:
Const WM_COPYDATA As Integer = 74
Const SIG_LENGTH As Integer = 36
Const MAX_COPY_LENGTH As Integer = 128
Const SigConnect As String = "F7B82657-BD18-4ee6-B182-78721293821C"
Dim CDCount As Integer
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
Dim hWndSender As Integer
Private Const _messageID As Integer = 10
'Estructura obligatoria para poder utilizar el API CopyData
<StructLayout(LayoutKind.Sequential)> _
Private Structure COPYDATASTRUCT
Public dwData As IntPtr
Public cbData As Integer
Public lpData As IntPtr
End Structure
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_COPYDATA Then
Dim Estructura As COPYDATASTRUCT
Dim tipo As Type = Estructura.GetType
Dim message As String
Estructura = CType(m.GetLParam(GetType(COPYDATASTRUCT)), COPYDATASTRUCT)
'Here you get the message
message = Marshal.PtrToStringAnsi(Estructura.lpData, Estructura.cbData)
End If