Environment: Visual C++
In part 1 of this series, I covered what types of .DLLs you can
make with MFC and how to build MFC regular and extension
.DLLs. In part 2, I covered potential problems that can
arise when you use .DLLs and suggested several ways to avoid them.
In this article, I’ll build upon the first two articles by providing
more coding examples and technical details.
Exporting Resources From a .DLL
You may wish to create a .DLL which exports resources such as dialog
templates, bitmaps, icons or strings. You may, for example, wish to
make your application multilingual, with one .DLL for English strings
and dialog templates, another with Spanish strings, etc. Exporting
resources is relatively easy to do.
Your first step in building a .DLL which exports resources is (of course) to
build a .DLL and give it some resources. You can use a regular or extension
.DLL. Use the App Wizard to create your .DLL, as described in Article 1.
To add a resource using the Visual C++ menu, select “Insert” and “Resource”
and then select the type of resource you wish to add.
Once you’ve built your .DLL and given it all the resources it is to import,
you must build a header file to be used by the client application. You can
add a new header file using the Visual C++ menu by selecting “File | Add To
Project | New | C/C++ Header File.” Now open your .DLL’s Resource.h file.
Assuming you have compiled your .DLL, the top of this file will look something
like this:
#define IDS_SOME_STRING1 1
#define IDS_SOME_STRING2 2
#define IDS_SOME_STRING3 3
#define IDD_SOME_DIALOG 11000
#define IDB_SOME_BITMAP 11001
As you can see, each of these #defines gives one of your resources an identifying
integer value. Copy the complete #define for each resource you wish to
export into your newly created header file.
At this point, your .DLL is almost ready to go. Only one more step is required.
In articles 1 and 2, I demonstrated how to use a .DLL with implicit linking.
When you are using implicit linking, the client application will attempt
to connect to your .DLL automatically. If it cannot do so,
the client application won’t run. When you use implicit linking, it
is important to remember that the client application will not attempt to
connect to your .DLL unless your .DLL exports at least one class or
function which is used by the client. So even if your .DLL’s only purpose
is to provide resources, you still must export at least one function or
class which is used by the client. It doesn’t matter whether your exported
class or function does anything useful. It simply has to be there in order to
force the client to connect to the .DLL. So all that’s left to make your .DLL
complete is to export a “do-nothing” function. See Article 1 for an explanation
of how to export a function.
Setting Up the Client Application
Article 1 describes how to build a client application which will connect to your
.DLL. Remember, your client application must actually call at least one function
from your .DLL or create at least one instance of an imported class. If not, your
client application will not automatically connect to your .DLL at runtime. Be sure
to add a copy of the header file you created earlier to your client application
project.
In some respects, an extension .DLL is easier to use when you are importing resources.
This is because an extension .DLL will try to find a resource in your .DLL if it
cannot find it in its own resources. Suppose your client application included the
following code:
CString str;
str.LoadString(IDS_SOME_STRING1);
AfxMessageBox(str);
The first thing your client application will do is look for a definition of
IDS_SOME_STRING1. Suppose the header file you imported defines IDS_SOME_STRING1
as 1. The client application will look in its own resources for resource #1. If
it cannot find resource #1 in its own resources, it will next look in the
extension .DLL. If you built a regular .DLL, the client application won’t take
this extra step.
Note that there is a big potential for problems here. If your client application
has some other resource which is also defined as resource 1, it will attempt to
load its own resource. The client only looks to your extension .DLL if it can’t
find the properly numbered resource within its own resources. One way to avoid this
problem is to renumber your resources to avoid conflicts.
When you use a regular .DLL, the client application must be told explicitly to
look to the .DLL to find the resource. Here’s how you do it:
//Store the current resource handle
HINSTANCE hClientResources = AfxGetResourceHandle();//Tell the client to use the .DLL’s resources
AfxSetResourceHandle(::GetModuleHandle(“SomeDll.dll”));//Do something with the .DLL’s resources
CString str;
strRes.LoadString(IDS_SOME_STRING1);//Restore the client application resource handle
AfxSetResourceHandle(hClientResources);
You can use this technique in your client application even if you are using
an extension .DLL. With a regular .DLL it is necessary. While not absolutely
necessary with an extension .DLL, it gives you the certainty that the right
resources will be loaded.
Exporting a CDialog Based Class
Exporting a class derived from the CDialog class is only slightly more complicated
than exporting other classes. The extra complication comes from the fact that you
must also export the dialog template, which is a resource.
When you export a class, you must provide a copy of the class header file to be
used by the client application. When you export a class derived from CDialog,
which has a resource, you must also export the #define for the resource ID.
So the easiest thing to do is simply tack the #define onto the top of the header
file like this:
#ifndef IDD_HELLO_DIALOG
#define IDD_SOME_DIALOG 9000
#endif
As with other exported resources, you can run into problems if the client
application also has a resource with the same number. Either be careful
to avoid conflicts, or use the code shown above to tell the client application
explicitly where to find the correct resource.
Note that the same technique used with CDialog bases classes can also be used
with any other class you wish to export which uses resources. Be sure to
define the resources in the header file you provide for the client application, and take whatever steps are necessary to avoid conflicts.