15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


CString::LoadString and Console Application

Gerry Sweeney -- gerry@hornbill.com
Tuesday, August 13, 1996


Environment: VC++ 4.1, Win NT3.51, 95

Dear listers,

I have a Win32 console application that I would like to use the 
CString::LoadString member to get my strings from the string table resource. 
It works fine but I get an from the MFC library. I believe this is because I 
am not using a CWinApp derived class I have no instance handle that MFC can 
find.

The asset is in AFXWIN1.INL at line 19
ASSERT(afxCurrentInstanceHandle != NULL)

Because this is a console application I do not have a WinMain(or do I?) so I 
can't get the instance handle from there. The only other idea I had was to 
have my string table in a resource only DLL and use the HINSTANCE from the 
LoadLibrary call, but this seems to be a bit long winded.


Questions?

Am I on the right track?
How do you get a HINSTANCE for a Win32 console application?


Any help would be much appreciated.


Gerry Sweeney
Hornbill Systems Ltd.



Pete Chestna -- pchestna@highground.com
Thursday, August 15, 1996

[Mini-digest: 7 responses]

If I understand your question, you have resources in your local DLL and you
want to load them from there.  One quick solution is to set the
afxCurrentInstanceHandle to the hInstance passed to you in DLLMain.  Since
you have no application, this should work.

Pete

At 07:09 PM 8/13/96 G, you wrote:
>
>Environment: VC++ 4.1, Win NT3.51, 95
>
>Dear listers,
>
>I have a Win32 console application that I would like to use the 
>CString::LoadString member to get my strings from the string table resource. 
>It works fine but I get an from the MFC library. I believe this is because I 
>am not using a CWinApp derived class I have no instance handle that MFC can 
>find.
>
>The asset is in AFXWIN1.INL at line 19
>ASSERT(afxCurrentInstanceHandle != NULL)
>
>Because this is a console application I do not have a WinMain(or do I?) so I 
>can't get the instance handle from there. The only other idea I had was to 
>have my string table in a resource only DLL and use the HINSTANCE from the 
>LoadLibrary call, but this seems to be a bit long winded.
>
>
>Questions?
>
>Am I on the right track?
>How do you get a HINSTANCE for a Win32 console application?
>
>
>Any help would be much appreciated.
>
>
>Gerry Sweeney
>Hornbill Systems Ltd.
>
---
"To you -- is it movement or is it action?       Peter J. Chestna
 It is contact or just reaction?                 HighGround Systems
 And you -- revolution or just resistance?       PChestna@highground.com
 Is it living, or just existence?" - RUSH        (508) 263-5588 x.125

-----From: Ian Brown 



Hi.

You are quite right to suspect that you do not have a WinMain etc. or 
indeed an HInstance (I believe).

A Console app is just that, not a Windows app - it would probably run very 
happily under a non-GUI/full-screen-only version of NT is such a beastie 
existed. Perhaps it should, and then we could call it DOS-32 :-)

Your idea of a resource DLL sounds promising, although I cannot claim to have 
tried it personally. The other option may be to create your own CWinApp instance
and use that (oh yes you can...).

I do recommend a look at 'The Revolutionary Guide to MFC 4 Programming' (Wrox 
Press, Mike Blaszczak). It has a good appendix on Console apps and is the source
for my reply.

Good luck...

Ian

-----From: Mike Blaszczak 

At 07:09 PM 8/13/96 G, Gerry Sweeney wrote:
>Environment: VC++ 4.1, Win NT3.51, 95

>I have a Win32 console application that I would like to use the 
>CString::LoadString member to get my strings from the string table resource. 
>It works fine but I get an from the MFC library. I believe this is because I 
>am not using a CWinApp derived class I have no instance handle that MFC can 
>find.

Yes, that's the problem. In other words, you're trying to use MFC without
properly initializing it first.

>Because this is a console application I do not have a WinMain(or do I?) so I 
>can't get the instance handle from there.

Console applications have main(), not WinMain().  You're right, you can't
get the instance handle of your own self from main()--but there are other
ways.

>The only other idea I had was to 
>have my string table in a resource only DLL and use the HINSTANCE from the 
>LoadLibrary call, but this seems to be a bit long winded.

You just need to load MFC properly.  This is covered in my book.  It
is also covered in article number  Q150764, which I found by searching
for "MFC Console" in the "Visual C++" Knowledge Base at
http://www.microsoft.com/kb.

You can do this:

#include 
#include 

CWinApp theApp; // you don't even have to derive your own

int main()
{
   if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
      return -1; // you're in serious trouble

   // now, everything works, including:

   CString str;
   str.LoadResource(37);
   printf("String number thirty seven is: %s\n", (LPCTSTR) str);
   return 0;
}

>How do you get a HINSTANCE for a Win32 console application?

By calling ::GetModuleHandle(NULL).


.B ekiM
http://www.nwlink.com/~mikeblas   <--- trip report central
1995 Honda VFR750F (Serial number 00050!)
1987 Yamaha FZ700 (damaged)                AMA, HRC, VFROC
These words are my own: I do not speak for Microsoft.

-----From: "Peter Jones" 

     There are some interesting MSDN hits on this issue:
     
     One article appears to be by calling GetModuleHandle which should be 
     the same an the instance handle. 
     
     The other, more kludgy way, is to obtain the window handle of the 
     console app and then get module handle from there.
     
     Hope this helps.
     
     Pj.
     
     Articles are included below:
     
  
PSS ID Number: Q150764
Article last modified on 05-16-1996
 
2.00 2.10 2.20 4.00 4.10
 
WINDOWS NT
 

--------------------------------------------------------------------------
The information in this article applies to:
 
 - The Microsoft Foundation Classes (MFC), included with:
   Microsoft Visual C++, 32-bit Edition, versions 2.0, 2.1, 2.2, 4.0, 4.1
--------------------------------------------------------------------------
 
SYMPTOMS
========
 
CString::LoadString() generates an MFC assert in debug mode console
applications or fails in release mode console applications.
 
CAUSE
=====
 
CString::LoadString() relies on MFC global resource handles that are
initialized in AfxWinMain(), which is only called in MFC GUI-based
applications.
 
RESOLUTION
==========
 
The resource string is loaded using the Win32 API ::LoadString() function.
This method does not require any extra MFC initialization.
 
Example #1 below uses this method.
 
It is possible to initialize the MFC global resource handles by calling the
undocumented AfxWinInit() function. In this case, it is advisable to
declare an instance of a CWinApp class.
 
Example #2 below demonstrates this method.
 
STATUS
======
 
This behavior is by design.
 
WORKAROUND
==========
 
Sample Code - Example #1
------------------------
 
   /* Compile options needed: /MT
   */
   #include 
   #include 
   #include 
 
   #define IDS_HELLO 1
 
   BOOL LoadStringResource(CString &cszString, UINT nID)
   {
       int nSize = 0;
       int nLen = -1;
 
       cszString.Empty();
 
       //Keep looping until we have the whole string
       while ((nLen != 0) && (nLen == nSize - 1))
       {
           //Grow buffer by 256 bytes
           nSize += 256;
 
           //Load String Resource
           nLen = ::LoadString( GetModuleHandle(NULL), nID,
                                cszString.GetBuffer(nSize-1), nSize);
       }
       cszString.ReleaseBuffer();
 
       return (BOOL) nLen;
   }
 
   void main(void)
   {
       AfxInitialize();
 
       CString cszString;
 
       // IDS_HELLO is a string resource attached to the console
   application
       if (LoadStringResource(cszString, IDS_HELLO))
           printf("The string was loaded\n%s\n", (LPCTSTR) cszString);
       else
           printf("Error loading string");
 
       getch();
   }
 
Sample Code - Example #2
------------------------
 
   /* Compile options needed: /MT
   */
   #include 
   #include 
   #include 
 
   #define IDS_HELLO 1
 
   CWinApp theApp;
 
   void main()
   {
       if (!AfxWinInit(GetModuleHandle(NULL), NULL,
           ::GetCommandLine(), 0))
       {
           printf("Couldn't initialize MFC!\n");
           return;
       }
 
       CString cszString;
 
       // IDS_HELLO is a string resource attached to the console
   application
       if (cszString.LoadString(IDS_HELLO))
           printf("The string was loaded\n%s\n", (LPCTSTR) cszString);
       else
           printf("Error loading string");
 
       getch();
   }
 
Additional reference words: 2.00 2.10 2.20 4.00 4.10
KBCategory: kbprg kbprb
KBSubcategory: MfcMisc
 
=============================================================================
Copyright Microsoft Corporation 1996.

----------------------------------------------------------------------------

  
PSS ID Number: Q124103
Article last modified on 09-29-1995
 
3.10 3.50  | 4.00
 
WINDOWS NT | WINDOWS
 

-------------------------------------------------------------------------
The information in this article applies to:
 
 - Microsoft Win32 Application Programming Interface (API) included with:
 
    - Microsoft Windows NT versions 3.1 and 3.5
    - Microsoft Windows 95 version 4.0
-------------------------------------------------------------------------
 
SUMMARY
=======
 
It may be useful to manipulate a window associated with a console
application. The Win32 API provides no direct method for obtaining the
window handle associated with a console application. However, you can
obtain the window handle by calling FindWindow(). This function retrieves a
window handle based on a class name or window name.
 
Call GetConsoleTitle() to determine the current console title. Then supply
the current console title to FindWindow().
 
MORE INFORMATION
================
 
Because multiple windows may have the same title, you should change the
current console window title to a unique title. This will help prevent the
wrong window handle from being returned. Use SetConsoleTitle() to change
the current console window title. Here is the process:
 
1. Call GetConsoleTitle() to save the current console window title.
 
2. Call SetConsoleTitle() to change the console title to a unique title.
 
3. Call Sleep(40) to ensure the window title was updated.
 
4. Call FindWindow(NULL, uniquetitle), to obtain the HWND
   this call returns the HWND -- or NULL if the operation failed.
 
5. Call SetConsoleTitle() with the value retrieved from step 1, to
   restore the original window title.
 
You should test the resulting HWND. For example, you can test to see if the
returned HWND corresponds with the current process by calling
GetWindowText() on the HWND and comparing the result with
GetConsoleTitle().
 
The resulting HWND is not guaranteed to be suitable for all window handle
operations.
 
Sample Code
-----------
 
The following function retrieves the current console application window
handle (HWND). If the function succeeds, the return value is the handle of
the console window. If the function fails, the return value is NULL. Some
error checking is omitted, for brevity.
 
HWND GetConsoleHwnd(void)
{
    #define MY_BUFSIZE 1024 // buffer size for console window titles
    HWND hwndFound;         // this is what is returned to the caller
    char pszNewWindowTitle[MY_BUFSIZE]; // contains fabricated WindowTitle
    char pszOldWindowTitle[MY_BUFSIZE]; // contains original WindowTitle
 
    // fetch current window title
 
    GetConsoleTitle(pszOldWindowTitle, MY_BUFSIZE);
 
    // format a "unique" NewWindowTitle
 
    wsprintf(pszNewWindowTitle,"%d/%d",
                GetTickCount(),
                GetCurrentProcessId());
 
    // change current window title
 
    SetConsoleTitle(pszNewWindowTitle);
 
    // ensure window title has been updated
 
    Sleep(40);
 
    // look for NewWindowTitle
 
    hwndFound=FindWindow(NULL, pszNewWindowTitle);
 
    // restore original window title
 
    SetConsoleTitle(pszOldWindowTitle);
 
    return(hwndFound);
}
 
Additional reference words: 3.10 3.50 4.00 95
KBCategory: kbui kbcode
KBSubcategory: BseCon UsrWndw
=============================================================================
Copyright Microsoft Corporation 1995.


 

 
-----From: Tim Peacock 


Have you tried ::GetModuleHandle()?  May be used same as handle returned by 
::LoadLibrary().

								tim.
-----From: "Eric Kenslow" 

Here's how I solved this problem.  Place this either in your main() or
somewhere else very near the beginning of your program:

    // Set our resource handle
    ::AfxSetResourceHandle( HINSTANCE( ::GetModuleHandle( NULL ) ) );
    afxCurrentInstanceHandle = HINSTANCE( ::GetCurrentProcess() );

Happily, after this your console app can use pretty much all of MFC's
services.

/* Eric Kenslow - Digital Lighthouse Inc.
 * webmaster@digilight.com
 * http://www.digilight.com
 */
-----From: Tim Hagemann 

MFC handles "global" variables such as the hInstance of the current =
module in special structures called module states, which are initialized =
in CWinApp - so bad luck for LoadString...

You should write your own version of LoadString:

int AFXAPI AfxLoadStringEx(HINSTANCE hI,UINT nID, LPTSTR lpszBuf, UINT =
nMaxBuf)
{
	ASSERT(AfxIsValidAddress(lpszBuf, nMaxBuf*sizeof(TCHAR)));
#ifdef _DEBUG
	// LoadString without annoying warning from the Debug kernel if the
	//  segment containing the string is not present
	if (::FindResource(hI,
	   MAKEINTRESOURCE((nID>>4)+1), RT_STRING) =3D=3D NULL)
	{
		lpszBuf[0] =3D '\0';
		return 0; // not found
	}
#endif //_DEBUG
	int nLen =3D ::LoadString(hI, nID, lpszBuf, nMaxBuf);
	if (nLen =3D=3D 0)
		lpszBuf[0] =3D '\0';
	return nLen;
}

BOOL CStringEx::LoadString(HINSTANCE hI,UINT nID)
{
	// try fixed buffer first (to avoid wasting space in the heap)
	TCHAR szTemp[256];
	int nLen =3D AfxLoadStringEx(hI,nID, szTemp, _countof(szTemp));
	if (_countof(szTemp) - nLen > CHAR_FUDGE)
	{
		*this =3D szTemp;
		return nLen > 0;
	}

	// try buffer size of 512, then larger size until entire string is =
retrieved
	int nSize =3D 256;
	do
	{
		nSize +=3D 256;
		nLen =3D AfxLoadStringEx(hI,nID, GetBuffer(nSize-1), nSize);
	} while (nSize - nLen <=3D CHAR_FUDGE);
	ReleaseBuffer();

	return nLen > 0;
}

Note that the code above has not been tested, I grabbed it from the =
MFC-sources and modified it on the fly.

Tim Hagemann
ifa informationssysteme



Mike Blaszczak -- mikeblas@nwlink.com
Friday, August 16, 1996

At 08:53 AM 8/15/96 -0400, you wrote:
>-----From: Ian Brown 

>You are quite right to suspect that you do not have a WinMain etc. or 
>indeed an HInstance (I believe).

>A Console app is just that, not a Windows app - it would probably run very 
>happily under a non-GUI/full-screen-only version of NT is such a beastie 
>existed. Perhaps it should, and then we could call it DOS-32 :-)

Actually, both a console app and a GUI app are Windows applications.  They
can make full use of the Win32 API.  Console applications have some special
considrations because theire architecture isn't always event-driven: depending
on what you want to do with your console application, you might need to 
write a message loop, for example.

The difference between a GUI app and a Console app is the subsystem it runs
under: read up on the /SUBSYSTEM option in the linker to see what this is
about.

To be successful at console app programming in Windows, you have to drop lots
of preconceptions (eg, "everything I need to run I get from parameters
to main()") and think carefully about the Windows architecture.

>The other option may be to create your own CWinApp instance
>and use that (oh yes you can...).

I think this is the most appropriate solution.  Since you're using MFC,
you should have a CWinApp.

>I do recommend a look at 'The Revolutionary Guide to MFC 4 Programming'
>(Wrox Press, Mike Blaszczak).

That guy is nothing but a loud-mouth drunk who has too big of a monitor
for his own good.

-----From: "Peter Jones" 

>     The other, more kludgy way, is to obtain the window handle of the 
>     console app and then get module handle from there.

You can't do this. Not reliably or easily, anyway--and if you invent
a hack to get your console window handle, and it'll probably break in
some later version of Windows.

It's far more reliable to call ::GetModuleHandle() 

>     Articles are included below:

Maybe it's better to just quote Q numbers--people can get them from
three or four different places... _if_ they want them.
 
>-----From: "Eric Kenslow" 
>
>Here's how I solved this problem.  Place this either in your main() or
>somewhere else very near the beginning of your program:

>    // Set our resource handle
>    ::AfxSetResourceHandle( HINSTANCE( ::GetModuleHandle( NULL ) ) );
>    afxCurrentInstanceHandle = HINSTANCE( ::GetCurrentProcess() );

>Happily, after this your console app can use pretty much all of MFC's
>services.

Actually, you'll likely break--depending on which "services" of MFC
you use. There are _lots_ of other things that CWinApp initializes for
MFC that you've not initialized in this one call.  If you run off and
try to control some OLE automation server, or create a CDatabase object,
you'll probably start tossing ASSERTs.

>-----From: Tim Hagemann 

>MFC handles "global" variables such as the hInstance of the current 
>module in special structures called module states, which are initialized 
>in CWinApp - so bad luck for LoadString...

>You should write your own version of LoadString:

It's far easier and safer to create a CWinApp instance and get it
initialized appropriately.

.B ekiM
http://www.nwlink.com/~mikeblas/
These words are my own. I do not speak on behalf of Microsoft.




Gerry Sweeney -- gerry@hornbill.com
Monday, August 19, 1996


Environment: VC++ 4.1, Win NT3.51, 95

Dear listers,

I recently asked a question about CString::LoadString inside a Win32 console 
application. Just in summary, I was unable to use CString::LoadString. The 
MFC was asserting. I tried making a call to 
AfxSetResourceHandle(GetModuleHandle(NULL)) but this its self would assert. 
and CString::LoadString would still not work. To make this work I have 
implemented my own 'LoadString' function as follows:-

extern "C" void ServerLoadString(UINT nID, CString& szString)
{
     char strBuff[256];
     strBuff[0] = 0;
     ::LoadString(NULL, nID, strBuff, 256);
     szString = strBuff;
}

It seems that passing null as the first parameter to 'LoadString' uses the 
resources found in the default module, The same as 'GetModuleHandle'. This 
now works fine for both the Service and the GUI build.

I hope this will be usefull to some one else in the future

Gerry Sweeney
Hornbill Systems Ltd.




| Вернуться в корень Архива |