Send String Msg between 2 .net applications (VB.NET) - vb.net

I want to send a string Message from .net Application A and Receive the message from the Application B and here's the code:
-------- Application A
Private Const RF_TESTMESSAGE As Integer = &HA123
Public Structure MyData
Public M As String
Public I As Integer
End Structure
Public Function SendTest()
Dim Data As New MyData
Data.M = "QWERTY"
Data.I = 15
Dim P As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(Data))
Marshal.StructureToPtr(Data, P, False)
Dim Hdl As New IntPtr(11111) 'While 11111 is the WndHD for the application B for testing
SendMessage(Hdl, RF_TESTMESSAGE, IntPtr.Zero, P)
End Function
------- Application B
Private Const RF_TESTMESSAGE As Integer = &HA123
Public Structure MyData
Public M As String
Public I As Integer
End Structure
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = RF_TESTMESSAGE Then
Dim A = DirectCast(m.GetLParam(GetType(MyData)), MyData)
MsgBox(A.M)
MsgBox(A.I)
Marshal.FreeHGlobal(m.LParam)
End If
MyBase.WndProc(m)
End Sub
The application B always receive the message, but unable to convert the point lParam to valid MyData Structure, and sometime raise access violation, sometime no error ....
Please advice.

The problem is that you're not properly marshalling the data between the two applications. You are allocating memory in one application, and then passing a pointer to that memory to a second application. But because application's have private memory spaces and cannot read each other's memory, that pointer is useless to the second application.
Depending on what it points to in that application's memory space, it might be triggering an access violation, or just not working properly.
Perhaps you're confused by the naming of the AllocHGlobal and FreeHGlobal functions. Contrary to what first impressions might suggest, they do not actually allocate and free global memory. At least not memory that is globally accessible to all processes running on a machine. The name comes from the Windows HGLOBAL data type, which used to mean exactly this back in the days of 16-bit Windows, where all applications did share a common memory space and could read each others' memory. But that is no longer the case in modern 32-bit Windows. The names were retained for backwards compatibility reasons. HGLOBAL and HLOCAL mean effectively the same thing nowadays. More on the nitty gritty details is available on MSDN. But that's mostly a curiosity. You don't need to know and understand all of it to get the code working correctly.
The point is that all AllocHGlobal does is allocate memory from the process's default heap, readable only to that process. Hence the need to marshal the memory across processes, making it accessible from the other one receiving the message. Doing this manually is, of course, an option. But not a very good one. It's tricky to get right, and there's little point. Like Tim's comment hints at, the easier option is to use the WM_COPYDATA message, which does the marshalling for you. When you use this message, the data you want to share is packaged in a COPYDATASTRUCT structure.
You can keep most of your existing code to allocate memory, you just need to replace your custom RF_TESTMESSAGE window message with WM_COPYDATA. Sample code, including the necessary structure definition, is available over on the pinvoke website.
Something like this (warning—untested and uncompiled):
Private Const WM_COPYDATA As Integer = &H004A
<StructLayout(LayoutKind.Sequential)> _
Public Structure COPYDATASTRUCT
Public dwData As IntPtr
Public cdData As Integer
Public lpData As IntPtr
End Structure
<StructLayout(LayoutKind.Sequential)> _
Public Structure MyData
Public M As String
Public I As Integer
End Structure
Public Function SendTest()
' Create your data structure, MyData, and fill it.
Dim data As New MyData
data.M = "QWERTY"
data.I = 15
Dim pData As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(data))
Marshal.StructureToPtr(data, pData, False)
' Create the COPYDATASTRUCT you'll use to shuttle the data.
Dim copy As New COPYDATASTRUCT
copy.dwData = IntPtr.Zero
copy.lpData = pData
copy.cbData = Marshal.SizeOf(data)
Dim pCopy As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(copy))
Marshal.StructureToPtr(copy, pCopy, False)
' Send the message to the other application.
SendMessage(New IntPtr(11111), WM_COPYDATA, IntPtr.Zero, pCopy)
' Free the memory we allocated
' (This works because SendMessage is synchronous, and does not
' return until the other application has finished processing
' the data that you have sent it. That also means that the
' other application should not and cannot free the memory.
' If it needs it after processing the message, it needs to
' make a local copy.)
Marshal.FreeHGlobal(pCopy)
Marshal.FreeHGlobal(pData)
End Function
If you decide not to go the easy route using WM_COPYDATA and instead marshal the data yourself, you need to make sure to call the RegisterWindowMessage function (if you are not doing so already in code I can't see) to ensure that the ID of your custom window message is unique.

Related

Improve class design – there is a function that should not be callable

Today, I thought to myself that I would quickly create a descriptive example of the access modifiers Public, Private and Protected for someone, taken from real life.
The following example: A caretaker can spend a budget provided by the landlord himself without having to call for every little thing. For moderate repairs, however, he must call the landlord or the landlord's son and ask for it. The son then knows what money he may spend. Big repairs can only be decided by the landlord (not the son).
I transferred that to source code.
I want to improve the class design, because I can still write Tom.DecideMajorRepair (" ") in Form1.vb, which should not work and what I find unclean.
Form1.vb
Public NotInheritable Class FormMain
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim Hans As New ClassVermieter ' Landlord
Dim Tom As New ClassVermietersSohn ' Landlord's son
'easily accessible
Dim MyBudget As UInt16 = Hans.Caretakers_budget_for_minor_repairs - 200US
Dim Budget_received_1 As UInt16 = Hans.DecideModerateRepair("Unfortunately, the heating is broken!")
' Call son because father didn't answer the phone (or whatever). He can also decide that (protected).
' I still have to ask permission from one of them.
Dim Budget_received_2 As UInt16 = Tom.DecideForHimselfModerateRepair("Unfortunately, the heating is broken!")
' Son cannot decide a major repair - only the landlord.
Dim Budget_received_3 As UInt16 = Tom.DecideForHimselfLargeRepair("Unfortunately, the heating is broken!")
Dim Budget_received_4 As UInt16 = Hans.DecideMajorRepair("Unfortunately, the heating is broken!")
End Sub
End Class
ClassVermieter.vb (landlord)
Public Class ClassVermieter
''' <summary>
''' per month
''' </summary>
Public Property Caretakers_budget_for_minor_repairs As UInt16 = 500US
'–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
Protected Property Permission_for_medium_repairs As UShort
'–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
Private Permission_for_large_repairs As UInt16
'answers the phone call from the caretaker
Public Function DecideModerateRepair(ByVal Message_from_the_caretaker As String) As UInt16
Permission_for_medium_repairs = 1000US
Return Permission_for_medium_repairs
End Function
'answers the phone call from the caretaker
Public Function DecideMajorRepair(ByVal Message_from_the_caretaker As String) As UInt16
Permission_for_large_repairs = 5000US
Return Permission_for_large_repairs
End Function
End Class
ClassVermietersSohn.vb (Landlord's son)
Public Class ClassVermietersSohn : Inherits ClassVermieter
Private ReadOnly zero As UInt16 = 0US
'answers the phone call from the caretaker
'He can decide for himself whether a moderate repair should be carried out.
Public Function DecideForHimselfModerateRepair(ByVal Message_from_the_caretaker As String) As UInt16
Permission_for_medium_repairs = 1000US
Return Permission_for_medium_repairs
End Function
'answers the phone call from the caretaker
Public Function DecideForHimselfLargeRepair(ByVal Message_from_the_caretaker As String) As UInt16
Return zero 'Well, that was nothing, because the son cannot spend (not see) the large amounts!
End Function
End Class
This code is kept elementarily. I am aware that money is not handled with Uint16. The first thing I wanted to do was build the structure.
The difficulty that kept me from solving it myself was that I didn't know how to change the Get and Set without an error message.
In terms of the domain, I would not put the burden of knowing "whom" to ask on the caretaker. Ideally, the caretaker should have a single point of contact which can internally route the request and return true/false.
One design decision is how to parameterise the repair request.
This is most easily done by using an enum RepairTypes with a list of possible repair requests.
Then, I would have an interface "RepairApprover" with a method "boolean request(RepairType rt) throws NotSupportedException" - the exception would be used to "go up the chain"
Next, I would either have a LandlordAssistant class implement RepairApprover,
and a separate Landlord class also implementing RepairApprover.
Internally, the Landlord class would have a private field for LandlordAssistant so that it could reuse the code for minor and medium repairs.
I would then make a "ApprovalHierarchy" class with method "RepairApproval getMyManager()"
I would inject the LandlordAssistant as a "RepairApprover" instance into the Caretaker class constructor.
Internally, the LandlordAssistant.request method would try to respond to a small or medium request. If the request type is MAJOR, it would call ApprovalHierarchy.getMyManager(this).request(..) thus passing the request up the chain.
Hope that helps?

Extrended hex address in visual basic

I'm having trouble implementing an extended hex value onto my project. When I type &H000007F0 into my Visual Basic project, Visual Studio goes and shortens it to &H7F0. In a normal situation, that would be fine but I need the preceding 0's to be included as a parameter for a function on an API.
I've tried creating it as a string Dim MastID As String = "000007F0" and then converting to a hex Convert.ToUInt32(MastID, 16), but that didn't work like I thought it would. Are there any other methods I can try or a VS17 setting I can turn off/on that will allow me to have the full &H000007F0?
I'm doing this to try to connect to a slave device on a CAN bus. I'm using an API that has it's own functions to control the CAN device and initialize a CAN channel. If I am able to pass the extended ID, &H000007F0, then the API would initialize the CAN channel with those parameters.
Parameters being set for slave data:
SlaveData.BroadcastID = &H18DAA1E1
SlaveData.MasterID = &H7F0
SlaveData.SlaveID = &H7F2
SlaveData.IncrementalIdUsed = False
Function being used:
Public Shared Function Connect(
ByVal Handle As UInt32,
ByVal Mode As Byte,
) As Result ' Returns no errors
End Function
Analyzing the CAN BUS, the device ID being used is 7F0h, therefore the slave device does not respond with an acknowledge message. It does however work when I send the same function with the extended master ID my making my own byte array.
As has been pointed out you do not need the leading zeros. Here is some food for thought...
Dim foo As UInt32 = &H7F0 'note missing 0's
' or
Dim provider As Globalization.CultureInfo = Globalization.CultureInfo.CurrentCulture
Dim styles As Globalization.NumberStyles = Globalization.NumberStyles.HexNumber
Dim bar As UInt32
Dim s As String = "000007F0" 'note leading 0's
If UInt32.TryParse(s, styles, provider, bar) Then
If foo = bar Then
Stop ' <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
End If
End If

ByRef parameter to DLL using Structure - Access Violation

UPDATE: Thanks for everyones help. I had no idea about Marshaling my Strings and after doing so everything is now working correctly. I have edited my code below for others who may find this issue
I am currently working on porting some VB6 code to .net
the vb6 application is using a precompiled .dll (made in C++ I beleive) and I do not have any access to its source code.
When googling the function name I get only one google result which DOES have information on its return value and its parameters and I believe I have declared the .DLL correctly
http://jelleybee.com/fun/vgames/emulate/snes/setup/super%20jukebox/Uematsu.html
.DLL function declaration
Declare Function Uematsu_LoadID666Tag Lib "uematsu.dll" (ByVal lpszFileName As String, ByRef lpTag As ID666_tag) As Boolean
I have defined my structure like this
Public Structure ID666_tag
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=256)> Public Song As String 'Title of song
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=256)> Public Game As String 'Name of game
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=256)> Public Artist As String 'Name of artist
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=256)> Public Dumper As String 'Name of dumper
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=256)> Public Dated As String 'Date dumped
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=256)> Public Emu As String 'Emulator used to dump
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=256)> Public Comment As String 'Optional comment
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=256)> Public OST As String 'Origonal soundtrak title
Public SongLength As Integer 'Length of song
Public LoopLength As Integer 'Length of the loop (extended ID666)
Public EndLength As Integer 'Length of end (extended ID666)
Public FadeLength As Integer 'length of fade out
Public OSTDiscNum As Byte 'Origonal sound track disc number
Public OSTTrack As Short 'Original sound track track number
Public Publisher As String 'Name Of Publisher
Public Copyright As Short 'Date of Copyright
Public Mute As Byte 'Channels to mute
End Structure
I am using the function like this
Function Extract_ID666(Fname As String) As ID666_tag
Dim TempExtr As ID666_tag
If Uematsu_LoadID666Tag(Fname, TempExtr) = True Then
MessageBox.Show("DONE")
Else
MessageBox.Show("FAIL")
End If
End Function
However when ever I run my Extract_ID666 function I receive an access violation error.
I know this has something to do with the way I setup to use TempExtr or how I have declared my .dll function. But I can not track it down.
Any ideas or solutions to this problem would be much appreciated.
I have searched SO for a long time trying to find a similar question but could not find a solution.
Access Violations are usually caused by an external .DLL trying to write to memory which is not designated. Here is an SO question with great detail on Access Violations
Common causes of - Access Violation errors under .NET
Marshaling is the process of converting a data field, or an entire set of related structures, into a serialized string that can be sent in a message. Here is an SO question with more information on Marshaling
What is marshalling? What is happening when something is "marshalled?"
Here is MSDN information on Marshaling Strings
http://msdn.microsoft.com/en-us/library/s9ts558h%28v=vs.71%29.aspx

ByRef vs ByVal Clarification

I'm just starting on a class to handle client connections to a TCP server. Here is the code I've written thus far:
Imports System.Net.Sockets
Imports System.Net
Public Class Client
Private _Socket As Socket
Public Property Socket As Socket
Get
Return _Socket
End Get
Set(ByVal value As Socket)
_Socket = value
End Set
End Property
Public Enum State
RequestHeader ''#Waiting for, or in the process of receiving, the request header
ResponseHeader ''#Sending the response header
Stream ''#Setup is complete, sending regular stream
End Enum
Public Sub New()
End Sub
Public Sub New(ByRef Socket As Socket)
Me._Socket = Socket
End Sub
End Class
So, on my overloaded constructor, I am accepting a reference to an instance of a System.Net.Sockets.Socket, yes?
Now, on my Socket property, when setting the value, it is required to be ByVal. It is my understanding that the instance in memory is copied, and this new instance is passed to value, and my code sets _Socket to reference this instance in memory. Yes?
If this is true, then I can't see why I would want to use properties for anything but native types. I'd imagine there can be quite a performance hit if copying class instances with lots of members. Also, for this code in particular, I'd imagine a copied socket instance wouldn't really work, but I haven't tested it yet.
Anyway, if you could either confirm my understanding, or explain the flaws in my foggy logic, I would greatly appreciate it.
I think you're confusing the concept of references vs. value types and ByVal vs. ByRef. Even though their names are a bit misleading, they are orthogonal issues.
ByVal in VB.NET means that a copy of the provided value will be sent to the function. For value types (Integer, Single, etc.) this will provide a shallow copy of the value. With larger types this can be inefficient. For reference types though (String, class instances) a copy of the reference is passed. Because a copy is passed in mutations to the parameter via = it won't be visible to the calling function.
ByRef in VB.NET means that a reference to the original value will be sent to the function (1). It's almost like the original value is being directly used within the function. Operations like = will affect the original value and be immediately visible in the calling function.
Socket is a reference type (read class) and hence passing it with ByVal is cheap. Even though it does perform a copy it's a copy of the reference, not a copy of the instance.
(1) This is not 100% true though because VB.NET actually supports several kinds of ByRef at the callsite. For more details, see the blog entry The many cases of ByRef
Remember that ByVal still passes references. The difference is that you get a copy of the reference.
So, on my overloaded constructor, I am accepting a reference to an instance of a System.Net.Sockets.Socket, yes?
Yes, but the same would be true if you asked for it ByVal instead. The difference is that with ByVal you get a copy of the reference — you have new variable. With ByRef, it's the same variable.
It is my understanding that the instance in memory is copied
Nope. Only the reference is copied. Therefore, you're still working with the same instance.
Here's a code example that explains it more clearly:
Public Class Foo
Public Property Bar As String
Public Sub New(ByVal Bar As String)
Me.Bar = Bar
End Sub
End Class
Public Sub RefTest(ByRef Baz As Foo)
Baz.Bar = "Foo"
Baz = new Foo("replaced")
End Sub
Public Sub ValTest(ByVal Baz As Foo)
Baz.Bar = "Foo"
Baz = new Foo("replaced")
End Sub
Dim MyFoo As New Foo("-")
RefTest(MyFoo)
Console.WriteLine(MyFoo.Bar) ''# outputs replaced
ValTest(MyFoo)
Console.WriteLine(MyFoo.Bar) ''# outputs Foo
My understanding has always been that the ByVal/ByRef decision really matters most for value types (on the stack). ByVal/ByRef makes very little difference at all for reference types (on the heap) UNLESS that reference type is immutable like System.String. For mutable objects, it doesn't matter if you pass an object ByRef or ByVal, if you modify it in the method the calling function will see the modifications.
Socket is mutable, so you can pass any which way you want, but if you don't want to keep modifications to the object you need to make a deep copy yourself.
Module Module1
Sub Main()
Dim i As Integer = 10
Console.WriteLine("initial value of int {0}:", i)
ByValInt(i)
Console.WriteLine("after byval value of int {0}:", i)
ByRefInt(i)
Console.WriteLine("after byref value of int {0}:", i)
Dim s As String = "hello"
Console.WriteLine("initial value of str {0}:", s)
ByValString(s)
Console.WriteLine("after byval value of str {0}:", s)
ByRefString(s)
Console.WriteLine("after byref value of str {0}:", s)
Dim sb As New System.Text.StringBuilder("hi")
Console.WriteLine("initial value of string builder {0}:", sb)
ByValStringBuilder(sb)
Console.WriteLine("after byval value of string builder {0}:", sb)
ByRefStringBuilder(sb)
Console.WriteLine("after byref value of string builder {0}:", sb)
Console.WriteLine("Done...")
Console.ReadKey(True)
End Sub
Sub ByValInt(ByVal value As Integer)
value += 1
End Sub
Sub ByRefInt(ByRef value As Integer)
value += 1
End Sub
Sub ByValString(ByVal value As String)
value += " world!"
End Sub
Sub ByRefString(ByRef value As String)
value += " world!"
End Sub
Sub ByValStringBuilder(ByVal value As System.Text.StringBuilder)
value.Append(" world!")
End Sub
Sub ByRefStringBuilder(ByRef value As System.Text.StringBuilder)
value.Append(" world!")
End Sub
End Module
Think of C, and the difference between a scalar, like int, and an int pointer, and a pointer to an int pointer.
int a;
int* a1 = &a;
int** a2 = &a1;
Passing a is by value.
Passing a1 is a reference to a; it is the address of a.
Passing a2 is a reference to a reference; what is passed is the address of a1.
Passing a List variable using ByRef is analogous to the a2 scenario. It is already a reference. You are passing a reference to a reference. Doing that means that not only can you change the contents of the List, you can can change the parameter to point to an entirely different List. It also means you can not pass a literal null instead of an instance of List

VB.NET pointer interop question

I am trying to write a VB.NET program that will call a function in an unmanaged C DLL passing the structure like this:
typedef struct {
unsigned char *msg;
int msglen;
}
What I have not been able to figure out is how to handle the "unsigned char *msg" part. How would you define this in the VB.NET Structure?
<StructLayout(LayoutKind.Sequential)> _
public structure foo
<MarshalAs(UnmanagedType.LPStr)> dim msg as string
dim msgLen as integer
end structure
This depends a lot on how the memory for the msg field is handled. You need to be careful to free any allocated memory which is transfered to managed code.
That being said I think the most straight forward interop type is as follows
Public Structure S1
Public msg as IntPtr
Public msgLen as Integer
End Structure
To get the actual msg value as a String you'll need to use the following code.
Public Function GetString(ByVal s1 as S1) As String
return Marshal.PtrToStringAnsi(s1.msg, s1.msgLen)
End Function
To create an S1 instance based on a String do the following. Note: You will need to free the memory allocated here if the calling function does not take ownership.
Public Function CreateS1(ByVal str As String) As S1
Dim local As New S1
local.msg = Marshal.StringToHGlobalAnsi(str)
local.msgLen = str.Length
return local
End Function