Click to See Complete Forum and Search --> : Legacy DLLs and Callbacks
Woogoo
January 22nd, 2009, 03:34 AM
Hi,
I assume this has been asked before but I can't find an answer that works for me. My application is written in C++/CLI and I'm using a legacy C DLL via a Type Lib. I need to pass the address of a C++/CLI Callback Function to the Legacy DLL but nothing I've read works.
I assume this can be done but I can't work it out for the life of me. Can anyone help me here please?
--
Woogoo
darwen
January 22nd, 2009, 04:43 AM
Can you post the interface definition from the type library ?
You can get this by opening the COM dll in OLEView.exe (found in C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\Bin\OLEView.exe).
I need to see
(a) The callback function declaration.
(b) The declaration of the method you're trying to pass the callback into.
(c) The type of the interface.
Darwen.
Woogoo
January 22nd, 2009, 06:34 AM
Hi, first things first I got it wrong I'm NOT using a typelib that was my mistake I am simply including a DLL within my search path.
But to answer two of the questions:
(a) int LSsetCallbackSolverLng(pLSenvLINGO pL, lngCBFuncError_t pcbf, void* pUserData) ;
(b) int __clrcall FormMain::SolverCallback(void* pModel, int nReserved, void* pUserData) {...}
When b is declared as :
int __stdcall FormMain::SolverCallback(void* pModel, int nReserved, void* pUserData)
I get the following complier warning:
C4441: calling convention of '__cdecl ' ignored; '__clrcall ' used instead Matrix.cpp 114
and when either __stdcall or __clrcall I get
error C2664: 'LSsetCallbackSolverLng' : cannot convert parameter 2 from 'Matrix::FormMain::CallBackDelegate ^' to 'lngCBFunc_t' Matrix.cpp 79
This is line 79
ProcessErrorCode(LSsetCallbackSolverLng(pLingo, MyDelegate, &dBestObjective)) ;
And MyDelegate is defined:
CallBackDelegate^ MyDelegate = gcnew CallBackDelegate(this, &FormMain::SolverCallback) ;
I have tried all manner of incantations to get this to work but for the life of me I just can't do it.
Does this make any sense to you?
darwen
January 22nd, 2009, 06:43 AM
Try not passing a delegate : try passing the function pointer directly. e.g.
// not in any class : just an ordinary 'C' style method
int __stdcall MyCallback(void* pModel, int nReserved, void* pUserData)
{
return 0;
}
// ...
LSsetCallbackSolverLng(pLingo, &MyCallback, &dBestObjective);
You can do this using delegates, but you'd have to define the necessary method first in managed code using p/invoke i.e. with DllImport attribute.
Darwen.
Woogoo
January 22nd, 2009, 07:12 AM
I had already tried a variation of that but since the Solver is on a thread there are callbacks in there to update the interface as the solution progresses, so the compiler *****es.
When you said "You can do this using delegates, but you'd have to define the necessary method first in managed code using p/invoke i.e. with DllImport attribute."
Do you have an example or a point of reference I can look at?
Thanks.
Alex F
January 22nd, 2009, 08:35 AM
Use Marshal.GetFunctionPointerForDelegate Method. Apply it to managed callback function. It returns IntPtr, which can be converter to void* using ToPointer method. Pass this value to the Set Callback function.
Alex F
January 22nd, 2009, 08:38 AM
#include "stdafx.h"
#include <windows.h>
using namespace System;
using namespace System::Runtime::InteropServices;
delegate int HandleWindowDelegate(IntPtr handle, IntPtr lParam);
ref class TestClass
{
public:
TestClass()
{
}
void EnumWindows()
{
HandleWindowDelegate^ d = gcnew HandleWindowDelegate(this, &TestClass::HandleWindow);
::EnumWindows(
(WNDENUMPROC)Marshal::GetFunctionPointerForDelegate(d).ToPointer(),
0);
}
int HandleWindow(IntPtr handle, IntPtr lParam)
{
Console::WriteLine(handle.ToInt32().ToString(L"X"));
return 1;
}
};
int main(array<System::String ^> ^args)
{
TestClass^ t = gcnew TestClass();
t->EnumWindows();
return 0;
}
Woogoo
January 22nd, 2009, 11:20 AM
Hi, with the addition of Marshal::... I now have:
CallBackDelegate^ MyDelegate = gcnew CallBackDelegate(this, &FormMain::SolverCallback) ;
if (pLingo != 0)
{
ProcessErrorCode(LSsetCallbackSolverLng(pLingo, Marshal::GetFunctionPointerForDelegate(MyDelegate).ToPointer(), &dBestObjective)) ;
...
}
But I still get the C2664 Compiler Error! With the declaration of the Callback being:
int __clrcall FormMain::SolverCallback(void* pModel, int nReserved, void* pUserData);
It's evident by now that I'm no expert on this which makes me think there may be a touch of WoMBat in this application. This application contains NO DllImport statements and the reference to the typelib has been remove. So how does the IntelliSense still work!
So I guess it must be working by accident as I can't see what I'm doing wrong, and my head's starting to hurt!
darwen
January 22nd, 2009, 02:53 PM
Intellisense will be picking up the function types from the .h file for the dll.
Try this :
ProcessErrorCode(
LSsetCallbackSolverLng(
pLingo,
(lngCBFuncError_t)Marshal::GetFunctionPointerForDelegate(MyDelegate).ToPointer(),
&dBestObjective)) ;
And PLEASE USE CODE TAGS as we all have done when responding. It makes code much easier to read.
Also, what is CallBackDelegate defined as ? It should be
delegate void CallbackDelegate(IntPtr pModel, int nReserved, IntPtr pUserData);
Darwen.
buddhikasgj
January 22nd, 2009, 03:08 PM
Im having s similar kind of senario. I want to write a C++ DLL and consume it from C#. what i want to do is pass a call back function with one string parameter to the dll and call it from the DLL, For the purposes of providing a status during long operation done at the DLL. I googled mybest to find the way but i couldnt find a simple guid to do that. So please anyone can help me with simple code sample of how to implement the C++ function with a function pointer argument, how to call the function pointed by the funtion pointer from the DLL and how to call the DLL function from the C# code. Any sort of help would be appriciated. thanks
codeguru.com
Copyright WebMediaBrands Inc., All Rights Reserved.