Using the VSPrinter Control > Visual C++ Topics > Dual Interfaces in MFC |
The wrapper classes created by the MFCwizard are very helpful, and for a while they were the best you could get. With Visual Studio6, however, the compiler has built-in COMsupport, including a different way to create wrapper classes for ActiveXobjects through the new #import compiler directive. These "native" wrapper classes are faster and more flexible than the ones generated by MFC:
The MFC wrappers are based on the IDispatch interface, so every method or property you access needs to pack its parameters into VARIANTS and go through a call to the Invoke method. The native wrappers, by contrast, take advantage of dual interfaces to access properties and methods via direct calls, which is much faster.
The native wrappers are more complete and configurable. They include object-defined enumerations, default values for optional parameters, and an optional VB-like syntax for accessing properties. These new features allow you to write
Example Title |
Copy Code
|
---|---|
m_spPrinter->NavBar = vpnbTopPrint; m_spPrinter->DrawLine(1440L, 1440L); |
instead of
Example Title |
Copy Code
|
---|---|
m_Printer.SetNavBar(3); COleVariant vNone(0L, VT_ERROR); COleVariant v1in(1440L); m_Printer.DrawLine(v1in, v1in, vNone, vNone); |
The native wrappers are based on COMsupport classes that make the handling of strings, variants and COM pointers less error-prone.
Taking advantage of dual interfaces in existing MFCprojects is easy. All you have to do is include the appropriate #import statement in your StdAfx.h file, then create a pointer and assign it to the existing control. For example, assuming you have an MFC-based m_Printer control, all the extra code you would need would be this:
Example Title |
Copy Code
|
---|---|
// include this statement in the StdAfx.h file #import "c:\windows\system\vsprint8.ocx" no_namespace |
Then, instead of using the control in the usual way, declare a variable of type IVSPrinterPtr, initialize it by setting it to m_Printer.GetControlUnknown(), and use it instead of m_Printer. The two routines listed below illustrate the difference between the two approaches (both routines create a table with 10,000 rows and report how long it took to do it):
Example Title |
Copy Code
|
---|---|
// Using the MFC-generated wrapper class void CMyDlg::BenchDispatchClick() { // keep track of the start time DWORD tStart = GetTickCount(); // generate the document COleVariant vNone(0L, VT_ERROR); m_Printer.StartDoc(); m_Printer.StartTable(); m_Printer.AddTable("1000|2000|2000", "Col1|Col2|Col3", "", vNone, vNone, vNone); m_Printer.SetTableCell(1 /*tcRows*/, vNone, vNone, vNone, vNone, COleVariant(1000L)); for (long r = 1; r <= 10000; r++) { for (long c = 1; c <= 3; c++) { CString str; str.Format("R%d C%d", r, c); m_Printer.SetTableCell(18 /*tcText*/, COleVariant(r), COleVariant(c), vNone, vNone, COleVariant(str)); } } m_Printer.EndTable(); m_Printer.EndDoc(); // report how long it took DWORD tElapsed = GetTickCount() - tStart; CString str; str.Format("Done in %d seconds using dual interface.", (int)(tElapsed / 1000)); MessageBox(str); } // Using the native wrapper class (#import-based) void CMyDlg::BenchDualClick() { // keep track of the start time DWORD tStart = GetTickCount(); // get VTBL (dual) interface IVSPrinterPtr spPrinter = m_Printer.GetControlUnknown(); // generate the document spPrinter->StartDoc(); spPrinter->StartTable(); spPrinter->AddTable("1000|2000|2000", "Col1|Col2|Col3", ""); spPrinter->PutTableCell(tcRows, vtMissing, vtMissing, vtMissing, vtMissing, 1000L); for (long r = 1; r <= 10000; r++) { for (long c = 1; c <= 3; c++) { CString str; str.Format("R%d C%d", r, c); spPrinter->PutTableCell(tcText, r, c, vtMissing, vtMissing, (LPCTSTR)str); } } spPrinter->EndTable(); spPrinter->EndDoc(); // report how long it took DWORD tElapsed = GetTickCount() - tStart; CString str; str.Format("Done in %d seconds using dual interface.", (int)(tElapsed / 1000)); MessageBox(str); } |
The code looks very similar, except for the dot notation used with the m_Printer variable and the arrow used with the spPrinter variable. The difference is in execution speed. The MFC/Dispatch version takes 8 seconds to create the table, while the native/dual version takes only 6 seconds. The dual version is 25% faster than the traditional MFC/Dispatch version.
In functions that only set a few properties, it probably doesn't matter much which type of wrapper class you choose. But in functions with lengthy for statements, that access properties or methods several hundred times, you should definitely consider using the #import statement and the dual interface.