|

Introduction
This whole business started one day when there was a post in the Microsoft
dotnet.languages.vc newsgroup where someone was complaining that he was having
trouble using EnumWindows from Managed C++. He stated very firmly that he did
not want to use the DllImport attribute. This got me interested naturally, and I
thought I could try and help him out. To my utter disappointment I found that I
was having trouble too. The issue was that EnumWindows took as it's first
argument a callback function. All my searches on MSDN and google took me to
solutions that showed how to do this using the DllImport attribute.
The technique suggested was simple. We are to declare a __delegate
object identical to the callback function. Now we are to use DllImport
to define EnumWindows so that it takes as first argument our
__delegate type. Now we can simply write our callback function as a
member of a managed class and pass this function to EnumWindows.
__delegate bool CallBack(IntPtr hwnd, IntPtr lParam);
...
[DllImport("user32")]
extern "C" int EnumWindows(CallBack* x, int y);
...
CallBack* cb = new CallBack(0,
&SomeClass::SomeMatchingMethod);
EnumWindows(cb, 0);
The problem
All this is well and good, but it was beginning to get annoying. My problem was that whatever I did I couldn't get the callback function to
work. Obviously I couldn't pass a delegate directly because when we use IJW, the
native API functions expect native arguments and not managed arguments. I even
tried something as silly as casting a delegate object to a WNDENUMPROC
and as you might have guessed failed thoroughly. I also tried passing both
static and instance members of managed classes as the callback function, but I
kept getting run time exceptions about NULL references and objects.
This was really disappointing to say the least.
That's when I got a huge boost from Richard Grimes who is a Microsoft MVP,
and who has written several quality books on Microsoft programming technologies.
His latest book is on using the managed extensions to program with VC++ .NET. In
reply to my query about calling EnumWindows using IJW, he replied
to me and the reply included a sample code snippet from his latest book, but
unfortunately he used DllImport. I replied back saying that I
wasn't looking for DllImport and I must say my exasperation must
have reflected poorly in my reply. Because Richard's answer was a little crispy too to
begin with. But he gave me my first clue as to why I was going the wrong
direction. He explained to me how managed class members use the __clrcall
calling convention and how unmanaged callback functions use the __stdcall
calling convention In fact when I took a closer look at the compiler
warnings, I was shocked to find a message that said that I was trying to attempt
a redefinition of calling convention from __clrcall to __stdcall
which is not possible and was therefore being ignored. That's when I realized
that I simply had to give up trying to use a managed class member method as my
callback.
The solution
Richard's final answer was an emphatic NO. But I badly wanted to figure out a way by which a managed class can pass a
delegate as the callback function. That's when this idea hit me out of the blue.
Inner classes. We could use inner classes, see! All we had to do was to have an
__gc class with an inner __nogc class and the outside
managed class will wrap the inner unmanaged class and expose it to the outside
world. The outer class has a delegate that acts as the managed callback. The
inner __nogc class has a native __stdcall method as the callback function. This
callback function will invoke the managed delegate each time it gets called.
Thus we simulate a managed callback mechanism here. I have commented the code in
vital areas so that you can understand this better.
__gc class CEnumWindows
{
private:
__nogc class _CEnumWindows
{
private:
static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM)
{
CEnumWindows* pew = CEnumWindows::GetClass();
pew->m_EnumProc->Invoke(hwnd, NULL);
return TRUE;
}
public:
void StartFinding()
{
EnumWindows((WNDENUMPROC)_CEnumWindows::EnumWindowsProc,NULL);
}
};
private:
_CEnumWindows* m_ew;
public:
__delegate bool EnumProc(IntPtr hwnd, IntPtr lParam);
static CEnumWindows* GetClass()
{
return m_pclass;
}
static CEnumWindows* m_pclass=NULL;
CEnumWindows()
{
m_pclass = this;
m_ew = new _CEnumWindows();
}
~CEnumWindows()
{
delete m_ew;
}
void StartFinding()
{
m_ew->StartFinding();
}
EnumProc* m_EnumProc;
};
Now we can use this from any managed class and pass any managed class member
function as the callback function. In the example below, I create a new instance
of CEnumWindows which is the outer class. Then I associate a managed function
from one of my classes to the delegate member of the CEnumWindows object. Alright,
alright,
I know that using a public delegate member is not a proper way to do this, but I
am only trying to demonstrate how this is done. Put this in a property if you
want to, or write a function that'll do this for you.
CEnumWindows* p = new CEnumWindows();
p->m_EnumProc = new CEnumWindows::EnumProc(this,&NForm::EWHandler);
p->StartFinding();
Conclusion
For my own whimsical reasons I am a big fan of using IJW which I feel is a
lot more natural for a C++ programmer than the use of weird looking attributes
that makes your code look like C# or VB .NET. I don't have anything against other
languages but I prefer my C++ code too look like C++ and not like some kind of
ugly mutation of other subjectively inferior languages. Anyway thanks goes to
Richard Grimes for pointing me in the correct direction. Those of you who are
interested in his new book on using the managed extensions can go to this link.
Programming
with Managed Extensions for Microsoft® Visual C++® .NET (Microsoft Press)
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 25 (Total in Forum: 25) (Refresh) | FirstPrevNext |
|
|
 |
|
|
Hi Nishant
Nice trick. I am not able to understand one thing here. The CEnumWindows::GetClass() is a static method which returns the static member m_pclass. And m_pclass is assigned this on every creation of CEnumWindows. So m_pclass will hold the last created instance. Later, _CEnumWindows::EnumWindowsProc uses that instance to invoke the EnumProc delegate. Even though the code is for demonstration purpose, I guess there is some confusion here. Does the code mena the right thing ? Or am I missing something ? Please explain.
Regards Vivek Ragunathan
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
It was all good until I read "other subjectively inferior languages". Why did you spoil a perfectly good article with language bigotry?
What's wrong with "other languages"?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi there!
You are complicating yourself too much, aren't you? why don't you use a mixed mode dll, declare the __gc class and the __nogc (templated) class inside, declare a pointer to the __nogc class inside the __gc one and then pass a pointer to a __gc function as a delegate? this way, you receive in the __nogc class only a function as its delegate and not the whole class!! it's only an idea, i have to try, but it appears to be a working solution, i will tell you!
regards, villalvilla
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I am sorry that I just began code in .NET
But is there anything wrong with DLLImport? Like, performance penalty or anything?
Hei, I don't know!
Thanks for the answer.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
For C#, DllImport is the only way to invoke API.
But for C++/CLI, it provides an easy way to invoke API function directly.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Following on from my previous post (see earlier thread) where I was attempting to use managed events in preference to the delegate / callback mechanism in the article. I wanted to be able to hold a pointer in the unmanaged inner class to the managed outer class in order to invoke the callbacks. The article has CEnumWindows as a singleton and uses a static member GetClass() to return a pointer to the outer object for the same purpose.
The stumbling block is that the compiler won't allow unmanaged classes to contain attributes that are pointers to managed classes.
The way around that is actually fairly simple (although not all that obvious). What is required is a helper template class called gcroot:
gcroot<CEnumWindows*> _pOuter
This wraps the managed pointer and allows it to be used within the unmanaged class (the pointer does not need to be pinned).
The steps to convert the sample (taking my previous example as a base) are:
Include the following to get gcroot<>...
#include <vcclr.h>
Add this attribute to _CEnumWindows...
gcroot<CEnumWindows*> _pOuter;
Initialise this attribute via the constructor for _CEnumWindows...
_CEnumWindows(gcroot<CEnumWindows*> pOuter) { _pOuter = pOuter; }
Remove the GetClass() impliementation from CEnumWindows and create it in the NForm object using new.
Pass the pointer to the inner class as the state parameter of the call to EnumWindows...
::EnumWindows((WNDENUMPROC)_CEnumWindows::EnumWindowsProc, (LPARAM) this);
and cast it back to its original type in the callback...
_CEnumWindows* pMe = (_CEnumWindows*) param;
then can be used to invoke the callback via the delegate as before...
pMe->_pOuter->m_EnumProc->Invoke(hwnd, NULL);
There is no great value in doing this for this particular example except as means to demonstrate the mechanism. However I can see a use for it when wrapping unmanaged APIs where a singleton wrapper is inappropriate.
Full listing below...
#include "stdafx.h"
#include <vcclr.h>
#using <mscorlib.dll> #using <System.dll> #using <System.Drawing.dll> #using <System.Windows.Forms.dll>
#include <tchar.h> #include <windows.h>
using namespace System; using namespace System::ComponentModel; using namespace System::Drawing; using namespace System::Windows::Forms;
__gc class CEnumWindows { private: __nogc class _CEnumWindows { private:
// pointer to outer class to invoke callbacks gcroot<CEnumWindows*> _pOuter;
static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM param) { _CEnumWindows* pMe = (_CEnumWindows*) param;
if (pMe->_pOuter!=NULL) { pMe->_pOuter->m_EnumProc->Invoke(hwnd, NULL); }
return TRUE; }
public:
// Constructor takes pointer to outer class - use gcroot<CEnumWindows*> as // type-safe wrapper to point to managed object _CEnumWindows(gcroot<CEnumWindows*> pOuter) { _pOuter = pOuter; }
void StartFinding() { ::EnumWindows((WNDENUMPROC)_CEnumWindows::EnumWindowsProc, (LPARAM) this); } };
private:
_CEnumWindows* m_ew;
void OnEnumProc(IntPtr hwnd, IntPtr lParam) { __raise this->EnumProc(hwnd, lParam); }
public:
__delegate void EnumProcDelegate(IntPtr hwnd, IntPtr lParam); __event bool EnumProc(IntPtr hwnd, IntPtr lParam);
CEnumWindows() { m_ew = new _CEnumWindows(this); this->m_EnumProc = new CEnumWindows::EnumProcDelegate(this, &CEnumWindows::OnEnumProc); }
~CEnumWindows() { delete m_ew; }
void StartFinding() { m_ew->StartFinding(); }
EnumProcDelegate* m_EnumProc; };
public __gc class NForm : public Form { public: bool EWHandler(IntPtr hwnd, IntPtr lParam) { char buff[512]; if(GetWindowText((HWND)hwnd.ToInt32(),buff,511)) { String* s = String::Format("{0}{1}", __box(hwnd.ToInt32())->ToString("X8")->PadRight(30), Convert::ToString(buff)); lbox->Items->Add(s); } return false; }
NForm() { StartPosition = FormStartPosition::CenterScreen; Text = "Callbacks with IJW - Nish for CodeProject - Avoid DllImport"; Size = Drawing::Size(750,550); FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog; MaximizeBox = false;
lbox = new ListBox(); lbox->Location=Point(5,5); lbox->Size=Drawing::Size(730,500); lbox->Sorted = true;
Controls->Add(lbox);
CEnumWindows* p = new CEnumWindows(); __hook(&CEnumWindows::EnumProc, p, &NForm::EWHandler);
p->StartFinding(); } ListBox* lbox;
};
int __stdcall WinMain(HINSTANCE,HINSTANCE,LPSTR,int) { Application::Run(new NForm()); return 0; }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I found this article when I was looking to design a IJW wrapper for a legacy C++ API. In particular I wanted to understand how I could include events within my wrapper that could be easily consumed by the APIs client (a C# application).
I have tried to update this example to replace the delegate / callback mechanism with an event.
Initially I created the event in CEnumWindows as follows:
__event bool EnumProc(IntPtr hwnd, IntPtr lParam);
I then replaced the class to instatiate the delegate in NForm with a __hook call as follows:
//p->m_EnumProc = new CEnumWindows::EnumProc(this,&NForm::EWHandler);
__hook(&CEnumWindows::EnumProc, p, &NForm::EWHandler);
I then replaced the call to invoke the delegate pointer:
pew->m_EnumProc->Invoke(hwnd, NULL);
with call to raise the event:
__raise pew->EnumProc(hwnd, NULL);
This proved to be a false start. The sample compiled OK but failed to run. The call to EnumWindows((WNDENUMPROC)_CEnumWindows::EnumWindowsProc,NULL); threw the exception System.MethodAccessException. I guess because the framework does not like managed events to be raised within an unmanaged call.
So...
I then modified the sample again to put the delegated callback back in. This time it is contained in the class CEnumWindows using the procedure:
void OnEnumProc(IntPtr hwnd, IntPtr lParam) { __raise this->EnumProc(hwnd, lParam); }
This procedure raises the managed event when called and all is well. It is unfortunate that this extra level of indirection is required but is not too onerous. If anybody can see a simpler way of doing this then I'd be glad of the advice.
Full listing below...
#include "stdafx.h"
#using #using #using #using
#include
#include
using namespace System; using namespace System::ComponentModel; using namespace System::Drawing; using namespace System::Windows::Forms;
__gc class CEnumWindows { private: __nogc class _CEnumWindows { private:
static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM) { CEnumWindows* pew = CEnumWindows::GetClass(); pew->m_EnumProc->Invoke(hwnd, NULL); return TRUE; }
public:
void StartFinding() { ::EnumWindows((WNDENUMPROC)_CEnumWindows::EnumWindowsProc,NULL); } };
private:
_CEnumWindows* m_ew;
void OnEnumProc(IntPtr hwnd, IntPtr lParam) { __raise this->EnumProc(hwnd, lParam); }
public:
__delegate void EnumProcDelegate(IntPtr hwnd, IntPtr lParam); __event bool EnumProc(IntPtr hwnd, IntPtr lParam);
static CEnumWindows* GetClass() { if (m_pclass==NULL) { m_pclass = new CEnumWindows(); }
return m_pclass; }
static CEnumWindows* m_pclass = NULL;
CEnumWindows() { m_ew = new _CEnumWindows(); this->m_EnumProc = new CEnumWindows::EnumProcDelegate(this, &CEnumWindows::OnEnumProc); }
~CEnumWindows() { delete m_ew; }
void StartFinding() { m_ew->StartFinding(); }
EnumProcDelegate* m_EnumProc; };
public __gc class NForm : public Form { public: bool EWHandler(IntPtr hwnd, IntPtr lParam) { char buff[512]; if(GetWindowText((HWND)hwnd.ToInt32(),buff,511)) { String* s = String::Format("{0}{1}", __box(hwnd.ToInt32())->ToString("X8")->PadRight(30), Convert::ToString(buff)); lbox->Items->Add(s); } return false; }
NForm() { StartPosition = FormStartPosition::CenterScreen; Text = "Callbacks with IJW - Nish for CodeProject - Avoid DllImport"; Size = Drawing::Size(750,550); FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog; MaximizeBox = false;
lbox = new ListBox(); lbox->Location=Point(5,5); lbox->Size=Drawing::Size(730,500); lbox->Sorted = true;
Controls->Add(lbox);
CEnumWindows* p = CEnumWindows::GetClass(); __hook(&CEnumWindows::EnumProc, p, &NForm::EWHandler);
p->StartFinding(); } ListBox* lbox;
};
int __stdcall WinMain(HINSTANCE,HINSTANCE,LPSTR,int) { Application::Run(new NForm()); return 0; }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi, I am having some linkage issues. Here is the code, very similar to the example:
__nogc class _EthernetIPCommunication { private: // the callback method that will be invoked from the // ethernetIP dll static BOOL CALLBACK EthernetIPCallback( INT32 nEvent, INT32 nParam) { EthernetIPCommunication* p_EthernetIPCommunication = EthernetIPCommunication::GetClass(); p_EthernetIPCommunication->EthernetIPManagedCallback(nEvent,nParam); return TRUE; }
public: // the method that will register the callback void RegisterCallback() { // this method will register the internal static callback method with the eml library so that // there is a non-managed way to get at and handle callbacks LogEventCallbackType* callback = (LogEventCallbackType*)&_EthernetIPCommunication::EthernetIPCallback; EtIPRegisterEventCallBack( callback ); } };
And then i construct an instance as follows:
_EthernetIPCommunication* eip = new _EthernetIPCommunication; eip->RegisterCallback();
Then i get this link error:
LNK2001: unresolved external symbol "void * __cdecl operator new(unsigned int)" (??2@$$FYAPAXI@Z)
Any ideas?
Mark
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Fixed it:
See ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.1033/vcmex/html/vcconConvertingManagedExtensionsForCProjectsFromPureIntermediateLanguageToMixedMode.htm
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
External names are mangled. Probably your program is in C++, not in C.
If your program is in C, you should change settings in ide to tell the compiler about that, or use the extension '.c' rather than '.cpp'.
If your program is in C++, you should use the following construction:
extern "C" { #include "glpk.h" }
because all glpk headers are written in C, not in C++.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
hi Nish,
thanks in advance for your cool articles.
assume i want to use some native function like: WSAASyncGetServByName(hwnd,msg,"",buf,len);
my interface windows are all managed .net . but this function require me to pass a pointer to windows by hwnd .
what is the solution here .
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Please, clarify for me: What exactly you mean and what is the value of this statement: That's when this idea hit me out of the blue. Inner classes. We could use inner classes, see! All we had to do was to have an __gc class with an inner __nogc class... Does it have to be inner class? For example small change and your example still works:
__nogc class _CEnumWindows { private: static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM); public: void StartFinding() { EnumWindows((WNDENUMPROC)_CEnumWindows::EnumWindowsProc,NULL); } };
__gc class CEnumWindows { private: private: _CEnumWindows* m_ew; public: __delegate bool EnumProc(IntPtr hwnd, IntPtr lParam); static CEnumWindows* GetClass() { return m_pclass; } static CEnumWindows* m_pclass=NULL; CEnumWindows() { m_pclass = this; m_ew = new _CEnumWindows(); } ~CEnumWindows() { delete m_ew; } void StartFinding() { m_ew->StartFinding(); } EnumProc* m_EnumProc; };
BOOL _CEnumWindows::EnumWindowsProc(HWND hwnd, LPARAM) { CEnumWindows* pew = CEnumWindows::GetClass(); pew->m_EnumProc->Invoke(hwnd, NULL); return TRUE; }
As you can see _CEnumWindows is not inner class anymore.
So, what is the value of your experiment?...
"...Ability to type is not enough to become a Programmer. Unless you type in VB. But then again you have to type really fast..." Me
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
Terrific article !! Thank you very much !! I've been tearing my hair out trying to figure out how to do this without DllImport.
Now, could you explain how to invoke the delegate asynchronously (with BeginInvoke etc.) using this pattern ? I have Ansi C++ libraries which constitute the "Worker Threads" of my application, and I want to be able to callback from them asynchronously to my C# GUI controls.
Using your pattern, I have finally been able to get a synchronous callback working. What do I need to do to make it work asynchronously ?
Thanks !!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi Yoni, I am wondering if you found a solution how to make asynchronous callbacks from Unmanaged C++ libraries to C# GUI controls. I am researching that for a awhile, and still could not find the right way of doing that.
Thanks, Eugene
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Eugene -
I was able to get the method described in Nishant's article to to work with asynchronous delegates too, using BeginInvoke() and EndInvoke() etc.
However, in the final analysis, I decided that this method is too "tricky", and I ditched the whole idea altogether. Instead, my unmanaged C++ code just sends regular Windows messages to the C# GUI Controls, which processes them using WndProc().
Good luck !!
Yoni
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
Right way is to use Marshal.GetUnmanagedThunkForManagedMethodPtr.
[EDIT] Found the right way to use from Commandprompt explorerbar article
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
|
Hi Daniel
Daniel Lohmann wrote: However, we don't have to use delegates under all circumstances, just because they are there
Heheh. Just wanted to do it the managed way, that's all
Daniel Lohmann wrote: It might also be a good idea to use templates to generalize your technique, so it is not necessary to write the same code again and again for EnumFonts(), EnumDesktops(),
Thanks, that's a very good idea
Regards, Nish
Author of the romantic comedy
Summer Love and Some more Cricket [New Win]
Review by Shog9 Click here for review[NW]
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
In this case the adapter should be an unmanaged class ( __nogc ) while a member function to call is a in managed class ( __gc ). But you cannot declare a pointer to managed class A in unmanaged class, thus the [ A* m_pObj ] in adaptor will not work if A is a managed class. Casting to managed class pointer is not working either. So, some other technique is needed for managed-unmanged callbacks to get the pointer to managed class instance. The one is shown in this article is to use static member that would hold the pointer to one [last created] instance.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I thought only underscores where ugly in MC++  Maybe you can write another article or update this one to be thread safe. Keep it up.
regards Kannan
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Only one thread can call EnumWindows in your case. DllImport requires less lines of code and is also thread safe.
You should use TLS or Context class to store m_pClass variable.
Step back, rub your eyes, take a deep breath, stretch a bit, and reflect on the relative importance of CP, CG, the age / travel time sustained by supposedly 'fresh' cheese curds, and Life in General. - Shog
|
| | | | | |