Another unique capability that we can provide through a DLL built using C++Builder is support for shared memory between multiple applications.
When a DLL is loaded by an application within Windows , it's designed to be protected within the memory space allocated for the application. If two different applications load and use the same DLL they will not impact each other. In other words, a separate instance of the DLL would exist for both applications. This is part of the protection provided by the Windows 32-bit operating system.
There's a trick, however, that can be applied to create a DLL that can share the same memory segmentation between multiple applications. This can be really useful if you have an application that requires data from other applications during its execution (and you don't want to use COM, pipes, or mail slots). This capability is a form of Interprocess Communications (IPC). The specific IPC technique that we'll look at in this section is often referred to as shared segmentation.
Over the course of the past several years , a handful of books and magazine articles focused on the Win32 API have provided examples of DLL shared segmentation that appear to be exclusive for Visual C++ developers. Within Visual C++, the pragma data_seg() clause is used to isolate code to be shared across multiple applications that load the same DLL. A Visual C++ code snippet is provided in Listing 16.18 that demonstrates how to use these pragmas.
#pragma data_seg(".sdata") // start of section must initialize! int numAutoDealers = 0; SAuto AllDealers = {0,0,0,0};' SAuto Factory = {100, 100, 100, 100}; int NextDealerID = 0; SDealer Dealer[MaxDealers] = {false, NULL}; Bool DealerLock = false; #pragma data_seg() // these will NOT be global/shared to all modules // (important only to attached component). SAuto LocalDealer = {0, 0, 0, 0}; int DealerID;
The code block that follows the .sdata segment identifies shared data variables . These must be initialized in this section to be effective. The code block that follows the empty data_seg() reverts back to local data, which is not shared across processes.
Also required within Visual C++ is a .def file identifying the shared segmentation area, which is included as part of the project and used by the linker. An example DEF file is shown in Listing 16.19.
LIBRARY SharedSeg ; name of DLL module (output file name) SECTIONS .sdata READ WRITE SHARED ; shared data
Unfortunately, C++Builder does not support these techniques that apply for Visual C++. Fear not though, there is a way.
To fully understand how shared segmentation works, we need a simple example that illustrates the effectiveness of multiple applications that are able to share data. A project called Inventory.bpr has been provided in the SharedSegCode folder on the CD-ROM. This project provides the basic constructs for a DLL that maintains inventory information among applications that either represent an automobile factory or an automobile dealer. The files included for this DLL project are illustrated in Figure 16.16.
The principal source file in the project is InventoryUnit.cpp . This file is quite lengthy and it's best if we don't get bogged down with the details of each function, therefore, you are encouraged to examine the file on your own to fully understand the functionality provided by the DLL. However, some interesting code elements are unique to supporting shared segmentation under C++Builder. They are provided in Listing 16.20.
#include <windows.h> #pragma hdrstop #include <stdio.h> #include <time.h> #include "InventoryUnit.h" #include "InventoryDataTypes.h" SAuto LocalDealer = {10, 10, 10, 10}; int DealerID; extern int numAutoDealers; extern SAuto AllDealers; extern SAuto TotalSold; extern int NextDealerID; extern SDealerFactory DealerFactory[MaxDealers]; extern SAuto Factory; extern bool InventoryLock;
This is just the top few lines of the DLL code (right before the DLLMain() ). You'll notice several header files included in the code such as InventoryUnit.h and InventoryDataTypes.h followed by two variables local only to the DLL, and finally a number of extern variables. These extern variables are the variables within our shared segmentation area. The data types associated to all the variables are found in the InventoryDataTypes.h file. Whereas, the InventoryUnit.h file exposes the functions provided by the DLL so that outside applications can interface with the DLL and, through these functions, get at the shared data.
Another important file identified in the project is InventorySharedSeg.cpp , which is provided in Listing 16.21.
#pragma option -zRSHSEG // change default data segment name #pragma option -zTSHCLS // change default data class name #include "InventoryDataTypes.h" // Here is the initialized data that will be shared. int numAutoDealers = 0; SAuto AllDealers = {0,0,0, 0}; //false}; SAuto TotalSold = {0,0,0,0}; SAuto Factory = {100, 100, 100, 100}; // start off with 100 cars int NextDealerID = 0; SDealerFactory DealerFactory[MaxDealers] = {false, NULL}; Bool InventoryLock = false;
This is probably the most unique and important file of this particular DLL. Notice the two opening pragma clauses.
#pragma option -zRSHSEG // change default data segment name #pragma option -zTSHCLS // change default data class name
They tell the compiler that the code to follow is to be included in the same shared segmentation and class. Next, we see the InventoryDataTypes.h file, again, which is followed by the variables to be used for containing the data that will be shared through the DLL. What's also vital is the initialization of these variables upon declaration. This is similar to what's required with Visual C++.
The third element of the Inventory DLL is the Inventory.DEF file, which is provided in Listing 16.22.
LIBRARY Inventory SEGMENTS SHSEG CLASS 'SHCLS' SHARED
This piece is also vital. The pragma calls that were used in InventorySharedSeg.cpp identified both SHSEG and SHCLS . These labels are declared in this definition file and can be named something else if desired.
To share information contained in a shared segmentation variable, the DLL needs to provide a function for an application to read or set the shared data. Let's look at just one function provided in Listing 16.23 that demonstrates this capability.
int BuySellCar(int model,int quantity) { unsigned int StartTime = clock(); while (InventoryLock) { if (StartTime - clock() > MaxWaitTime) break; } InventoryLock = true; // dealer can't sell if no cars are available from dealer or factory if (quantity > 0) // dealer can't sell back to factory { int value = Factory[model]; if (value >= quantity) // can't deplete the factory below zero { LocalDealer[model] += quantity; AllDealers[model] += quantity; Factory[model] -= quantity; } else // the factory has enough cars for order { LocalDealer[model] += value; AllDealers[model] += value; Factory[model] -= value; MessageBeep(MB_OK); // factory doesn't have enough cars to ship } } else // dealer is selling a car { int value = LocalDealer[model]; if (value >= abs(quantity)) // can't deplete all the vehicles { LocalDealer[model] += quantity; AllDealers[model] += quantity; TotalSold[model] -= quantity; } else { LocalDealer[model] -= value; AllDealers[model] -= value; TotalSold[model] -= value; MessageBeep(MB_OK); // dealer doesn't have enough cars to sell } } // notify other Dealers of local dealer's change Status for (int i= 0; i< MaxDealers; i++) { if (DealerFactory[i].Open) // tell all dealers... PostMessage(DealerFactory[i].hwnd, WM_CAR_CHANGE, (WPARAM) i, // what dealer id changed (LPARAM) model);// what car changed } InventoryLock = false; return DealerID; // no error, return store id }
We do a few things here such as performing a lock to ensure that another application's call to this function won't step on the same data. When locked, we examine the parameter values and assign the data to the appropriate variable whether it is local or shared. We also use a custom windows message to notify other applications, which represent dealers and/or a factory, that new data is available.
Figure 16.17 provides an illustration of our DLL supporting multiple, simultaneous applications that represent either a factory or car dealerships. All these systems are able to share and reflect information, even though they are separate processes.
This complete project group , including the Factory and Dealership applications as well as the Inventory DLL, is provided on the CD-ROM in the SharedSegCode folder. Also, the Visual C++ code that we started with earlier in this discussion could be used to develop an equivalent Inventory DLL under VisualC++ ”this is also included on the CD-ROM. Although the myth had been that shared segmentation could only be supported by Visual C++, we've shown that it can indeed be supported by C++Builder.
Top |