Reputation: 589
I have successfully made a resource only DLL, and placed some dialogs, that were in the main exe. So I have Test.exe, and TestENU.dll. It works fine, but I am trying to find a way to only override some dialogs, instead of being required to have all exactly the same dialogs, and instead pull some from the base exe, as my real project has around 40 dialogs, and I only want to have to translate some, slowly over time.
(I used this link to do satellite/resource dll) http://msdn.microsoft.com/en-us/library/8fkteez0(v=vs.90).aspx
Upvotes: 2
Views: 1282
Reputation: 71
In 2022 I am in the process of doing the same thing - trying to localize only some resources. Like the author, I am having a very hard time, and the solution has not been found yet. Here's what I can say after reading countless articles on the subject, some making careless statements that cost hours of work just to see that they don't work.
Given : you have an EXE with "default" resources and an extension DLL that replaces only some of then (I specifically tested it on strings so far).
AfxGetResourceHandle()
) points to the EXEGiven this order, if the EXE has the needed resource, it wins. In our task it always does, so the DLL is as good as not loaded - useless.
Some people suggest setting the DLL module as the resource handle (AfxSetResourceHandle
), claming it will reverse the order. DO NOT do it. It will not reverse the order, and here's why.
Doing so will replace the EXE's handle by the DLLs, that's all. It's a one liner in the MFC source. Now the search process will go as follows:
AfxGetResourceHandle()
) now points to the DLLThis is because extension DLLs are added to a search linked list when loaded, but the EXE (I should say the resource handle that defaults to the EXE) is not! It is handled separately upfront. Replacing the resource handle does nothing to the linked list.
Setting the resource handle to the DLL makes EXE-based resources unavailable, so you end up with only localized resources, and now your program will most likely not start up.
I don't see a way (yet) to add the EXE to this list. The type required is (roughly) "extension DLL", you can't just do a new CDynLinkLibrary
call.
What I am thinking is separating out even the default resources into a DLL. That way both DLLs become equal and get added to the search list. The precedence will, I suppose, be governed by the LoadLirary
call order, so if you first load the localized DLL and then the "main", you should be OK.
P.S. Creating resources in two languages inside the EXE, while doable, has proven to be a nightmare - it has never worked the way you'd want: first the selected UI language (see SetThreadUILanguage
) then, if not found, the main in any language available. Comments on StackOverflow say "make DLLs, it's the only way" without going into details why.
Upvotes: 1
Reputation: 4590
Default resources (including dialogs) are governed by the current setting of the resource handle. You can set the resource handle at any time using AfxSetResourceHandle to point to a resource only dll, or, your executable. You'll need to be prudent in making sure that the handle is pointing to the correct resource location before instantiating a dialog, so, you'll need to save the current handle before changing it to a new resource handle, and, changing it back when you are done.
Upvotes: 2
Reputation: 2937
Pass a template ID to the constructor of your dialogs.
Constructor:
CTestDlg::CTestDlg(int Template, CWnd* pParent = NULL) : CDialog(Template)
{
...
Call:
int idd;
if (bTestDialogTranslationExists)
idd = IDD_TESTDIALOG_FROM_DLL;
else
idd = IDD_TESTDIALOG;
CTestDialog dlg(idd);
dlg.DoModal();
This code is for demonstration only. You might want to derive a new class from CDialog that handles localized dialog templates automatically, depending on, for example, if the Template exists in a replacement map.
// have this as a global variable or in your CWinApp class
CMap <int, int, int, int> translatedDialogsMap;
// put this maybe in you CWinApp::InitInstance before any dialog could possibly be displayed
translatedDialogsMap[IDD_TESTDIALOG] = IDD_TESTDIALOG_FROM_DLL;
translatedDialogsMap[IDD_TESTDIALOG2] = IDD_TESTDIALOG2_FROM_DLL;
translatedDialogsMap[IDD_TESTDIALOG3] = IDD_TESTDIALOG3_FROM_DLL;
...
// check in your dialog subclass constructor code, if a mapped dll dialog exists
int idd_replaced;
if (translatedDialogsMap.Lookup(Template, &idd_replaced))
Template = idd_replaced;
Upvotes: 1