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

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)();

Related

Use native function pointer for listening to managed event / marshaling issue

I'm writing a mixed mode C++/CLI assembly bridge in order to be able to call into my .NET class library from old C++ application.
In one of my classes in the .NET library one can attach to an event whenever some message needs to be displayed (to console or whatever depending on calling application).
class NetApi
{
public event EventHandler<MessageEventArgs> MessageReported;
}
To call this from native C++ application, I defined the following pointer/delegate bridge:
typedef void(*MessageHandler)(const char* msg);
delegate void ManagedMessageHandler([MarshalAs(UnmanagedType::LPStr)] String^ msg);
Omitting from glue for connecting everything (attaching to MessageReported, removing sender from EventHandler, etc...), here is how I create managed delegate from native function pointer:
class NetApiBridge
{
public:
void SetMessageHandler(MessageHandler handler)
{
wrappedListener = (ManagedMessageHandler^)Marshal::GetDelegateForFunctionPointer((IntPtr)handler, ManagedMessageHandler::typeid);
}
private:
msclr::auto_gcroot<NetApi^ > wrappedApi;
msclr::auto_gcroot<ManagedMessageHandler^ > wrappedListener;
// In another helper ref class in fact, but here pseudo code to simplify
void onMessageReported(Object^ sender, MessageEventArgs^ e)
{
if (!wrappedListener) { return; }
wrappedListenter(e->Message); // Send message to native function pointer
}
}
And I'm almost there when creating dummy C++ test code:
void messageHandler(const char* s)
{
cout << s;
}
void main()
{
NetApiBridge api = new NetApiBridge();
api->SetMessageHandler(&messageHandler);
api->Measure();
delete api;
}
Everything goes fine, events are reported correctly except .... except I receive a PInvokeStackImbalance from Managed Debugging Assistant when leaving the native handler and I clearly don't know why ?
What's wrong with marshaling const char* as UnmanagedType::LPStr here with GetDelegateForFunctionPointer ?
NB: C++ bridge is compiled in x86 if it is important to know here.
typedef void(*MessageHandler)(const char* msg);
delegate void ManagedMessageHandler([MarshalAs(UnmanagedType::LPStr)] String^ msg);
Your delegate declaration is not compatible with the function pointer declaration in 32-bit code. The default calling convention in native code is almost always __cdecl. The default for delegates is __stdcall. A somewhat quirky choice but inspired because interop was assumed to be useful to make OS calls, Windows and COM use __stdcall.
The mismatch right now causes the delegate stub to pop the arguments off the stack. So does the native code so the stack gets imbalanced by 4 bytes. The MDA is there to help you diagnose this common mishap.
You'll have to help and get them to agree. Either with the delegate declaration:
using namespace System::Runtime::InteropServices;
...
[UnmanagedFunctionPointer(CallingConvention::Cdecl)]
delegate void ManagedMessageHandler(String^ msg);
Or the function pointer declaration:
typedef void (__stdcall * MessageHandler)(const char* msg);

Function from a referenced library which has function pointers as parameters

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

Why do libgit2 methods using kernel32.dll's GetProcAddress always return 0?

I have the need to manually handle the loading / unloading of the actual git2.dll, instead of using [DllImport("git2")] in C#. I seem to have issues with creating an IntPtr for reference to the address of methods stored in libgit2.
Here're the good bits from my PluginManager class which are supposed to help facilitate manually loading, marshal/delegate (whenever I get this kink fixed), and unloading libraries.
public class PluginManager {
public const string LIB = "Assets\\Plugins\\git2.dll";
[DllImport( "kernel32.dll", CharSet = CharSet.Ansi )]
public static extern IntPtr LoadLibrary( [In, MarshalAs( UnmanagedType.LPStr )] string lib );
[DllImport( "kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true )]
public static extern IntPtr GetProcAddress( [In] IntPtr reference, [In, MarshalAs( UnmanagedType.LPStr )] string method );
[DllImport( "kernel32.dll" )]
public static extern bool FreeLibrary( [In] IntPtr reference );
}
Here's where I attempt to use them:
//# Working; always non-zero.
IntPtr reference = PluginManager.LoadLibrary( PluginManager.LIB );
//# Should be valid -- LibGit2Sharp.Core.NativeMethods.cs calls this method, too.
//# Always returns IntPtr.Zero.
IntPtr methodReference = PluginManager.GetProcAddress( reference, "git_repository_open" );
Is the library not exposed to this type of loading? I've tried all kinds of methods present in the LibGit2Sharp native hooks, but they always return zero.
It seems that git2.dll has mangled export names, so git_repository_open is actually _git_repository_open#4. After researching some more on why this is happening, it seems that, according to This Post, and several others that I've run into while looking for a fix, that the functions may not be exported using extern "C" or may not be using a .def file.
I did look through some of the libgit2 code, and I saw extern used on a couple functions, but on others I saw no export keyword at all, but they're still being accessed externally from LigGit2Sharp's NativeMethods.cs class.
While all of that is foreign to me, I did find a very useful post about a tool shipped with Visual Studio that allows you to see all exported items, and their names: Dumpbin. With this, I was able to determine the exported names, and used the /OUT:filename flag to save them to a file: PasteBin
With all of the exported names at my disposal, I just have to follow function calls, that I'm actually using, and replace the the methods in NativeMethods.cs to look something like this: LibGit2Sharp Issue #341.

Unable to find an entry point

I import two WinApi functions and use them in my class
using namespace System::Runtime::InteropServices;
[DllImport("user32",ExactSpelling = true)]
extern bool CloseWindow(int hwnd);
[DllImport("user32",ExactSpelling = true)]
extern int FindWindow(String^ classname,String^ windowname);
public ref class WinApiInvoke
{
public:
bool CloseWindowCall(String^ title)
{
int pointer = FindWindow(nullptr,title);
return CloseWindow(pointer);
}
};
Then I create object in main program and call CloseWindowCall method
Console::WriteLine("Window's title");
String ^s = Console::ReadLine();
WinApiInvoke^ obj = gcnew WinApiInvoke();
if (obj->CloseWindowCall(s))
Console::WriteLine("Window successfully closed!");
else Console::WriteLine("Some error occured!");
When I write in console for example Chess Titans to close I've got an error
Unable to find an entry point named 'FindWindow' in DLL 'user32'
What entry point?
You are using the ExactSpelling property inappropriately. There is no FindWindow function in user32.dll, just like the exception message says. There is FindWindowA and FindWindowW. The first one handles legacy 8-bit character strings, the second uses Unicode strings. Any Windows api function that accepts strings has two versions, you don't see this in C or C++ code because the UNICODE macro selects between the two.
Avoid ExactSpelling on winapi functions, the pinvoke marshaller knows how to deal with this. You have some other mistakes, the proper declaration is:
[DllImport("user32.dll", CharSet = CharSet::Auto, SetLastError = true)]
static IntPtr FindWindow(String^ classname, String^ windowname);

C++/CLI Interop: COM function always returns null IntPtr

I have C++/CLI module which uses COM library. COM library function returns pointer to the object. Signature of the COM function call in C++/CLI is like this
IntPtr p = myCOMObject->GetSettings();
In COM interface same function has the following singnature
virtual HRESULT STDMETHODCALLTYPE GetSettings(
/* [retval][out] */ void *CSettings) = 0;
argument object *CSettings is native C++ object of some other class created in side GetSettings function.
When call this function in C++/CLI. I always Get IntPtr as '0' and no value set to it. I checked with pure C++ code, I get proper object. what could be the reason for this.Am i doing somthing wrong please suggest.