Third, pure manual creation a COM component 1. From the construction of the project to implement the registration in this process we will complete the three steps: Create a DLL entry function, define the interface file, and implement a registration function 1.1 Create a type of Win32 DLL project creation A Win32 DLL project called Mathcom. Select the "A SMIPLE DLL Project" option in the second step of the wizard. Of course, if you choose an empty project, you will complete the DLLMAIN definition yourself. 1.2 Defining Interface files Generate an interface file called Mathcom.IDL. And add this file to the project you just created.
//Mathcom.idl file
// mathcom.idl: idl source for mathcom.dll
//
// this file will be processed by the Mid Tool To
// produted the Type library (Mathcom.tlb) and Marshalling Code.
Import "OAIDL.IDL";
Import "OCIDL.IDL";
[
UUID (FAEAE6B7-67BE-42A4-A318-3256781E945A),
Helpstring ("Isimplemath Interface",
Object,
Pointer_DEFAULT (UNIQUE)
]
Interface Isimplemath: IUNKNOWN
{
HRESULT ADD ([in] int NOP1, [IN] int NOP2, [OUT, RETVAL] INT * PRET);
HRESULT SUBTRACT ([In] int NOP1, [IN] int NOP2, [OUT, RETVAL] INT * PRET);
HRESULT MULTIPLY ([in] int NOP1, [IN] int NOP2, [OUT, RETVAL] INT * PRET);
HRESULT DIVIDE ([In] int NOP1, [IN] int NOP2, [OUT, RETVAL] INT * PRET);
}
[
UUID (01147c39-9da0-4f7f-b525-d129745aad1e),
Helpstring ("Iadvancedmath Interface),
Object,
Pointer_DEFAULT (UNIQUE)
]
Interface Iadvancedmath: IUNKNOWN
{
HRESULT FACTORAL ([in] int NOP1, [OUT, RETVAL] INT * PRET);
HRESULT FABONACCI ([In] int NOP1, [OUT, RETVAL] INT * PRET);
}
[
UUID (CA3B37EA-E44A-49B8-9729-6E9222CAE844),
Version (1.0),
Helpstring ("Mathcom 1.0 Type Library")
]
Library MathComlib
{
Importlib ("stdole32.tlb");
Importlib ("stdole2.tlb");
[
UUID (3BCFE27E-C88D-453C-8C94-F5F7B97E7841),
Helpstring ("Mathcom Class")
]
CoClass Mathcom
{
[default] interface isimath;
Interface Iadvancedmath;
}
}
Check the settings in Project / Setting / MIDL before compiling this project. Correctly set as shown below: Figure 1.4 MIDL's correct settings After the correct settings, if the compilation is no error, then four in the directory of the project
File Name The header file of the MATHCOM.H interface, if you want to declare or define the interface, use this file Mathcom_i.c definition interface and class objects and libraries, only this file is introduced when you want to use something related to GUID. This file can only be introduced throughout the project, otherwise there will be repeated definition Mathcom_p.c Used for stub and proxy DLLDATA.C Unknown 1.3 Increase the registration function as COM must register and log out. 1.3.1 Add a MATHCOM.DEF file DEF file is a module definition file. It allows the extraction symbol to be posed to different introduction symbols.
//Mathcom.def file
Mathcom.def: Declares the module parameters.
Library "mathcom.dll"
Exports
DllcanunloadNow @ 1 Private
DllgetClassObject @ 2 private
DllRegisterServer @ 3 private
DllunregisterServer @ 4 private
DllunregisterServer This is a function name @ 4 <- This is the function serial number private next to DllRegisterServer () and DllunregisterServer (). (The role of other two functions will be described later) 1.3.2 DllRegisterServer () and dllunregisterServer () DllRegisterServer () functions The role is to register the COM server to the unit. The effect of the DllunregisterServer () function is to log out of the COM server from this unit. 1.4 MATHCOM.CPP file now modify the mathcom.cpp file as follows:
// Mathcom.cpp: Defines the entry point for the dll application.
//
#include "stdafx.h"
#include
#include
#include "mathcom.h"
// Standard Self-Registration Table
Const char * g_regtable [] [3] = {
{"CLSID / / {3BCFE27E-C88D-453C-8C94-F5F7B97E7841}", 0, "Mathcom"}
{"CLSID / / {3BCFE27E-C88D-453C-8C94-F5F7B97E7841} // InprocServer32",
0,
(const char *) -1 / * Represents the value of the file name * /},
{"CLSID / / {3BCFE27E-C88D-453C-8C94-F5F7B97E7841} // progid", 0, "Tulip.mathcom.1"},
{"tulip.mathcom.1", 0, "mathcom"},
{"Tulip.mathcom.1 // CLSID", 0, "{3BCFE27E-C88D-453C-8C94-F5F7B97E7841}"}
}
Hinstance g_hinstdll;
Bool apientry dllmain (Handle Hmodule,
DWORD UL_REASON_FOR_CALL,
LPVOID LPRESERVED
)
{
g_hinstdll = (hinstance) hmodule;
Return True;
}
/ ************************************************** ******************** Function Declare: DllunregisterServer
* EXPLAIN: Self-unregistration Routine
* Parameters:
* void -
* Return:
* Stdapi -
* Author: Tulip
* TIME: 2003-10-29 19:07:42
*********************************************************** ****************** /
StDAPI DllunregisterServer (Void)
{
HRESULT HR = S_OK;
Char szfilename [MAX_PATH];
:: getModuleFileName (g_hinstdll, szfilename, max_path);
INT NENTRIES = SIZEOF (g_regtable) / sizeof (* g_regtable);
For (int i = 0; succeeded (hr) && i { Const char * pszkeyname = g_regtable [i] [0]; Long Err = :: regdeletekey (hkey_classes_root, pszkeyname); IF (Err! = Error_Success) HR = s_false; } Return HR; } / ************************************************** ******************* * Function Declare: DllRegisterServer * EXPLAIN: SELF Registration Routine * Parameters: * void - * Return: * Stdapi - * Author: Tulip * TIME: 2003-10-29 19:43:51 *********************************************************** ****************** / StDAPI DLLREGISTERSERVER (VOID) { HRESULT HR = S_OK; Char szfilename [MAX_PATH]; :: getModuleFileName (g_hinstdll, szfilename, max_path); INT NENTRIES = SIZEOF (g_regtable) / sizeof (* g_regtable); For (int i = 0; succeeded (hr) && i { Const char * pszkeyname = g_regtable [i] [0]; Const char * pszvaluename = g_regtable [i] [1]; Const char * pszvalue = g_regtable [i] [2]; IF (pszvalue == (const char *) - 1) { pszvalue = szfilename; } HKEY HKEY; Long Err = :: regReateKey (HKEY_CLASS_ROOT, PSZKEYNAME, & HKEY); IF (err == error_success) { Err = :: RegSetValueex (HKEY, Pszvaluename, 0, REG_SZ, (const byte *) pszvalue, Strlen (pszvalue) 1); :: regcloseKey (HKEY); } IF (Err! = Error_Success) { :: DllunregisterServer (); HR = E_FAIL; } } Return HR; } Stdapi DllgetClassObject (Refclsid Rclsid, Refiid Riid, Void ** PPV) { Return Class_e_classNotavailable; } StDAPI DLLCanunloadNow (Void) { Return E_FAIL; } I just add a few necessary header files and several global variables in this file. DllRegisterServer () and DllunregisterServer () are implemented. And for other two-in-one function I only returned a wrong value. 1.5 Summary Now our project should have the following documents: File Name Schemes STDAFX.H and Stdafx.cpp Precommination File Mathcom.cppdll Entry Functions and Other Important Functions Defined Local Mathcom.def Module Definition File Mathcom.IDL Interface Definition File (If compiled after 1.2, there should be four files ) Ok, now, my so-called COM has implemented registration and logout. If you do the following "Regsvr32 absolute path mathcom.dll" is registered, you will register this COM component. After performing this command, check if the HKEY_CLASSES_ROOT / CLSID item of the registration item is to see if the 3BCFE27E-C88D-453C-8C94-F5F7B97E7841 is present (God bless the existence). Like the "Regsvr32 -u absolute path Mathcom.dll", then look at the registry. In fact, the DLL that has just been generated is not a COM component, haha! ! ! Because he did not implement dllgetClassObject () nor did any one of IsMipleMath and Iadvancedmath two interfaces. Let us continue to travel! ! ! 2. Implement IsmipleMath, IadvancedMath interface and DllgetClassObject () 2.1 Implementation IsMipleMath and IadvanceDmath interfaces Let the original CMATH class to implement the ISMIPLEMATH interface and IadvancedMath interface. The modified place is as follows: 1) Math.h file / * @ ** # --- 2003-10-29 21:33:44 (tulip) --- # ** @ #include "interface.h" * / #include "mathcom.h" // Added, replacing the above Class Cmath: Public Isimplemath, Public Iadvancedmath { Private: Ulong m_cref; Private: INT CALCFACTORIAL (int NOP); INT CALCFABONACCI (INT NOP); PUBLIC: CMATH (); // iUnknown method STDMETHOD (QueryInterface) (Refiid Riid, Void ** PPV); STDMETHOD_ (ULONG, ADDREF) (); STDMETHOD_ (ULONG, RELEASE) (); // Isimplemath Method StdMethod (Add) (int NOP1, INT NOP2, INT * PRET); StdMethod (int NOP1, INT NOP2, INT * PRET); stdmethod (%) (int NOP1, INT NOP2, INT * PRET) STDMETHOD (Divide) (int NOP1, INT NOP2, INT * PRET); // Iadvancedmath Method StdMethod (INT NOP, INT * PRET); StdMethod (INT NOP, INT * PRET); } 2) Math.cpp file / * @ ** # --- 2003-10-29 21:32:35 (tulip) --- # ** @ #include "interface.h" * / #include "math.h" STDMETHODIMP CMATH :: Queryinterface (Refiid Riid, Void ** PPV) {// Here this is the function of implementing Dynamic_cast, but because Dynamic_CAST is related to the compiler. IF (riid == iid_isimplemath) * ppv = static_cast Else IF (riid == iid_iadvancedmath) * ppv = static_cast Else IF (riid == iid_iunknown) * ppv = static_cast Else { * ppv = 0; Return E_NOINTERFACE; } Reinterpret_cast Return S_OK; } STDMETHODIMP_ (ULONG) CMATH :: AddRef () { Return m_cref; } STDMETHODIMP_ (ULONG) CMATH :: release () { Ulong res = --m_cref; // Use temporary variables to cache the modified reference count value If (res == 0) // Because the data will be quoted after the object has been destroyed, this object will be illegal. DELETE THIS; Return res; } STDMETHODIMP CMATH :: Add (int NOP1, INT NOP2, INT * PRET) { * PRET = NOP1 NOP2; Return S_OK; } StdMethodimp Cmath :: Subtract (int NOP1, INT NOP2, INT * PRET) { * PRET = NOP1 - NOP2; Return S_OK; } STDMETHODIMP CMATH :: Multiply (int NOP1, INT NOP2, INT * PRET) { * PRET = NOP1 * NOP2; Return S_OK; } StdMethodimp Cmath :: Divide (int NOP1, INT NOP2, INT * PRET) { * PRET = NOP1 / NOP2; Return S_OK; } INT CMATH :: CalcFactorial (int NOP) { IF (NOP <= 1) Return 1; Return NOP * CALCFACTORIAL (NOP - 1); } STDMETHODIMP CMATH :: Factorial (int NOP, INT * PRET) { * PRET = CalcFactorial (NOP); Return S_OK; } Int cmath :: Calcfabonacci (int NOP) { IF (NOP <= 1) Return 1; RETURN CALCFABONACCI (NOP - 1) Calcfabonacci (NOP - 2); } StdMethodimp Cmath :: Fabonacci (int NOP, INT * PRET) { * Pret = Calcfabonacci (NOP); Return S_OK; } Cmath :: cmath () { m_cref = 0; } 2.2 COM components are transferred to the process 1) COM library initialization Using COINITIALIZE Sequence Function (Client) 2) Activation COM (Client) 3) Turn the corresponding DLL into the COM library () 4) Call DllgetClassObject () 4) Call COM Components through the registry entry Function (COM Components) 5) This step is not required by returning the interface pointer through the class factory (COM library) 2.3 DllgetClassObject () Implementing the following statement in Mathcom.cpp, #include "math.h" #include "mathcom_i.c" And modify the dllgetClassObject () in matHcom.cpp into the following: / ************************************************** ******************* * Function Declare: DllgetClassObject * EXPLAIN: * Parameters: * Refclsid rclsid - * Refiid Riid - * void ** PPV - * Return: * Stdapi - * Author: Tulip * TIME: 2003-10-29 22:03:53 *********************************************************** ****************** / Stdapi DllgetClassObject (Refclsid Rclsid, Refiid Riid, Void ** PPV) { Static CMATH * PM_MATH = New CMATH; IF (rclsid == CLSID_MATHCOM) RETURN PM_MATH-> QueryInterface (RIID, PPV); Return Class_e_classNotavailable; } 2.4 Client Next We write a client program to test this COM. Newly built a Win32 Console project with Testmathcom, add it to Mathcom Workspace. Add a file called main.cpp in the TestMathcom engineering, this file is as follows: //main.cpp file #include #include "../mathcom.h"// here, please pay attention to the path #include "../mathcom_i.c"// here, please pay attention to the path #include Using namespace std; Void main (void) { // Initialize the COM library HRESULT HR = :: Coinitialize (0); ISIMPLEMATH * PSIMPLEMATH = NULL; IADVANCEDMATH * PADVANCEDMATH = NULL; INT nreturnvalue = 0; HR = :: CogetherClassObject (CLSID_MATHCOM, CLSCTX_INPROC, NULL, IID_IMPLEMATH, (void **) & psimplemath); En (ac)) { HR = psimplemath-> add (10, 4, & nreturnValue); En (ac)) Cout << "10 4 =" << nreturnValue << Endl; NRETURNVALUE = 0; } / / Query the interface Iadvancedmath HR = psimplemath-> queryinterface (IID_IADVANCEDMATH, (Void **) & padvancedmath); En (ac)) { HR = padvancedmath-> fabonacci (10, & nreturnValue); En (ac)) Cout << "10 fabonacci is" << nreturnValue << Endl; } Padvancedmath-> release (); psimplemath-> release (); :: Couninitialize (); :: System ("Pause"); Return; } For how to debug a DLL, please refer to Appendix A 2.5 small knot to now we should have 2 projects and 8 files, as follows: Engineering Files MathcomstDafx.h and Stdafx.cpp Precatchable File Mathcom.cppdll Entry Functions and Other Important Functions Defined Local Mathcom.def Module Definition File Mathcom.IDL Interface Definition File (If compiled after 1.2, there should be four files ) Math.h and Math.cppismiPLEMATH, IADVANCEDMATH interface Implementation class TestmathCommain.cpp Mathcom's client for testing Mathcom components In this section we have completed a practical COM component. We completed the client of this COM component. If you have created a COM instance, you may find that the client in this section is not creating a COM instance in cocreateInstance (), because we have not implemented the iClassFactory interface in this COM component (this interface is implemented in the next part) . Through this example, I hope everyone understands the following points: 1) DLLGETCLASSOBJECT (), please see the COM component to transfer the approval process, and also put the breakpoint on the dllgetclassobject () function, take a closer look at his implementation (without achieving the iClassFactory interface) And his incoming parameters. 2) Why don't use COCREATEINSTANCE () in this client program to create a COM instance using CogetherClassObject (). You can try COCREATEINSTANCE () to create CMATH to see what the first parameter of DllgetClassObject () is? 3) Implementing the iClassFactory interface is not required, but it should be said to be necessary (how to implement it Next chapter) 4) The implementation of DllRegisterServer () and DllunregisterServer () should be grasped. 5) The client requires those files when calling the COM component (as long as the two files generated by the IDL file) 3, the class is appendix A I am a little understanding of the DLL: Write a few simple DLLs and learn about ** .dll and **. LIB. One: DLL1.1 without lib builds a DLL without LIB 1) Create a COM_1.cpp file (note that this DLL does not use) 2) Code below COM_1.CPP 3) Press F5 to run, all things are pressed. 4) The following error should occur: LINKING ... Creating library debug / com_1.lib and object debug / com_1.exp Libcd.lib (CRT0.Obj): Error LNK2001: Unresolved External Symbol_Main DEBUG / COM_1.EXE: FATAL ERROR LNK1120: 1 Unresolved Externals 5) Enter Project | Setting, modify "/ d '_console' '" in "Project Options" in the "C / C " property box "" / d' '_Windows'' ". 6) Enter Project | Setting, add the following compilation switch "/ DLL" in "Project Options" in the "Link" property box The increased compilation switch is approximately as follows: Kernel32.lib user32.lib gdi32.lib Winspool.lib comdlg32.lib advapi32.lib shell32.lib Ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib / NOLOGO / DLL / INCREMENTAL: YES /pdb:"debug/com_1.pdb "/ debug / machine: i386 /out:"debug/com_1.dll" /implib:"debug/com_1.lib " / PDBTYPE: SEPT Note: "/ DLL" should have a space between the rear switch //com_1.cpp #include Bool Apientry Dllmain (Handle Hmodule, DWORD DWREASON, VOID * LPRESERVED) { Handle g_hmodule; Switch (dwreason) { Case DLL_Process_attach: g_hmodule = (hinstance) hmodule; Break; Case DLL_PROCESS_DETACH: g_hmodule = null; Break; } } It can be compiled now, this small segment code will generate a DLL, but this DLL is useless. There is no extraction function and variable. 1.2 Debug DLL1 DLL1) Newly built a new project client, the engineering type is console, and the DLL COPY to the Client Project Created above will increase the client.cpp (code) to the project client to 3) Select the Client project. And in the Project | Setting | Debug | Category drop-down box, Figure 1.4 Debugging Note This is a method of debugging the DLL 5) Now you can debug in the client and com_1.dll. Here we can only debug the dllmain () function, because there is no other thing in the DLL, and I will start to add something. Second: DLL2.1 with lib creates a DLL with lib that we produce one Lib on the original basis. The new code is as follows: #include Extern "c" __declspec (dllexport) Void Tulip (void) { :: MessageBox (Null, "OK", "I '''am Fine", MB_OK; } Bool Apientry Dllmain (Handle Hmodule, DWORD DWREASON, VOID * LPRESERVED) { Handle g_hmodule; Switch (dwreason) { Case DLL_Process_attach: g_hmodule = (hinstance) hmodule; Break; Case DLL_PROCESS_DETACH: g_hmodule = null; Break; } Return True; } In this DLL, we lead to a Tulip function. What if we want to call this function in the customer? In addition to generating a DLL, the code is generated more than the first program, and now you should know the relationship between DLL and LIB. The lib file is a DLL output symbol file. If a DLL does not have anything output, then there is no corresponding lib file, but as long as a DLL outputs a variable or function, the corresponding lib file. In general, DLL and lib are mutually supporting. How should we call DLL functions when a DLL he has an output function (or variable) without a lib file? Please see the method below. 2.2 Debug with DLL with reference but no header file Note: This method does not use the com_1.lib file at all, you can delete the com_1.lib file without affecting. The client code at this time is below: #include Int main (void) { / / Define a function pointer Typedef void (* tulipfunc) (void); / / Define a function pointer variable Tulipfunc tulipfunc; / / Load our DLL Hinstance hinst = :: loadLibrary ("com_1.dll"); / / Find the Tulip function of the DLL Tulipfunc = (Tulipfunc) GetProcaddress (HinSt, "Tulip"); // Call the function in the DLL Tulipfunc (); RETURN 0; } It is very convenient to call the system function with the above method, because for user32.dll, gui32.dll, I have no corresponding LIB, so generally the above method. Three: DLL3.1 with header file Create a DLL with the lead information header file if it is too annoyed to call our own DLL! Because our DLL may not have a standardized document like WINDOW. After a while, we will forget the format of the DLL internal function. If we release this DLL, the customer will definitely marry you behind you! At this time we need a way to understand the DLL to lead the information. I created one .h file. Continue us. Our DLL code only needs to modify a little bit, the code is as follows: #include #include "header.h" // See no, this is our increased header file Extern "c" __declspec (dllexport) Void Tulip (void) { :: MessageBox (Null, "OK", "I '''am Fine", MB_OK; } Bool Apientry Dllmain (Handle Hmodule, DWORD DWREASON, VOID * LPRESERVED) { Handle g_hmodule; Switch (dwreason) { Case DLL_Process_attach: g_hmodule = (hinstance) hmodule; Break; Case DLL_PROCESS_DETACH: g_hmodule = null; Break; } Return True; } And the Header.h file has only one line of code: EXTERN "C" __DECLSPEC (DLLEXPORT) Void Tulip (Void); 3.2 Debugging the DLL with header files At this time our client program should become as follows: (more simple than the second) #include #include "../header.h"// Note path // Note the path, another method of loading com_1.lib is Project | Setting | Link settings #pragma comment (LIB, "com_1.lib") Int main (void) { Tulip (); // As long as we can call the functions in the DLL. Return 0; } Four: Summary Today, three DLL forms, the first is there is no practical value, but it can clear the relationship between DLL and LIB. Most of the cases we have encountered are the third, the DLL provides usually **. Lib and **. H file, and the second method is applicable to the system function. I hope that the high-end fingers are communicating. Note: I am now in a moment, I wrote above, I have always summed up something about COM, but I write to this look, COM is starting from DLL. (Full text)