Dear Charles . . . : Opening a New Recordset and Language-Specific Resources

Charles Steinhardt

This month our ever-curious problem solver fools OnFileNew into opening a recordset and demonstrates the correct technique for writing multilingual applications.
Most of my work involves MDI applications that use Access to write data to permanent storage. I’ve had a problem attempting to get around the File command. I need File Open to allow me to select a single record from a recordset. The File New command would provide me with an empty record that contains only the predefined default values. File Save would need to save the data back to the recordset, if it was the result of a File Open Command, or Append a New Record to the recordset if it was the result of the File New Command.

MFC seems set up to open or create only standard files, not database records, so it puts up the standard File Open/Save Common Dialog and expects a file name to be entered. I would like to define my own Tree Control that displays information based on my recordset.

Can you tell me how to get around this limitation in the library?
Dan Marks, Internet Newsgroups

Dan, if Microsoft had wanted a mechanism to open new recordsets as opposed to files, don’t you think they would’ve implemented this behavior? Well, James “Mr.-Rebel-Without-a-Cause” Dean, maybe we’ll have to devise a method just for you!

Actually, you got me on a sore point! This is the first part of MFC that really tripped me up when I first started learning it. Following these non-intuitive initialization functions can be quite painful when all you want to do is the obvious. What you’re trying to do is very common, and I understand your frustration. On one hand, you need OnFileNew so that the MDI frame will appear; on the other hand, you don’t really want to open a “file” that is of your document type. You want to override the default behavior of OnFileNew. The simplest way is to edit the InitInstance that AppWizard created for you, following these four steps:

BOOL COnFileNewApp::InitInstance()
{
	:

First, comment this local declaration out, create a new variable of this type called m_pDocTemplate, and place it as a member of your class (in the *.H file):

//CMultiDocTemplate* pDocTemplate;

Second, leave the remaining lines, but change any reference to pDocTemplate to m_pDocTemplate:

m_pDocTemplate = new CMultiDocTemplate(
	IDR_ONFILETYPE,
	RUNTIME_CLASS(COnFileNewDoc),
	RUNTIME_CLASS(CChildFrame), // custom MDI child frame
	RUNTIME_CLASS(COnFileNewView));
AddDocTemplate(m_pDocTemplate);

// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
	return FALSE;
m_pMainWnd = pMainFrame;	

Third, comment out the following lines:

// Parse command line for standard shell commands,   
// DDE, file open
//CCommandLineInfo cmdInfo;
//ParseCommandLine(cmdInfo);

// Dispatch commands specified on the command line
// if (!ProcessShellCommand(cmdInfo))
//	return FALSE;

Fourth, add a function call something like this and leave the remaining lines:

GoAheadMakeMyView();

// The main window has been initialized, 
// so show and update it.
	pMainFrame->ShowWindow(m_nCmdShow);
	pMainFrame->UpdateWindow();

	return TRUE;

}

The GoAheadMakeMyView() function should do two things. It should create the default document/mdi/view of the type you created with new in m_pDocTemplate = new CmultiDocTemplate(..), and it should then perform the database processing you requested:

BOOL COnFileNewApp::GoAheadMakeMyView()
{

This is the magic line that creates the doc/mdi/view:

m_pDocTemplate->OpenDocumentFile(NULL);

Perform your database initialization here if you’d like, or create a thread:

	:
	:
return TRUE;
}
Just don’t forget to add error processing in InitInstance() and check for GoAheadMakeMyView()’s return value. That way, you can stop processing if database/recordset handling fails for any reason.
I’m working on an application that has to be compiled in both English and Spanish versions. How can I make changes so that I can have my menus, dialog boxes, and so on in both languages?

Stephen Beck, via MSN

O ye, Chico! Your best bet is to use resource-only DLLs and have your application load them dynamically and use the same IDs in both DLLs. Browse through your hard drive and I’m sure you’ll come across a few similar DLLs for other programs; they’re usually unimaginatively called RESDLL.DLL. These and other important issues relating to the development of multi-language applications are covered in detail in Nadine Kano’s Microsoft Press book, Developing International Software for Windows 95 and NT. I’m not trying to steer you away, it’s just that I know this book will be really beneficial.

Here’s how to make a resource-only DLL:

Create a DLL project. Just use AppWizard to create a DLL project for you.

Add a resource to the DLL project. Assume you’re adding a string table resource. Be sure to highlight the table name, right-click, select Properties in the pop-up menu, and change the resource to the language you’ll be placing in this resource file.

Compile the DLL just as if it were an EXE with resources.

In your main app, call LoadLibrary on the DLL you want to load and use AfxSetResourceHandle to instruct the EXE to look for your resources in the DLL and not in the EXE.

CMyApp::InitInstance() { // one of the first things in the init code HINSTANCE hInst = LoadLibrary(“myres.dll”); if (hInst != NULL) AfxSetResourceHandle(hInst); // other initialization code would follow . . . }

Two other important points: Read the MFC Tech Note “TN057: Localization of MFC Components.” This Tech Note contains important information regarding circumstances such as yours, for example, saying that you must set the linker options to include /NOENTRY. This tells the linker that the DLL has no entry point-a logical conclusion since it has no code.

I’d use a single RESOURCE.H file for my EXE as well as for all the DLLs. One way would be to finish your EXE application first, and place all resources inside the EXE using one of the languages. Then, when everything is working, really screw things up and create the resource-only DLL by moving all the resources from the EXE to this new DLL project, along with a copy of the RESOURCE.H file. Then test the EXE to make sure that it’s loading the DLL properly and that the EXE utilizes all resources properly. Be sure to test every resource, because the compiler and linker won’t detect missing resources and your app will only fail at runtime! If you’re convinced the first DLL is complete, use the first DLL as a model for the remaining language DLLs. If I’ve failed to answer your question, well . . . what can I say other than, Je ne comprends pas, Monsieur!

Have a VC++ programming problem? Send it to “Dear Charles,” Visual C++ Developer, PO Box 72255, Marietta, GA 30007-2255; or visualcdev@pinpub.com. Be sure to include an e-mail address so Charles can contact you if he has additional questions.

An MFC/C++ consultant in New York City, the headquarters for the United Nations, Charles Steinhardt provides advanced Windows system consulting, including troubleshooting, for local and international clients. 70353.2604@compuserve.com.

To find out more about Visual C++ Developer and Pinnacle Publishing, visit their website at: http://www.pinppub.com/vcd/

Note: This is not a Microsoft Corporation website.
Microsoft is not responsible for its content.

This article is reproduced from the April 1997 issue of Visual C++ Developer. Copyright 1997, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. Visual C++ Developer is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call (800) 788-1900 or (206) 251-1900.