Stack Imbalance - vb.net

A call to PInvoke function 'ReleaseCapture' has unbalanced the stack.
This is likely because the managed PInvoke signature does not match
the unmanaged target signature. Check that the calling convention and
parameters of the PInvoke signature match the target unmanaged signature.
The function has been defined this way and has been working for over 6 years just fine. We didn't get word of this error until a user reported it. It happens when a user starts to drag a user control on the screen, if it's not dragged it is fine.
<DllImport("user32")> _
Public Shared Function ReleaseCapture(ByVal hwnd As IntPtr) As Integer
End Function
This function is called on the user control MouseDown event. For example:
Private Sub uxCalcTitleBar_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles uxCalcTitleBar.MouseDown, lblCalcTitle.MouseDown
If e.Button = Windows.Forms.MouseButtons.Left And e.Clicks = 1 Then
If Not Me._CalcIsMoving And Not Me._CalcIsPackedForMove Then
Me.Calc_PackForMove()
End If
ReleaseCapture(Me.uxCalculator.Handle) **ERROR HERE**
SendMessage(Me.uxCalculator.Handle, WM_SYSCOMMAND, MOUSE_MOVE, 0)
Me._CalcNewLocation = Me.uxCalculator.Location
Me.uxCalcTitleBar_MouseUp(sender, e)
End If
End Sub
One thing we noticed, this started happening after moving to the 4.5 framework from 2.0. Do not know if this makes a difference, but I think it should not. After some research I found that the resolution should be reviewing the managed platform invoke signature and calling convention to confirm it matches the signature and calling convention of the native target.
What I have Tried
I examined the signature and it seem's to be just fine, nothing I can actually see. I also specified the convention as such to clear the stack it doesn't help...
<DllImport("user32", CallingConvention:=CallingConvention.Cdecl)> _
Public Shared Function ReleaseCapture(ByVal hwnd As IntPtr) As Integer
End Function

The correct signature is this:
<DllImport("user32.dll")> _
Public Shared Function ReleaseCapture() As Boolean
End Function
The function does not take any parameters as can be seen from the documentation: https://msdn.microsoft.com/en-us/library/windows/desktop/ms646261.aspx
Regarding your use of CallingConvention.Cdecl, that is a mistake. The calling convention is CallingConvention.StdCall, which is the default and so can be omitted. You don't get to decide what the calling convention is any more than you get to decide what the parameters are. You cannot decide to impose CallingConvention.Cdecl as a means to "clear the stack". That is just meaningless. The implementer of the function decides its calling convention, parameters and so on. Your job is to meet the interface contract specified by the implementer of the function.
One thing we noticed, this started happening after moving to the 4.5 framework from 2.0.
Indeed. Version 2.0 of .net did not contain the pInvokeStackImbalance MDA that is producing this message. Your program has been wrong for all that time and you've just been lucky. Now that you are using better tooling, that tooling has been able to inform you of your error.

Related

vb.net How can I pass a reference to a structure from one thread to two other threads? [duplicate]

I get a build error when I try to pass a structure reference into a thread.
dim antenna_frame_buffer as Antenna_Frame_Buffer_structure
...
new_buffer_write_thread = new Thread( AddressOf frame_buffer_write_Thread )
new_buffer_write_thread.Start( antenna_frame_buffer )
...
sub frame_buffer_write_Thread( ByRef antenna_frame_buffer as Antenna_Frame_Buffer_structure )
...
THE ERROR...
Severity Code Description Project File Line Suppression State
Error BC30518 Overload resolution failed because no accessible 'New'
can be called with these arguments:
'Public Overloads Sub New(start As ThreadStart)': Method 'Public Sub frame_buffer_write_Thread(ByRef antenna_frame_buffer As
Embedded_Communication_Interface.Antenna_Frame_Buffer_structure)' does
not have a signature compatible with delegate 'Delegate Sub
ThreadStart()'.
'Public Overloads Sub New(start As ParameterizedThreadStart)': Method 'Public Sub frame_buffer_write_Thread(ByRef
antenna_frame_buffer As
Embedded_Communication_Interface.Antenna_Frame_Buffer_structure)' does
not have a signature compatible with delegate 'Delegate Sub
ParameterizedThreadStart(obj As Object)'. SYS HUB and HW
GUI C:\PRIMARY\WORK\SYSTEM
HUB\SOURCE\Embedded_Communication_Interface.vb 1030 Active
You can't. You're not actually calling that method directly anyway, so how could the ByRef parameter be of use? You're calling the Thread.Start method and it doesn't have a ByRef parameter, so you couldn't get the value back that way. That's even ignoring the fact that Thread.Start returns immediately and you don't know when the method it invokes will return, so you couldn't know when the modified value was available anyway. In short, ByRef parameters don't make sense in such a context so don't try to use them.
EDIT:
You may be able to use a Lambda expression that calls your method as the delegate when you create the thread and then you will be able to get the code to run:
new_buffer_write_thread = New Thread(Sub() frame_buffer_write_Thread(antenna_frame_buffer))
new_buffer_write_thread.Start()
I don't think that that will ever return the parameter value after the method completes to the original variable though and, if it did, you'd not know when it did so because you don't know when the method completed, which is exactly why it shouldn't happen at all. I think that LINQ creates a closure that shields the original variable from changes via that parameter, even though it appears like they'd be linked.
Structure cannot be passed by reference to a thread.
However, and fortunately, an object of a class CAN be passed by reference.

Calling API from non-main thread causing a missing interface error

I'm using system image list in my application, as will be explained below. Calling this from the main thread works perfectly, however, if when I try to do this from a different thread, it causes error for a reason that I don't really understand.
The system image list interface is:
<ComImportAttribute(), GuidAttribute("46EB5926-582E-4017-9FDF-E8998DAA0950"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)> _
Private Interface iImageList
<PreserveSig> _
Function Add(ByVal hbmImage As IntPtr, ByVal hbmMask As IntPtr, ByRef pi As Integer) As Integer
'here comes the rest of standard functions for this interface
End Interface
The Windows API definition is:
<DllImport("shell32.dll", EntryPoint:="#727")> _
Private Shared Function SHGetImageList(ByVal iImageList As Integer, ByRef riid As Guid, ByRef ppv As IImageList) As Integer
End Function
And finally, to get\create the image list, I am executing the following:
Dim ID As New Guid("46EB5926-582E-4017-9FDF-E8998DAA0950")
Dim Intrf As iImageList=Nothing
Dim extraLargeIcons = &H2
Dim Ret As Integer = SHGetImageList(CInt(Fix(extraLargeIcons)), ID, Intrf )
This last call to create the list casing the following error if called from a non-main thread (simply calling it from standard .Net Background worker):
Unable to cast COM object of type 'System.__ComObject' to interface type 'iImageList'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{46EB5926-582E-4017-9FDF-E8998DAA0950}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).
I would like to understand why this happening and why iImageList interface become suddenly not supported when created in a different thread (it works perfectly from main thread), and if there is a way to resolve this.
P.S.
I did read similar questions, but I am not passing information across threads (I am trying to create and use Imagelist from one thread), also I did not understood how to resolve this. Using STA\MTA-Thread attribute gave nothing. Finally, I should say that I know little about COM business.

How to get ObjectContext to compile?

I'm converting some code from VB6 to VB.Net and it contains a number of occurrences of:-
Private m_myobj As ObjectContext
m_myobj = GetObjectContext()
' do stuff, then
m_myobj.SetAbort()
' or
m_myobj.SetComplete()
By dint of including a reference to System.Data and System.Data.Entity and Imports System.Data.Object I have managed to get the declaration to compile, but the others have so far resisted. The errors showing are:-
'GetObjectContext' is not declared. It may be inaccessible due to its protection level
'SetComplete' is not a member of 'System.Data.Objects.ObjectContext'
'SetAbort' is not a member of 'System.Data.Objects.ObjectContext'
It would appear from the documentation that the two methods don't actually exist but they (presumably) must have worked in the VB6. Does anyone know what I should do about this?
The SetAbort and SetComplete methods are calls out to the COM+ (Was once called MTS) application that the class is running in, and allow parts of the code to vote on whether distributed database transactions will be committed by the com+ environment. You will want to look at the code path and see whether the code is required or not. If it is you will want to investigate other methods of extending database transactions across multiple DB accesses. In my experience people sometimes got excited about this technology and implemented it unnecessarily and it is quite possible that you can just eliminate the code.
The simplest way to duplicate this functionality would be to maintain an open connection and call begintran and endtran appropriately, this kind of stuff tend to get complicated though.
I suppose the closest modern Microsoft equivilant is Entity Framework.
GetObjectContext is a Windows function. You can declare it using P/Invoke like this:
<DllImport("ComSvcs.dll", CallingConvention:=CallingConvention.Cdecl)> _
Public Shared Function GetObjectContext(<Out> ByRef pCtx As IObjectContext) As Integer
End Function
<ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("51372AE0-CAE7-11CF-BE81-00AA00A2FA25")> _
Public Interface IObjectContext
Function CreateInstance(ByVal rclsid As Guid, ByVal riid As Guid) As Object
Sub SetComplete()
Sub SetAbort()
Sub EnableCommit()
Sub DisableCommit()
<PreserveSig> _
Function IsInTransaction() As Boolean
<PreserveSig> _
Function IsSecurityEnabled() As Boolean
Function IsCallerInRole(<MarshalAs(UnmanagedType.BStr)> ByVal role As String) As Boolean
End Interface

Visual C++ managed app: Unable to find an entry point named 'Add'

I have followed the tutorial on the following page to create a c++ DLL and I have put it in the System32 folder: http://msdn.microsoft.com/en-us/library/ms235636%28v=vs.80%29.aspx. I am able to run the .exe from anywhere on the PC. Now I want to be able to call Add from a VB.NET application, so I have added the following code:
Imports System.Runtime.InteropServices
Public Class Form1
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Try
Dim Test As Integer
Test = Add(1, 1)
MsgBox(Test)
Catch ex As Exception
End Try
End Sub
<DllImport("MathFuncsDll.dll", EntryPoint:="Add", SetLastError:=True, CharSet:=CharSet.Ansi)> _
Private Shared Function Add(ByVal a As Double, ByVal B As Double) As Double
End Function
End Class
I get the following error: Unable to find an entry point named 'Add' in DLL 'MathFuncsDll.dll. I believe this is because of the namespace. I have researched this and some web pages say namespaces are not allowed with Platform Invoke and some web pages say they are allowed. What is the problem?
The entry point is not named "Add". From the Visual Studio Command Prompt, run dumpbin /exports MathFuncsDll.dll to see the exported names. To get this declaration:
<DllImport("MathFuncsDll.dll", EntryPoint:="?Add#MyMathFuncs#MathFuncs##SANNN#Z", _
CallingConvention:=CallingConvention.Cdecl)> _
Private Shared Function Add(ByVal a As Double, ByVal B As Double) As Double
End Function
The strange looking name is produced by the C++ compiler, a feature called "name decoration". It supports function overloading. You can put extern "C" in front of the function declaration to suppress it. It is actually better if you don't. Also note that SetLastError wasn't correct, the code doesn't actually call SetLastError() to report errors. And CharSet wasn't appropriate, these functions don't take strings.
You'll also need to do something about the Divide function, throwing a C++ exception won't come to a good end in an interop scenario, only C++ code can catch the exception.
Namespaces are not allowed. PInvoke works like plain C client. Don't forget also to declare MathFuncsDll as extern "C" to prevent C++ name mangling. Use Dependency Walker or dumpbin to see list of functions exported from MathFuncsDll.

Why is it not necessary to indicate ByVal/ByRef anymore?

I just installed Visual Studio 2010 Service pack (proposed on Windows Update), and I can see a new feature on the "intellisense" that means when I write a Function or Sub in VB.NET it doesn't auto-complete parameters with ByRef or ByVal...
1) Is there anyway that I can configure this option back to how it was before?
2) If I don't specify ByX, which one is used by default? (it seems like it is always ByRef)
It seems that this post covers your question:
http://msmvps.com/blogs/carlosq/archive/2011/03/15/vs-2010-sp1-changing-quot-byval-quot-vb-net-code-editor-experience.aspx
So no, there is no way to get the old behaviour. From now on ByVal is the default (what it was before) and it won't get added automatically to the method parameters.
In my opinion this is a good decision since it's making VB.NET a bit more consistent with C# and avoids unnecessary "noises"(it's already verbose enough).
Old behaviour:
Private Sub test(ByVal test As String)
End Sub
New behaviour
Private Sub test(test As String)
End Sub
Tim covered what you asked directly, but something else to keep in mind is that any reference type variable, like a user defined class even if passed by value will allow you to make changes to that instances properties etc that stay. It won't however allow you to change the entire object. Which may be why it seemed to you to be defaulting to by reference
Public Sub (Something As WhateverClass)
Something = New WhateverClass 'will result in no changes when outside this method
Something.Property1 = "Test" 'will result in an updated property when outside this method
End Sub
From MSDN:
The value of a reference type is a pointer to the data elsewhere in memory.
This means that when you pass a reference type by value,
the procedure code has a pointer to the underlying element's data,
even though it cannot access the underlying element itself. For
example, if the element is an array variable, the procedure code does
not have access to the variable itself, but it can access the array
members.
Beware when transferring routines to VBA, where the default is ByRef (see, e.g., "The Default Method Of Passing Parameters" at the bottom of this page, by the great Chip Pearson).
That can be messy.