|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Introduction3 years ago, I had written an article titled
Implementing Callback functions using IJW (avoiding DllImport) that
described a technique by which you could call API functions requiring callbacks
without using the DllImport mechanism. The technique I used involved declaring
an Sample usagedelegate BOOL EnumWindowsDelegateProc(HWND hwnd,LPARAM lParam);
I've declared the delegate type that'll be exposed to the .NET world (or at
least to the C++ .NET world given that I've used some native types). The
delegate type matches the callback prototype for the ref class WindowEnumerator
{
private:
EnumWindowsDelegateProc^ _WindowFound;
public:
WindowEnumerator()
{
_WindowFound = nullptr;
}
event EnumWindowsDelegateProc^ WindowFound
{
public:
void add(EnumWindowsDelegateProc^ d)
{
if(_WindowFound == nullptr)
_WindowFound = d;
}
void remove(EnumWindowsDelegateProc^)
{
_WindowFound = nullptr;
}
}
void Init()
{
pin_ptr<EnumWindowsDelegateProc^> tmp = &_WindowFound;
EnumWindows((WNDENUMPROC)Marshal::GetFunctionPointerForDelegate(
_WindowFound).ToPointer(), 0);
}
};
I have a non-trivial event ( delegate int DispProc(String^, String^);
Here, I've declared my second delegate (which I'll use to wrap the ref class MyClass
{
public:
static BOOL HandleFoundWindow(HWND hwnd,LPARAM lParam)
{
char buff[512];
GetWindowText(hwnd, buff, 511);
if(IsWindowVisible(hwnd) && pDispProc && strlen(buff))
pDispProc("%s\r\n",gcnew String(buff));
return TRUE;
}
static DispProc^ pDispProc = nullptr;
};
This class simply defines a int main(array<System::String ^> ^args)
{
WindowEnumerator wenum;
EnumWindowsDelegateProc^ d1 = gcnew EnumWindowsDelegateProc(
MyClass::HandleFoundWindow);
wenum.WindowFound += d1;
HMODULE h1 = LoadLibrary("msvcrt.dll");
if(h1)
{
typedef int (*FUNC_PTR)(const char *, ...);
FUNC_PTR pfn = reinterpret_cast<FUNC_PTR>(GetProcAddress(h1, "printf"));
if(pfn)
{
DispProc^ d2 = (DispProc^)Marshal::GetDelegateForFunctionPointer(
(IntPtr)pfn,DispProc::typeid);
MyClass::pDispProc = d2;
wenum.Init();
}
FreeLibrary(h1);
}
return 0;
}
I get a pointer to the Performance improvementI did a little speed test to see which is more performant. I wrote two
classes - both of them enumerating windows using the Here are the two classes :- delegate int DEnumWindowsProc(IntPtr hWnd, IntPtr lParam);
ref class Base abstract
{
public:
static int EnumWindowsProc(IntPtr hWnd, IntPtr lParam)
{
return TRUE;
}
virtual void Init() abstract;
};
ref class PIClass : Base
{
public:
[DllImport("user32.dll")]
static bool EnumWindows(DEnumWindowsProc^ lpEnumFunc,
IntPtr lParam);
virtual void Init() override
{
EnumWindows(gcnew DEnumWindowsProc(EnumWindowsProc),
IntPtr::Zero);
}
};
ref class FuncPtrClass : Base
{
public:
static DEnumWindowsProc^ dg = gcnew DEnumWindowsProc(EnumWindowsProc);
virtual void Init() override
{
pin_ptr<DEnumWindowsProc^> tmp = &dg;
::EnumWindows((WNDENUMPROC)
Marshal::GetFunctionPointerForDelegate(dg).ToPointer(), 0);
}
};
And here's my test code :- generic<typename T> where T : Base int CalcSpeed(int count)
{
Console::WriteLine(
"Checking class : {0} with {1} iterations", T::typeid, count);
T t = Activator::CreateInstance<T>();
DWORD start = GetTickCount();
while(count--)
t->Init();
DWORD stop = GetTickCount();
Console::WriteLine("{0} milliseconds", stop - start);
return stop - start;
}
void DoCalc(int count)
{
int t1 = CalcSpeed<PIClass^>(count);
int t2 = CalcSpeed<FuncPtrClass^>(count);
float pc = (float)(t1-t2)/t1 * 100;
int pcrounded = (int)pc;
Console::WriteLine(
"{0}% improvement for {1} iterations\r\n", pc, count);
}
int main(array<System::String ^> ^args)
{
DoCalc(10000);
DoCalc(50000);
DoCalc(100000);
DoCalc(500000);
return 0;
}
Here's the output I got (you'll get slightly varying results obviously) :- History
|
||||||||||||||||||||||