Thread: String as Resource in txt file

  1. #1
    Registered User KeithS's Avatar
    Join Date
    Jul 2009
    Location
    Colombia
    Posts
    21

    String as Resource in txt file

    Hi to everyone. First post.

    WIN API fan, and have been "lurking" for a while, getting most questions answered on MSDN or indirectly here. Not a programmer by profession, but need it every once in a while for my work, & besides it is a cool hobby. Been programming in various forms of BASIC on various platforms since 1985, some "C" practice over 10 years ago, recent convertee to C++. Okay, enough blurb...

    I have finally been trumped with this one. Could someone offer some help, please? How do you access the string in a resource *.txt file? Here's the abreviated code snippet first, description of my trouble shoot after..

    Code:
    // Snippet applicable declarations...
    LPCTSTR ClsName = "Basic_Window";
    TCHAR szBuffer[250];
    LPSTR outBuffer;
     
    LRESULT CALLBACK WndProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
    {
     switch(Msg)
     {
     // Not applicable code removed....
       case WM_CREATE:
      { 
       UINT_PTR ret;
       BITMAP bm;
    //  Etcetera....
     
       LoadString(GetModuleHandle(NULL), IDS_STRING, szBuffer, 0);       /*sizeof(TCHAR));*/
     
     FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER, szBuffer, IDS_STRING, NULL, outBuffer, NULL, NULL);
      }
      break;
     case WM_PAINT:
      {
       // removed for post... 
      }
      break;
     
     }
     return 0;
    }
     
    void DrawBebop(HDC hdc, RECT* prc)
    {
     
     // This version of the drawing integrates double-buffering
     HDC hDcBuffer = CreateCompatibleDC(hdc);
     HBITMAP hbmBuffer = CreateCompatibleBitmap(hdc, prc->right, prc->bottom);
     HBITMAP hbmOldBuffer;
     
    // Not applicable code removed...
     
     //TextOut(hDcBuffer, 100, 100, "TESTING", 7);  // This works....
     BitBlt(hDcBuffer, (BmpPos.bx - Ofst), (BmpPos.by - Ofst), BmpPos.width, BmpPos.height, hDcMem, 0, 0, SRCAND);
     SelectObject(hDcMem, g_hbmBebop);
     BitBlt(hDcBuffer, (BmpPos.bx - Ofst), (BmpPos.by - Ofst), BmpPos.width, BmpPos.height, hDcMem, 0, 0, SRCPAINT);
     
    DrawText(hDcBuffer, outBuffer, -1, prc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);// This shows nothing...
     //DrawText(hDcBuffer, "Hi There!", -1, prc, DT_SINGLELINE | DT_CENTER | DT_VCENTER); //  This works...
    //DrawText(hDcBuffer, ClsName, -1, prc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);  // And this works... 
     
     // ^^^^
    // Hmmm. Normal, interpreted as Null Terminated ("**"), strings prints no problem, as does
    // a standard char array buffer. The question is, then, how do you get a STRING
    // resource to format as char? FormatMessage does not seem to do the trick.
    // Or I am not using it correctly. There is only
    // some stuff in Japanese about it on MSDN, so
    // I have no reference....
    //
    // Just a note, the STRING resource, that comes from a *.txt file, is NOT stored with the
    // *.exe as letters, but apparently as its character (scan?) codes. Ie; non hexadecimal numbers.
    // I am wondering if the conversion to characters is required in a separate function, even though
    // a scant, indirect reference seems to promise that this
    // is not needed when using FormatMessage() function.
    //
    // So, my doubt: Is the way I am embedding a STRING resource the right way (ie; as a txt file)?
    // Is there another way? Is this possibly the problem?
     
     BitBlt(hdc, 0, 0, prc->right, prc->bottom, hDcBuffer, 0, 0, SRCCOPY);
     
     SelectObject(hDcMem, hbmOld);
     DeleteDC(hDcMem);
     SelectObject(hDcBuffer, hbmOldBuffer);
     DeleteDC(hDcBuffer);
     DeleteObject(hbmBuffer);
     }
    What I have already covered (please note the comments in the code, too).

    The txt file that contains the string embeds correctly. I have tried the string with and without "" quotation marks around it. The compiler setup is correct, or it would not be printing the NULL terminated char array. Various string typecastings of the LoadString() or the char Buffers does not produce any results either. The string WILL load and display properly from the txt file, if I donīt embed it, and access it through an IOstream. Resource.h and TestQuiz.rc files are working perfectly, too, as the bitmaps and icons load okay (I have tried STRING and RT_STRING already, as resource types in the *.rc file, same results). I think MAKEINTRESOURCE is not applicable to this case, as the string is already loaded with its resource ID (in LoadString). Tried it anyway, did not work.

    So, I believe this narrows it down to a character access problem, as described, or a LoadString problem. Something in the formatting of the embedded resource?

    My thanks in advance,

    KeithS
    Last edited by KeithS; 08-19-2009 at 12:29 PM.

  2. #2
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    Code:
    LoadString(GetModuleHandle(NULL), IDS_STRING, szBuffer, 0);
    Shouldn't that be:
    Code:
    LoadString(GetModuleHandle(NULL), IDS_STRING, szBuffer, sizeof(szBuffer) / sizeof(TCHAR));
    bit∙hub [bit-huhb] n. A source and destination for information.

  3. #3
    Registered User KeithS's Avatar
    Join Date
    Jul 2009
    Location
    Colombia
    Posts
    21
    Quote Originally Posted by bithub View Post
    Code:
    LoadString(GetModuleHandle(NULL), IDS_STRING, szBuffer, 0);
    Shouldn't that be:
    Code:
    LoadString(GetModuleHandle(NULL), IDS_STRING, szBuffer, sizeof(szBuffer) / sizeof(TCHAR));
    That makes perfect sense... now that you've pointed it out. Thanks!

  4. #4
    Registered User KeithS's Avatar
    Join Date
    Jul 2009
    Location
    Colombia
    Posts
    21
    Quote Originally Posted by KeithS View Post
    That makes perfect sense... now that you've pointed it out. Thanks!
    But it was not it, unfortunately.

    However, I am a bit clearer on what objective I am after now. It is like this;

    1). Embed a text file as a resource.
    2). Access text file resource from the application.
    3). Format its contents into char strings.

    I tried using "CreateFile" with "GENERIC_READ", and attempting to "TEXT(MAKEINTRESOURCE)" of the text file resource. No luck. String tables in the *.rc file I can do no problem, but it is not appropriate to the goal. The eventual application I am doing this for will have quite a volume of text (it is a question bank) and needs to be updated about every six months, plus the text file must not be accessible to users.

    Is what I am trying to do possible?

    There must be alternate options. If anyone knows one, or has had a similar experience to which they found a solution, please let me know. Thanks in advance...

  5. #5
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    You use FindResource, LoadResource and LockResource to get a pointer to the start of your embedded file. Treat that as a big ol' array of (w)char as appropriate with a byte size of what SizeofResource() returns.

  6. #6
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Quote Originally Posted by KeithS View Post
    The eventual application I am doing this for will have quite a volume of text (it is a question bank) and needs to be updated about every six months, plus the text file must not be accessible to users.
    Embedding the file in the exe will require redistrobuting the exe on every update.

    Also opening the exe in a hex editor (UltraEdit, MSVC etc) will display the text in a readable form (as it is stored in congruent memory).

    These may not be issues for your app however.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  7. #7
    Registered User KeithS's Avatar
    Join Date
    Jul 2009
    Location
    Colombia
    Posts
    21
    Quote Originally Posted by adeyblue View Post
    You use FindResource, LoadResource and LockResource to get a pointer to the start of your embedded file. Treat that as a big ol' array of (w)char as appropriate with a byte size of what SizeofResource() returns.
    That did produce some positive results. The behaviour of that pointer made by LockResource seems to be a bit unconventional(?), but I seem to be getting around it, slowly. The main thing is I can get the access I was looking for. Thank you!

    A question, something off the top of my head just this moment. Is there any known incompatibility issue >-Edit: I mean, security issue -< with the use the string class in these operations? (have not tried that yet, honestly).

    Embedding the file in the exe will require redistrobuting the exe on every update.

    Also opening the exe in a hex editor (UltraEdit, MSVC etc) will display the text in a readable form (as it is stored in congruent memory).

    These may not be issues for your app however.
    It would be a problem if it was for distribution out-house, but it will be for internal training purposes in the company only. I am not too worried if they can see the question bank. I would qualify it as initiative if they figure out the means to peek , just so long as they can't alter it. But thanks for the consideration, just the same!
    Last edited by KeithS; 08-21-2009 at 02:08 PM.

  8. #8
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    Quote Originally Posted by KeithS View Post
    That did produce some positive results. The behaviour of that pointer made by LockResource seems to be a bit unconventional(?), but I seem to be getting around it, slowly. The main thing is I can get the access I was looking for. Thank you!
    It's just a normal void pointer, Windows has no idea what type of data is contained in your resource or who's consuming it so it produces a generic pointer which somebody with more knowledge of what the data represents can mould appropriately.

    A question, something off the top of my head just this moment. Is there any known incompatibility issue >-Edit: I mean, security issue -< with the use the string class in these operations? (have not tried that yet, honestly).
    Depends on which string class you mean but as long as you or it doesn't try to write to anything that the LockResource() pointer points to it'll be fine. You must use a range or element count constructor if the string class provides one (or memcpy the data into reserved space if it doesn't), as there's no guarantee that the string data in the resource is nul terminated.

    For std::string that'd be:
    Code:
    ULONG resDataSize = SizeOfResource(...);
    char* resData = static_cast<char*>(LockResource(...));
    // range
    std::string resDataAsString(resData, resData + resDataSize);
    // or count
    std::string resDataAsString(resData, resDataSize);
    // or memcpy to reserved space
    std::string resDataAsString(resDataSize);
    memcpy(&resDataAsString[0], resData, resDataSize);
    Last edited by adeyblue; 08-21-2009 at 04:16 PM.

  9. #9
    Registered User KeithS's Avatar
    Join Date
    Jul 2009
    Location
    Colombia
    Posts
    21
    Quote Originally Posted by adeyblue View Post
    It's just a normal void pointer, Windows has no idea what type of data is contained in your resource or who's consuming it so it produces a generic pointer which somebody with more knowledge of what the data represents can mould appropriately.


    Depends on which string class you mean but as long as you or it doesn't try to write to anything that the LockResource() pointer points to it'll be fine. You must use a range or element count constructor if the string class provides one (or memcpy the data into reserved space if it doesn't), as there's no guarantee that the string data in the resource is nul terminated.

    For std::string that'd be:
    Code:
    ULONG resDataSize = SizeOfResource(...);
    char* resData = static_cast<char*>(LockResource(...));
    // range
    std::string resDataAsString(resData, resData + resDataSize);
    // or count
    std::string resDataAsString(resData, resDataSize);
    // or memcpy to reserved space
    std::string resDataAsString(resDataSize);
    memcpy(&resDataAsString[0], resData, resDataSize);
    Hi again,

    Yes, it does appear to be my handling of the pointer is at fault. Now, what I was doing was not far off what is in the snippet above, but a different way...
    Code:
      char* resdata;
      // Etc...
      LPVOID PtrResource = LockResource(...);
      resdata = PtrResource;
    Same thing, yeah? Or maybe not?

    The problem I am having; I can only get the first line of text (NULL terminated string in the resource). The pointer will not look at the other lines. I am attempting to step through the strings like this....
    Code:
      charbuffer = *(resdata+1);
    ...but what it is doing is lopping of as many letters as I add to the pointer. For example, if the strings are;

    "Hello curly lizard\0",
    "Are you happy on your rock?\0",
    "The vultures are coming\0"

    Code:
      *(resdata+3)
    will output

    lo curly lizard

    ...when I was expecting...

    The vultures are coming

    I do not have problems reading actual text files, but the method is different, after all. Still, I will try what you recommend. My thanks again.

  10. #10
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    When you declare a pointer the complier assumes a size, char in this case.

    So adding 3 to the pointer will move the size of 3 chars, not to the 3rd string.

    Look for the null terminators, something like (written in code window, not compiled let alone tested.....)

    Code:
    //find size 
    int iRemaining=(int)SizeOfResource(...);
    //get data
    char *pResource = (char*)LockResource(...);
    char * pStart=NULL,*pEnd=NULL;//line pointers
    char szTemp[MAX_PATH]={0}; //temp string buffer 
    
    //set the start pointer to the begining of the first string
    pStart=pResource;
    //find the first term (prime loop)
    pEnd=strchr(pStart,'\0'); 
    while(pEnd && iRemaining>0) //found another string
    {
    	//print in the part of the string we need 
    	_snprintf(szTemp, pEnd-pStart ,"%s", pStart);
    
    	//output string or copy to string array
    
    	//move past terminator
    	pEnd++;
    	//increment our counters
    	iRemaining-=pEnd-pStart;
    
    EDIT: realised this is a bug, can look after end of string, this is not an elegant solution
    
    	//if data left
    	if(iRemaining>0)
    	{
    		//find next string
    		pStart=pEnd;
    		pEnd=strchr(pStart,'\0'); 
    	}
    	else pEnd=NULL;//leave loop
    }
    Last edited by novacain; 08-24-2009 at 10:25 PM.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  11. #11
    Registered User KeithS's Avatar
    Join Date
    Jul 2009
    Location
    Colombia
    Posts
    21
    Quote Originally Posted by novacain View Post
    When you declare a pointer the complier assumes a size, char in this case.

    So adding 3 to the pointer will move the size of 3 chars, not to the 3rd string.

    Look for the null terminators, something like (written in code window, not compiled let alone tested.....)
    Yes, that is what was confusing me; why the char pointer was outputting the whole string without any need to step through and retrieve each character, including looking for the NULL terminator. Bit of a mix up, it seems, between char pointers, char strings, and string class, on my part, of course. It is a clearer now (the snippet above is the sort of thing I expected to have to do originally, and led me to make the comment about the "unconventional" nature of the void pointer in the first place when it acted differently to the expected), I see how it should be done with the example.

    Just the same I am going to put myself through a "back to basics" session with these topics to consolidate each and uncross some cables that appear to have become crossed.....

    Again, my thanks!

  12. #12
    Registered User KeithS's Avatar
    Join Date
    Jul 2009
    Location
    Colombia
    Posts
    21
    Hi once again, some continued bother, please....

    It seems there are some basic issues in there between C++ and WINAPI string handling, and it has forced me backwards a bit, instead of allowing progress forward with this. Frankly I think it is a minor issue, and my questions are in the code comments. If someone is kind enough to shed some light on the matter I would be grateful, my thanks in advance.

    Please note, I even attempted use of StringSafe to solve the problem, but met with only the same....

    Code:
    LRESULT CALLBACK WndProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
    {
    	switch(Msg)
    	{
    	case WM_DESTROY:
    		PostQuitMessage(WM_QUIT);
    		break;
    	case WM_PAINT:
    		{
    			PAINTSTRUCT ps;
    			RECT rcClient;
    			HDC hdc = BeginPaint(hWnd, &ps);
    			GetClientRect(hWnd, &rcClient);
    			char text[200]; // = "I am a string";
    			//	char text[] = "I am a string";
    			//char* pttext = text;
    			//strcpy(text, "I am a string"); 
    
    /* It is a string literal, so why does it not read the end of the string?
    Why does it read the undetermined blocks too, if it is implicitly NULL terminated?*/
    
    			StringCchCopy(text, sizeof(text), "I am a string"); // same here...
    
    			DrawText(hdc, text, sizeof(text), 
    				// only works with -1 as this parametrer...
    				// /sizeof(char)-1 (not needed, it is only one byte each char, anyway 
    				&rcClient, DT_LEFT);
    			
    			EndPaint(hWnd, &ps);
    			ReleaseDC(hWnd, hdc);
    			DeleteDC(hdc);
    		}
    		break;
    
    	default:
    		return DefWindowProc(hWnd, Msg, wParam, lParam);
    	}
    	return 0;
    }
    
    
    /* I cannot get it to read the NULL terminated string
    in a fixed size char array, and I cannot see how to set the array size to the
    string size before actually reading the string into the fixed size buffer.
    A real catch 22 case....
    It reads the WHOLE buffer, and prints out a load of "||||||" after the string.
    Loads of experiments have been supressed out of the code for clarity, as they
    all produce the same result. The only comment left in solves the problem.
    ie; an undetermined string length char array assigned the string literal 
    directly, as in char text = "I am a string", but then even the sizeof in
    DrawText needs a -1 to stop it from printing at least one "|" at the end.
    
    What is different from C++ in WINAPI? Why is it doing this?
    None of this happens when using cout or sprintf in C++....*/
    
    /* Also, how exactly does StringCchLength() replace strlen()? Its second parameter requires
    a pointer to precisely what you are trying to FIND with it.  
    */
    
    /* the crux of the matter is I need the char array to be undetermined until the program
    reads the resource file. Specifying HUGE char arrays before the program reads the file
    seems grossly memory inefficient. Or is it just me being finicky?
    
    Now, the extern definition prefix seems interesting, as does the volatile, but 
    I am not at all sure it is what I am after. There is also the vector, but again, I am unsure 
    (a). that it suits the requirement, as I cannot get it to work and (b). I am not sure it agrees 
    with WINAPI, as there seems to be several C++ things that do not.
    */
    PS. Also, I cannot get the string class to go hand in hand with WINAPI in all respects;

    string objects will not be pointed to by LPCSTR, and casting is illegal for the type, so DrawText and TextOut are out of the window....

    As said, some basic problems were in there after all....

    Thanks again...

  13. #13
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    /* It is a string literal, so why does it not read the end of the string?
    Why does it read the undetermined blocks too, if it is implicitly NULL terminated?*/
    The same reason the middle bit of this does. See the mistake you made in DrawText?

    Code:
    #include <iostream>
    #include <cstring>
    
    int main()
    {
        char stringBuffer[200];
        strcpy(stringBuffer, "This isn't 200 characters long");
        std::cout << "sizeof(stringBuffer) = " << sizeof(stringBuffer) << '\n';
        std::cout << "strlen(stringBuffer) = " << strlen(stringBuffer) << '\n';
        std::cout << "** Printing " << sizeof(stringBuffer) << " characters **\n";
        std::cout.write(stringBuffer, sizeof(stringBuffer));
        std::cout << std::endl << "** Printing " << strlen(stringBuffer) << " characters **\n";
        std::cout.write(stringBuffer, strlen(stringBuffer)) << std::endl;
    }
    If you're working with something undetermined then instead of strlen you'd use the number you got when you created the something, or the one provided by the creator. For your resource, you'd use the number returned by SizeofResourcer; or if you stuff it in a std::string, string.length().

    Non of the C++ types "agree" with the WinAPI since it's a C based api. They do have functions to provide C-type data though. std::string has string.c_str() to get a char* for things like DrawText, and std::vector has &vector[0] to get a pointer to the data.

  14. #14
    Registered User KeithS's Avatar
    Join Date
    Jul 2009
    Location
    Colombia
    Posts
    21

    Thumbs up

    Hats off....

    Quote Originally Posted by adeyblue View Post
    See the mistake you made in DrawText?
    Certainly do. Works great now (here's one of the two options in action).

    Code:
    case WM_PAINT:
    		{
    			PAINTSTRUCT ps;
    			RECT rcClient;
    			HDC hdc = BeginPaint(hWnd, &ps);
    			GetClientRect(hWnd, &rcClient);
    			
    			string text = "I am a string of C++ String Class being turned into a char*";
    			
    			LPCSTR pt_text = new char [text.size()+1];
    			strcpy ((char *)pt_text, text.c_str());
    			
    			DrawText(hdc, pt_text, strlen(pt_text), &rcClient, DT_LEFT);
    
    			EndPaint(hWnd, &ps);
    			ReleaseDC(hWnd, hdc);
    			DeleteDC(hdc);
    			delete [] pt_text;
    		}
    		break;
    I can get back to the main project now. The key comment was this one...

    Non of the C++ types "agree" with the WinAPI since it's a C based api.
    I did not know that. It is clear why I was having problems.

    Thank you!

  15. #15
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    You do not have to delete/release the HDC returned from BeginPaint(), that is what EndPaint() is for.
    You can remove those lines.

    You also only use one of ReleaseDC or DeleteDC (after you have returned the DC to it's default state).

    If you use GetDC() you must later call ReleaseDC() (and not DeleteDC())
    If you use CreateCompatibleDC() then you must use DeleteDC() (and not ReleaseDC() )


    EDIT:
    I would also drop the mem alloc
    try

    DrawText(hdc, text.c_str(), text.size(), &rcClient, DT_LEFT);


    Just to clarify what adeyblue said.

    Code:
    char szBuffer[MAX_PATH] = (0); //init array to empty
    
    _snprintf(szBuffer,MAX_APTH-1,"1234567890");
    sizeof() returns the total buffer size. [in this case MAX_PATH]

    strlen() or lstrlen() returns the size up to the first null terminator. [in this case 10]
    Last edited by novacain; 08-29-2009 at 02:53 AM.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Basic text file encoder
    By Abda92 in forum C Programming
    Replies: 15
    Last Post: 05-22-2007, 01:19 PM
  2. Replies: 3
    Last Post: 03-04-2005, 02:46 PM
  3. Headers that use each other
    By nickname_changed in forum C++ Programming
    Replies: 7
    Last Post: 10-03-2003, 04:25 AM
  4. Classes inheretance problem...
    By NANO in forum C++ Programming
    Replies: 12
    Last Post: 12-09-2002, 03:23 PM