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

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.

Related

vb.net: How to Structure to IntPtr

I have a vb6.0 (basic) project which I want to migrate to the new vs2022.
Most of the code just translated "more or less" fine, but there are some open points. I tried checking internet, but I couldnt find solution.
So here is my issue:
I have following code:
Public Structure Test
Dim a as long
Dim b as long
....
End structure
Public Sub xyz()
'here im filling Test structure locally
a = test...
..
A(a)
End sub
external dll function I want to call is A([In] IntPtr Data)
so the calling of A with my Test structure is failing, in vb6.0 it was working fine. I am not sure whats the problem. I tried some things, but I have no clue
I expected that it can just compile like its comiling in vb6.0. I have not touched any of this code part yet.
Another thing is that VarPtr() function is not available anymore. What could be the replacement in vs2022?
When I need to pass a structure to an unmanaged API as a pointer Intptr, I would do the following:
Allocate unmanaged memory of the size of the structure. Dim buffer As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(T)))
Pass the structure pointer to the unmanaged API.
When the unmanaged methods returns, converts the pointer to the structure using: Marshal.PtrToStructure(Of T)(buffer)
Do not forget to free the previously allocated unmanaged memory
Marshal.FreeHGlobal(buffer)

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.

VBA: Only user-defined types defined in public object modules can be coerced to or from a variant or passed to a late-bound functions

Compile Error:
Compile Error: Only user-defined types defined in public object
modules can be coerced to or from a variant or passed to a late-bound
functions.
I'm new to VBA and I was tasked with debugging some code for a custom screen in Dynamics SL. The first thing I did was to see if it compiled and I got the above message.
When I read the built-in help reference I found the following for the above error:
You attempted to use a public user defined type as a parameter or
return type for a public procedure of a class module, or as a field of
a public user defined type. Only public user defined types that are
defined in a public object module can be used in this manner.
I also went through these similar questions:
How to put user defined datatype into a Dictionary
Only user-defined type defined in public object modules can be coerced when trying to call an external VBA function
They have the same error but I don't see a collection object that the above two questions focused on.
If you may have any idea what may be causing this error please don't hesitate to suggest it.
Code:
Private Sub cpjt_entity_Chk(ChkStrg As String, retval As Integer)
Dim ldDate As Sdate
Dim xStrDailyPost As Sdate
ldDate.val = GetObjectValue("cpe_date")
'xStrDailyPost = DateToStr(ldDate)
'Call MsgBox("Daily Post Date: " & xStrDailyPost, vbOKOnly, "TEST")
serr1 = SetObjectValue("cld_id08", xStrDailyPost) <- Error highlights "xStrDailyPost"
End Sub
Definition for SetObjectValue:
Declare Function SetObjectValue Lib "swimapi.dll" Alias "VBA_SetObjectValue" (ByVal ctlname$, newval As Variant) As Integer
Thank you in advance!
You are probably working with code that was originally written with the Dynamics SL (actually it was Solomon IV at the time) Basic Script Language (BSL) macro language instead of VBA.
Regardless... the fix is, pass results of the "val" method of your xStrDailyPost instance of SDate. SO the code should look like:
serr1 = SetObjectValue("cld_id08", xStrDailyPost.val)
I've not actually tested this but I'm pretty sure this will address your issue.
If you want a little more background, "Sdate" is really just a very thin wrapper of an integer (actually I think it's a short, but I've never found I really needed to know for sure). the "Val" method returns the underlying integer in the SDate variable.

Stack Imbalance

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.

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