Function from a referenced library which has function pointers as parameters - vb.net

UPDATE: Changed question to match updated code and be more specific to my problem.
I have no experience working with VB.Net or Visual Studio, and limited experience with C. I have been trying to learn about Function Pointers and Delegates but still don't fully understand them.
I am writing a project in VB.Net that makes calls to methods from a dll file.
C code from the dll file:
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef void (__stdcall *fp_setbaud)(WORD);
typedef short (__stdcall *fp_get)(WORD);
typedef void (__stdcall *fp_put)(BYTE);
typedef void (__stdcall *fp_flush)(void);
typedef void (__stdcall *fp_delay)(WORD);
BYTE __stdcall InitRelay(fp_setbaud _setbaud, fp_get _get, fp_put _put,
fp_flush _flush, fp_delay _delay) { ... }
BYTE __stdcall ReadRelay(void){ ... }
VB.net Module code:
Declare Function InitRelay Lib "Z:\Devel\RelayAPI\Debug\RelayAPI.dll" (ByVal setbaud As Action(Of Short), ByVal getit As Func(Of Short, Short), ByVal putit As Action(Of Short), ByVal flushit As Action, ByVal delay As Action(Of Short)) As Byte
Declare Function ReadRelay Lib "Z:\Devel\RelayAPI\Debug\RelayAPI.dll" () As Byte
Public Sub setbaud(ByVal baud As Short)
...
End Sub
Public Function getit(ByVal timeout As Short) As Short
...
End Function
Public Sub putit(ByVal dat As Short)
...
End Sub
Public Sub flushit()
...
End Sub
Public Function delaymS(ByVal mS As Short) As Short
...
End Function
VB.Net Form Code:
Dim a As Byte
Call InitRelay(AddressOf moduleCode.setbaud, AddressOf moduleCode.getit,
AddressOf moduleCode.putit, AddressOf moduleCode.flushit,
AddressOf moduleCode.delaymS)
a = ReadRelay()
The error I am getting is when I call a = ReadRelay() from within my VB.Net code. The error appears on each one of the parameters and says the following:
An unhandled exception of type 'System.AccessViolationException' occurred
The ReadRelay function uses all of the functions passed to the InitRelay function. InitRelay is called prior to the ReadRelay and gives me no errors. I am assuming the error still has to do with how I am passing the function pointers to the InitRelay function.
I have been using this website to try to figure out the type conversions but I still don't know what to do.
Does anyone have any information on what I should be doing to properly call these functions?
EDIT
New Delegate declarations as suggested below:
Private setBaudDelegate As New Action(Of Short)(AddressOf modCommStuff.setbaud)
Private getItDelegate As New Func(Of Short, Short)(AddressOf modCommStuff.getit)
Private putItDelegate As New Action(Of Short)(AddressOf modCommStuff.putit)
Private flushItDelegate As New Action(AddressOf modCommStuff.flushit)
Private delayItDelegate As New Action(Of Short)(AddressOf modCommStuff.delaymS)
....
Call InitRelay(setBaudDelegate, getItDelegate, putItDelegate, flushItDelegate, delayItDelegate)

I think you need to pin the delegates so that they are not garbage collected.
dim handle as GCHandle = GCHandle.Alloc(ObjectToPin, GCHandleType.Pinned)
Don't forget to free the object after you are done with it using .Free
handle.Free()
More information is available at this website.
http://manski.net/2012/06/pinvoke-tutorial-pinning-part-4/
UPDATE 1
After reading more it looks like pinning delegates is not specifically allowed, but you still have to keep the delegate in memory. I would try creating instance variables for your delegates and keeping them in fields defined in your form's code. This should be enough to keep the delegates from getting garbage collected and keep the unmanaged "stubs" from getting cleaned up.
Along the same lines, managed Delegates can be marshaled to unmanaged
code, where they are exposed as unmanaged function pointers. Calls on
those pointers will perform an unmanaged to managed transition; a
change in calling convention; entry into the correct AppDomain; and
any necessary argument marshaling. Clearly the unmanaged function
pointer must refer to a fixed address. It would be a disaster if the
GC were relocating that! This leads many applications to create a
pinning handle for the delegate. This is completely unnecessary. The
unmanaged function pointer actually refers to a native code stub that
we dynamically generate to perform the transition & marshaling. This
stub exists in fixed memory outside of the GC heap.
However, the application is responsible for somehow extending the
lifetime of the delegate until no more calls will occur from unmanaged
code. The lifetime of the native code stub is directly related to the
lifetime of the delegate. Once the delegate is collected, subsequent
calls via the unmanaged function pointer will crash or otherwise
corrupt the process.
Update 2
Here is an example with actual code. I only did it for something that takes a single argument, if you need it adjusted for the additional arguments let me know.
Private Sub InitRelay(d1 As Action(Of Short))
'This sub represents the InitRelay function exported by your library. You wouldn't actually have this directly in your code.
End Sub
Private Sub setBaud(baud As Short)
'This is the SetBaud sub in your module
End Sub
'This would be in your form code, at the class level (outside all subs)
Private setBaudDelegate As New Action(Of Short)(AddressOf setBaud)
'This sub is whatever sub in your code calls InitRelay
Private Sub Test()
InitRelay(setBaudDelegate)
End Sub

Related

Can a CodeAnalysis return a false positive of CA2202? or is really something wrong with my code?

I'm suffering the same issue explained here but iterating a EnvDTE.Processes.
In the question that I linked the user #Plutonix affirms it is a false warning, and I think him reffers to the obj.Getenumerator() mention so I assume my problem will be considered a false warning too, however, if this is a false warning I would like to know more than an affirmation, the arguments to say it is a false warning.
This is the warning:
CA2202 Do not dispose objects multiple times Object
'procs.GetEnumerator()' can be disposed more than once in method
'DebugUtil.GetCurrentVisualStudioInstance()'. To avoid generating a
System.ObjectDisposedException you should not call Dispose more than
one time on an object.: Lines:
214 Elektro.Application.Debugging DebugUtil.vb 214
This is the code, procs object is the involved one on the warning, but I don't see any disposable object:
Public Shared Function GetCurrentVisualStudioInstance() As DTE2
Dim currentInstance As DTE2 = Nothing
Dim processName As String = Process.GetCurrentProcess.MainModule.FileName
Dim instances As IEnumerable(Of DTE2) = DebugUtil.GetVisualStudioInstances
Dim procs As EnvDTE.Processes
For Each instance As DTE2 In instances
procs = instance.Debugger.DebuggedProcesses
For Each p As EnvDTE.Process In procs
If (p.Name = processName) Then
currentInstance = instance
Exit For
End If
Next p
Next instance
Return currentInstance
End Function
PS: Note that the code-block depends on other members but they are unrelevant for this question.
Short version: this looks like a bug in the Code Analysis component to me.
Long version (hey, you suckered me into spending the better part of my afternoon and evening deciphering this, so you might as well spend a little time reading about it :) )…
The first thing I did was look at the IL. Contrary to my guess, it did not contain multiple calls to Dispose() on the same object. So much for that theory.
The method did, however, contain two separate calls to Dispose(), just on different objects. By this time, I was already convinced this was a bug. I've seen mention of CA2202 being triggered when dealing with related classes where one class instance "owns" an instance of the other class, and both instances are disposed. While inconvenient and worth suppressing, the warning seems valid in those cases; one of the objects really is getting disposed of twice.
But in this case, I had two separate IEnumerator objects; one did not own, nor was even related to, the other. Disposing one would not dispose the other. Thus, Code Analysis was wrong to warn about it. But what specifically was confusing it?
After much experimentation, I came up with this near-minimal code example:
Public Class A
Public ReadOnly Property B As B
Get
Return New B
End Get
End Property
End Class
Public Interface IB
Function GetEnumerator() As IEnumerator
End Interface
Public Class B : Implements IB
Public Iterator Function GetEnumerator() As IEnumerator Implements IB.GetEnumerator
Yield New C
End Function
End Class
Public Class C
Dim _value As String
Public Property Value As String
Get
Return _value
End Get
Set(value As String)
_value = value
End Set
End Property
End Class
Public Shared Function GetCurrentVisualStudioInstance2() As A
For Each a As A In GetAs()
For Each c As C In a.B
If (c.Value = Nothing) Then
Return a
End If
Next c
Next a
Return Nothing
End Function
Public Shared Iterator Function GetAs() As IEnumerable(Of A)
Yield New A()
End Function
This produces the same spurious CA2202 you are seeing in the other code example. Interestingly though, a minor change to the declaration and implementation of interface IB causes the warning to go away:
Public Interface IB : Inherits IEnumerable
End Interface
Public Class B : Implements IB
Public Iterator Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
Yield New C
End Function
End Class
Somehow, Code Analysis is getting confused by the non-IEnumerable implementation of GetEnumerator(). (Even more weirdly is that the actual type you're using, the Processes interface in the DTE API, both inherits IEnumerable and declares its own GetEnumerator() method…but it's the latter that is the root of the confusion for Code Analysis, not the combination).
With that in hand, I tried to reproduce the issue in C# and found that I could not. I wrote a C# version that was structured exactly as the types and methods in the VB.NET version, but it passed Code Analysis without warnings. So I looked at the IL again.
I found that the C# compiler generates code very similar to, but not exactly the same as, the VB.NET compiler. In particular, for the try/finally blocks that protect the IEnumerator returned for each loop, all of the initialization for those loops is performed outside the try block, while in the VB.NET version the initialization is performed inside.
And apparently, that is also enough to prevent Code Analysis from getting confused about the usage of the disposable objects.
Given that it seems to be the combination of VB.NET's implementation of For Each and the nested loops, one work-around would be to just implement the method differently. I prefer LINQ syntax anyway, and here is a LINQified version of your method that compiles without the Code Analysis warning:
Public Shared Function GetCurrentVisualStudioInstance() As DTE2
Dim processName As String = Process.GetCurrentProcess.MainModule.FileName
Return GetVisualStudioInstances.FirstOrDefault(
Function(instance)
Return instance.Debugger.DebuggedProcesses.Cast(Of EnvDTE.Process).Any(
Function(p)
Return p.Name = processName
End Function)
End Function)
End Function
And for completeness, the C# version (since all of this code started when a C# implementation was converted to VB.NET and then extended to handle the "current instance" case):
public static DTE2 GetCurrentVisualStudioInstance()
{
string processName = Process.GetCurrentProcess().MainModule.FileName;
return GetVisualStudioInstances()
.FirstOrDefault(i => i.Debugger.DebuggedProcesses
.Cast<EnvDTE.Process>().Any(p => p.Name == processName));
}

When are a module's variables in VB.NET instantiated?

I was wondering where in the lifetime of the program a variable that is in a module would be initialized as in this example:
Module Helper
Friend m_Settings As New UserSettings()
Sub Foo()
'...
End Sub
Sub Bar()
'...
End Sub
End Module
Public Class UserSettings
Public Property UserName As String
Public Property PrefServer As Integer
Public Sub New()
'...
End Sub
Public Sub LoadSettings()
'...
End Sub
End Class
When would m_Settings be initialized? I can set a break point at the constructor for UserSettings and look at the call stack, but I see "External Code" in there but that doesn't tell me a lot.
The CLR has no direct support for VB.NET modules, it requires all methods and variables to be part of a type. So the VB.NET compiler actually generates a class under the hood. All of the functions you wrote in the module become static methods of that class. All of the variables you declared in the module become static fields of the class.
Any variables that are initialized in your module causes a static constructor to be generated. And the initialization code is moved into this constructor.
Now CLR rules apply: a soon as the jitter touches any of the members of this class, the CLR runs the static constructor. Which then initializes all of the module variables. Which is also why you see [external code] on the call stack, the call originated inside the CLR.
It is rare to have problems with this, the static constructor guarantee in the CLR is a very strong one. About the only tricky mishap is a variable initializer that causes an exception to be thrown. That's when the guts start showing. The stack trace is pretty mystifying since it shows code that doesn't exist in your source code. The actual exception thrown is a TypeInitializationException, very mystifying since you didn't write any type, you need to look at its InnerException to find the real reason.

Autovivified properties?

suppose I declare a class like this:
Class tst
Public Props As New Dictionary(Of String, MyProp)
End Class
and added properties something along these lines:
Dim t As New tst
t.Props.Add("Source", new MyProp(3))
but now want to access it like this:
t.Source
how can I create a getter without knowing the name of the getter?
Ok, if you insist on "auto-vivifying", the only way I know of to do something like that is to generate the code as a string, and then compile it at runtime using the classes in the System.CodeDom.Compiler namespace. I've only ever used it to generate complete classes from scratch, so I don't know if you could even get it to work for what need to add properties to an already existing class, but perhaps you could if you compiled extension methods at runtime.
The .NET framework includes multiple implementations of the CodeDomeProvider class, one for each language. You will most likely be interested in the Microsoft.VisualBasic.VBCodeProvider class.
First, you'll need to create a CompilerParameters object. You'll want to fill its ReferencedAssemblies collection property with a list of all the libraries your generated code will need to reference. Set the GenerateExecutable property to False. Set GenerateInMemory to True.
Next, you'll need to create a string with the source code you want to compile. Then, call CompileAssemblyFromSource, passing it the CompilerParameters object and the string of source code.
The CompileAssemblyFromSource method will return a CompilerResults object. The Errors collection contains a list of compile errors, if there are any, and the CompiledAssembly property will be a reference to your compiled library (as an Assembly object). To create an instance of your dynamically compiled class, call the CompiledAssembly.CreateInstance method.
If you're just generating a small amount of code, it's pretty quick to compile it. But if it's a lot of code, you may notice an impact on performance.
Here's a simple example of how to generate a dynamic class containing a single dynamic property:
Option Strict Off
Imports System.CodeDom.Compiler
Imports Microsoft.VisualBasic
Imports System.Text
Public Class Form3
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim code As StringBuilder = New StringBuilder()
code.AppendLine("Namespace MyDynamicNamespace")
code.AppendLine(" Public Class MyDynamicClass")
code.AppendLine(" Public ReadOnly Property WelcomeMessage() As String")
code.AppendLine(" Get")
code.AppendLine(" Return ""Hello World""")
code.AppendLine(" End Get")
code.AppendLine(" End Property")
code.AppendLine(" End Class")
code.AppendLine("End Namespace")
Dim myDynamicObject As Object = generateObject(code.ToString(), "MyDynamicNamespace.MyDynamicClass")
MessageBox.Show(myDynamicObject.WelcomeMessage)
End Sub
Private Function generateObject(ByVal code As String, ByVal typeName As String) As Object
Dim parameters As CompilerParameters = New CompilerParameters()
parameters.ReferencedAssemblies.Add("System.dll")
parameters.GenerateInMemory = True
parameters.GenerateExecutable = False
Dim provider As VBCodeProvider = New VBCodeProvider()
Dim results As CompilerResults = provider.CompileAssemblyFromSource(parameters, code)
If results.Errors.HasErrors Then
Throw New Exception("Failed to compile dynamic class")
End If
Return results.CompiledAssembly.CreateInstance(typeName)
End Function
End Class
Note, I never use Option Strict Off, but for the sake of simplicity in this example, I turned it off so I could simply call myDynamicObject.WelcomeMessage without writing all the reflection code myself.
Calling methods on objects using reflection can be painful and dangerous. Therefore, it can be helpful to provide a base class or interface in a shared assembly which is referenced by both the generated assembly, and the fixed assembly which calls the generated assembly. That way, you can use the dynamically generated objects through a strongly typed interface.
I figured based on your question that you were just more used to dynamic languages like JavaScript, so you were just thinking of a solution using the wrong mindset, not that you really needed to or even should be doing it this way. But, it is definitely useful in some situations to know how to do this in .NET. It's definitely not something you want to be doing on a regular basis, but, if you need to support custom scripts to perform complex validation or data transformations, something like this can be very useful.

Issue with passing parameters by reference into public shared methods

I have a List(Of AddlInfo) with AddlInfo being an object.
I'm trying to pass addlInfoList by reference into a function of another class:
Public Shared Sub SortAddlInfo(ByRef addlInfoList As List(Of AddlInfo))
addlInfoList.Sort(AddressOf Comparer)
End Sub
Private Function Comparer(ByVal x As AddlInfo, ByVal y As AddlInfo) As Integer
Dim result As Integer = x.AddlInfoType.CompareTo(y.AddlInfoType)
Return result
End Function
This works if I'm not passing the reference into another class, but when I try to do this, I get the following error:
Overload resolution failed because no accessible 'Sort' can be called with these arguments:
'Public Sub Sort(comparison As System.Comparison(Of AddlInfo))': Cannot refer to an instance member of a class from within a shared method or shared member initializer without an explicit instance of the class.
'Public Sub Sort(comparer As System.Collections.Generic.IComparer(Of AddlInfo))': 'AddressOf' expression cannot be converted to 'System.Collections.Generic.IComparer(Of MyProject.AddlInfo)' because 'System.Collections.Generic.IComparer(Of MyProject.AddlInfo)' is not a delegate type.
I could put the methods back into the calling class, but I'd like to be able to call these methods from different classes within my application.
I could also instantiate a fresh List in the methods, but why? Seems silly.
Any way around this? (Or do I need to explain more?)
Thanks in advance!
Try putting your compare function into a class that implements IComparer.

Run-Time Check Failure #0 vb.net callback from C dll

I'm writing Add-inn Application A in VB.Net and DLL B in C language.
Application A pass callback method to dll B.
When certain event occur the dll invoke the callback from A.
Whole works fine on my PC but when I move it to Notebook I get an error:
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
This is part of C code:
typedef void (__cdecl * OFFICE_PTR)();
void TAPIClient::tapiCallBack(
DWORD hDevice,
DWORD dwMessage,
DWORD dwInstance,
DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3){
switch (dwMessage)
{
case LINE_CALLSTATE:
switch (dwParam1)
{
case LINECALLSTATE_OFFERING:
if(dwInstance!=NULL)
{
try
{
OFFICE_PTR vbFunc =(OFFICE_PTR)dwInstance;
vbFunc( );//Critical moment
}
catch(...)
{
MessageBox (NULL, L"( (OFFICE_PTR)dwInstance )(&sCallNr)",L"ERROR",MB_OK);
}
}
break;
};
break;
}
}
Where dwInstance is a address of application A callback method
This is part of VB.Net code:
Public Class TapiPlugin
Public Delegate Sub P_Fun()
Private Declare Function startSpy _
Lib "TAPIClient.dll" _
(ByVal pFun As P_Fun) As IntPtr
Public Shared Sub simpleTest()
MsgBox("Plugin sub simpleTest")
End Sub
Public Sub onStart()
Dim pBSTR As IntPtr
pBSTR = startSpy(AddressOf simpleTest)
MsgBox(Marshal.PtrToStringAuto(pBSTR))
Marshal.FreeBSTR(pBSTR)
End Sub
End Class
The Error occur when I try call 'vbFunc( )'. I would be grateful for any help. :D
If the calling convention is cdecl, then you need to declare your delegate like this:
<UnmanagedFunctionPointer(CallingConvention.Cdecl)>
Public Delegate Sub P_Fun()
You can only do this in .NET 2.0 and after, as the attribute was not introduced before then (and the interop layer was not changed to acknowledge it before that).
If the calling convention is indeed stdcall then the delegate can remain as is. You said it is stdcall, but I have doubts, since the exception is explicitly telling you that there might be a mismatch in calling conventions.
Do the two computers have different pointer sizes perhaps? Maybe one is a 64 bit machine and the other only 32?
typedef void (__cdecl * OFFICE_PTR)();
void TAPIClient::tapiCallBack(
DWORD hDevice,
DWORD dwMessage,
DWORD dwInstance,
...){
...
OFFICE_PTR vbFunc =(OFFICE_PTR)dwInstance;
vbFunc( );//Critical moment
The DWORD type is not really valid for passing pointer types. You should be using INT_PTR I guess.
I thing it is not a reason to check it out I passed the callback as global pointer of type OFFICE_PTR and i get the same result. On PC it work fine on Notebook it crash :(
A have to apologies for a mistake I wrote that the def look like:
typedef void (__cdecl * OFFICE_PTR)();
but for real it looks like
typedef void (__stdcall * OFFICE_PTR)();