@ stdcall -private DllCanUnloadNow() @ stdcall -private DllGetClassObject(ptr ptr ptr) @ stdcall -private DllRegisterServer() @ stdcall -private DllUnregisterServer()
|
»DLLs
|
Count DLL in COMThis tutorial describes a simple COM object implemented as a DLL. Client programs may create an instance of the counter, increment the counter, and read back the count value. Exciting, eh? The tutorial demonstrates foundation topics important to COM development and describes how to integrate new COM DLLs into Wine. A basic understanding of COM and C programming is assumed. An installation of Wine is required to compile and run the code developed in this tutorial. Source code for this tutorial is available from the tutorial's introduction. Synopsis
Generic Structure of a COM DLLCOM DLLs often share a great deal of structure that can be easily copied from an existing DLL and updated for new DLL. This includes:
Interface SpecificationAn interface specification describes the methods that client programs can use to interact with specific COM object. The interface definition provides flexibility in where the service resides by aiding in the creation of marshaling functions for out of processes services, and provides a language independent description of how clients can call into an interface. While development without an interface specification is possible, doing so would limit the created service to running as an in process server and not allow languages such as Visual basic to interact with the service. To learn more about authoring a new IDL take a look at MSDNs description of IDL or pick up a good book on the topic. IDLs for services ported to Wine may be generated using Wine's oleview program. This requires a type library for the service or a DLL that carries the type library within it. If these are not available, the IDL can be rewritten by hand from the services interface documentation that is often available on MSDN. The count IDL should be stored with the other Wine IDLs in $WINEPATH/include. Projects using code generated from an IDL can use a simple wrapper IDL located in the project's directory. The count DLL uses definitions of class IDs defined in $WINEPATH/include/count.idl. Thus, it uses the local wrapper $WINEPATH/dlls/count/count_local.idl to include the base version of the IDL.
DLL (Un)RegistrationCOM references the registry to find the DLL required to satisfy client requests to create an in-process COM object. A DLL must implement the DllRegisterServer and DllUnregisterServer functions used to register and unregister itself. This is accomplished in the count DLL using the generic registration implementation in $WINEPATH/dlls/count/regsvr.c.
DllMain, DllGetClassObject, and DllCanUnloadNowDllMain is a DLL's entry point. When an object is requested by a client, COM calls the DllGetClassObject function to access a class factory provided by the DLL, and uses the class factory to provide the client with a pointer to an object instance. DllCanUnloadNow is called by COM to see if it is safe to unload the DLL. Base implementations of these functions are often quite similar. A first cut for the count DLL is located in $WINEPATH/dlls/count/count_main.c.
IClassFactoryIClassFactory provides the interface used to create new instances of a COM class and is used by the DllGetClassObject function to provide new object instances to requesting clients. The IDL for the IClassFactory is found in $WINEPATH/include/unknwn.idl The count servers IClassFactory is located in $WINEPATH/dlls/count/factory.c.
Core DLL implementationLittle remains to be written after the above broiler plate code in place. The core of a new COM object will probably use:
Common Header FileA common header file is used by the implementation of a DLL to define the object's structure and expose a static instance of the object. The common header file used by count is seen in $WINEPATH/dlls/count/count_private.h.
Application LogicThe core functionality of count is implemented in $WINEPATH/dlls/count/count.c, including methods for the interfaces defined in $WINEPATH/include/count.idl. The IUnknown interface is used to interact with COM objects and is provided by all COM objects. The IDL for the IUnknown is found in $WINEPATH/include/unknwn.idl. Since ICount descends from IUnknown, the implementation of IUnknown resides within ICount.
Wine Specifics for COM DLLsMost of the description up to this point is applicable to general COM development. We now transition to points specific to development of a COM object in Wine. These Wine specifics include:
Specification of Exported FunctionsAll COM DLLs will export at least the following four functions: DllCanUnloadNow, DllGetClassObject, DllRegisterServer, and DllUnregisterServer. A DLL must explicitly export functions that will be called by COM. In Wine this accomplished using a spec file to specify the functions to export. Count's spec is located in $WINEPATH/dlls/count/count.spec: @ stdcall -private DllCanUnloadNow() @ stdcall -private DllGetClassObject(ptr ptr ptr) @ stdcall -private DllRegisterServer() @ stdcall -private DllUnregisterServer() Makefile GenerationWine's build system eases the authoring of Makefiles. A new DLL requires Makefile.in describing key properties of the DLL. For count this is located in $WINEPATH/dlls/count/Makefile.in: TOPSRCDIR = @top_srcdir@ TOPOBJDIR = ../.. SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = count.dll IMPORTLIB = libcount.$(IMPLIBEXT) IMPORTS = ole32 user32 advapi32 kernel32 EXTRALIBS = -luuid C_SRCS = \ count_main.c \ factory.c \ count.c \ regsvr.c IDL_I_SRCS = count_local.idl @MAKE_DLL_RULES@ @DEPENDENCIES@ # everything below this line is overwritten by make depend
After generating the Makefile the DLL can then be built using make. Be warned that building all of Wine takes a long time. For this reason the -C option is your good friend. The count DLL can be built using: make -C $WINEPATH/dlls/count
Registering and Testing COM DLLs in WineRegistering a DLLAfter the DLL is built, it must be registered in the registry so that COM knows the DLL to load when a client requests an instance of the object. As in Windows, DLL registration is accomplished using the regsvr32 program from the base Wine directory. For count running in Wine this is accomplished using: cd $WINEPATH $WINEPATH/programs/regsvr32/regsvr32 count This adds to the registry entries similar to: [Software\\Classes\\CLSID\\{55E9B534-76AA-11DC-8B8E-F6CC56D89593}] 1193091471
@="Count"
[Software\\Classes\\CLSID\\{55E9B534-76AA-11DC-8B8E-F6CC56D89593}\\InProcServer32] 1193091471
@="count.dll"
"ThreadingModel"="Both"
[Software\\Classes\\Interface\\{3EE455D8-76AA-11DC-86B3-C4CC56D89593}] 1193091471
@="ICount"
[Software\\Classes\\Interface\\{3EE455D8-76AA-11DC-86B3-C4CC56D89593}\\NumMethods] 1193091471
@="2"
By default this registry is located in ~/.wine/system.reg. A module is unregistered using /u option with regsvr32 (but don't do this if you want to test the count DLL in the next step!): cd $WINEPATH $WINEPATH/programs/regsvr32/regsvr32 /u count TestingEach DLL in Wine should include a suite of tests. The tests are located in the DLLs tests directory. The client program using the count DLL is $WINEPATH/dlls/count/tests/count_test.c.
Test MakefileTests use a Makefile.in similar to that used for DLLs. Count's Makefile is located in $WINEPATH/dlls/count/tests/Makefile.in.
Running TestsThe count tests can now be run within Wine: $WINEPATH/wine $WINEPATH/dlls/count/tests/count_test.exe.so |