c++ loop through registry recursively is slow - c++

Have an annoying problem with my code, probably I am doing something wrong because my Python
implementation is much faster!
C++ implementation problems:
Iterating over "HKEY_CLASSES_ROOT" takes a lot of ram, I assume it's because c++ implementation uses lots of variables. Fixed
It's also slow, much slower than python implantation of the code Fixed
The code even slower when trying to iterate over HKEY_CLASSES_ROOT Fixed
New Questions:
Thanks to Nam Nguyen i understood what causing the leaks in my code, and directly effecting execution time, the code below is the fixed one. how come c++ implementation runs as fast as my python implementation?
C++ implementation:
#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <string>
using namespace std;
#define MAX_KEY_LENGTH 255
int RecurseOpenRegEx(HKEY hkey, string subKey = "",string search = "nothing", DWORD sum = 0)
{
TCHAR csubKey[MAX_KEY_LENGTH];
DWORD nSubKeys = 0;
DWORD pathLength = MAX_PATH;
TCHAR storeKeyName[MAX_KEY_LENGTH];
DWORD keyLength;
HKEY hKey = hkey; //somehow i need to reassign HKEY, otherwise it won't pass it with the function, this is bigger than me tough...
const char * ccsearch = search.c_str();
const char * ccsubKey;
if (subKey != "")
{
ccsubKey = subKey.c_str();
copy(subKey.begin(), subKey.end(),csubKey); //convert string to TCHAR
}
if (RegOpenKeyEx(hkey, ccsubKey, 0, KEY_READ, &hkey) == ERROR_SUCCESS)
{
if (RegQueryInfoKey(hkey, csubKey, &pathLength, NULL,&nSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
{
sum += nSubKeys;
for (DWORD subKeyIndex = 0; subKeyIndex < nSubKeys; subKeyIndex++)
{
keyLength = MAX_KEY_LENGTH;
if (RegEnumKeyEx(hkey, subKeyIndex, storeKeyName, &keyLength, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
{
string sKeyName = storeKeyName; //Convert TCHAR to string explicitly
if (subKey != "")
{
sKeyName = subKey + "\\" + sKeyName;
}
sum += RecurseOpenRegEx(hKey, sKeyName);
}
}
}
}
RegCloseKey(hkey); //Now closing the right key
return sum;
}
int main()
{
cout << "sum of all keys: " << RecurseOpenRegEx(HKEY_LOCAL_MACHINE);
return 0;
}
Python implementation:
import winreg
def recurseRegistrySearch(key, keySearch = "",subkey = "", subkeypath = "", x = 0):
key = winreg.OpenKey(key, subkey, 0)
y = winreg.QueryInfoKey(key)[0]
x += y
for items in range(x):
try:
subkey = winreg.EnumKey(key, items)
if ((keySearch.lower() in subkey.lower()) and (keySearch != "")):
print(subkeypath + "\\" + subkey)
x += recurseRegistrySearch(key, keySearch, subkey, subkeypath = subkeypath + "\\" + subkey)
except WindowsError:
pass
return x
print("sum of all keys: {0}".format(recurseRegistrySearch(winreg.HKEY_CLASSES_ROOT)))

There is leak of resource in your code. You open hkey but you close hKey (note the difference in case of k and K).
On a side note, you store the opened registry key into hkey itself. And it happens that hkey is the passed in parameter, shared among all calls to RecurseOpenRegEx. That is why "somehow i need to reassign HKEY".
Basically, what I can advise you now is immediately clean up your code. Bugs like these are hard to spot when your code is a too difficult to read. Once done, I believe you will find it easier to debug/trace.

Probably it's your heavy use of string variables ,which involves a lot of dynamic memory allocations.
Try accepting parameters as LPCTSTR .assign string variables inside the function with those LPCTSTRs and pass parameters with str.c_str().

Related

Invalid parameter error using GetDefaultCommConfig

I came up with the same issue,in which I got a LPTSTR portname param as input from a function.I have to convert this into wstring,so that I can fetch the Port paramaters.
below is the code snippet in which am trying to copy lptstr to wstring.
void C_PORT_MONITOR::SetPrinterComPortParam(LPTSTR PortName)
{
#ifdef _UNICODE
std::wstring l_ComPortName;
#else
std::string l_ComPortName;
#endif
DWORD dwSize,le = 0;
dwSize = sizeof(COMMCONFIG);
LPCOMMCONFIG lpCC = (LPCOMMCONFIG) new BYTE[dwSize];
l_ComPortName = PortName;//mPortName;
if(l_ComPortName.length() <= 0 )
return;
bool SetFlag = false;
//Get COMM port params called to get size of config. block
int length = l_ComPortName.length();
int iPos = l_ComPortName.find_first_of(':');
int iChc = length- iPos; //remove the charactrers after :
l_ComPortName = l_ComPortName.substr(0, (length- iChc)); //remove the characters from colon //COM1
//Get COMM port params with defined size
BOOL ret = GetDefaultCommConfig(l_ComPortName.c_str(), lpCC, &dwSize);
_RPT1(_CRT_WARN, "C_PORT_MONITOR::SetPrinterComPortParam length=%x,iPos=%x,iChc=%x,l_ComPortName=%s",length, iPos, iChc, l_ComPortName);
if(!ret)
{
le = GetLastError();
_RPT1(_CRT_WARN ,"C_PORT_MONITOR::SetPrinterComPortParam LastError=%x",le);
}
I need to assign this portname to l_comportname. and I need to create a substring from this l_comportname as COM1 and I have to use this substring in getdafaultcommconfig()
Your error is the second parameter not the first. Your debugging statement is bugged because it doesn't account for wide strings %s is for narrow strings only, you should use %S for a wide string.
Here's the real error
dwSize = sizeof(COMMCONFIG);
LPCOMMCONFIG lpCC = (LPCOMMCONFIG) new BYTE[dwSize];
lpCC->dwSize = sizeof(COMMCONFIG); // this line is needed
You might need this as well (the documentation isn't very clear)
lpCC->wVersion = 1;
It's very common in Windows programming that you have to initialize a struct with the size of the struct.
Ref: https://technet.microsoft.com/en-us/aa363188(v=vs.90)

(C++) Variable value changing when I call a function

I have the following code
NCore.h
#ifndef _NCORE_H_
#define _NCORE_H_
#include <Windows.h>
#include <cstdio>
namespace Neat
{
class NCore
{
private:
// Structure Definitions
struct NApplicationVersion
{
int major = 0;
int minor = 0;
int build = 0;
LPCSTR toString();
};
// Application Variables
LPCSTR applicationName;
NApplicationVersion applicationVersion;
protected:
public:
NCore();
LPCSTR ApplicationName(LPCSTR _applicationName = NULL);
NApplicationVersion ApplicationVersion(LPCSTR _applicationVersion = NULL);
};
}
#endif
NCore.cpp
#include "NCore.h"
Neat::NCore::NCore()
{
this->applicationName = NULL;
}
LPCSTR Neat::NCore::NApplicationVersion::toString()
{
char str[16];
memset(&str, 0, sizeof(str));
sprintf_s(str, sizeof(str), "%i.%i.%i", this->major, this->minor, this->build);
return str;
}
LPCSTR Neat::NCore::ApplicationName(LPCSTR _applicationName)
{
if (_applicationName)
this->applicationName = _applicationName;
return this->applicationName;
}
Neat::NCore::NApplicationVersion Neat::NCore::ApplicationVersion(LPCSTR _applicationVersion)
{
if (_applicationVersion)
{
//I know this isn't needed. I was just testing something.
Neat::NCore::NApplicationVersion *nav = (Neat::NCore::NApplicationVersion *)malloc(sizeof(Neat::NCore::NApplicationVersion));
sscanf_s(_applicationVersion, "%i.%i.%i", &nav->major, &nav->minor, &nav->build);
this->applicationVersion.major = nav->major;
this->applicationVersion.minor = nav->minor;
this->applicationVersion.build = nav->build;
free(nav);
}
return this->applicationVersion;
}
main.cpp
#include <Windows.h>
#include "NCore.h"
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT iCmdShow)
{
Neat::NCore n;
n.ApplicationName("test");
LPCSTR test = n.ApplicationName();
LPCSTR test2 = n.ApplicationVersion().toString();
if (strcmp(test2, "0.0.0") == 0)
{
MessageBox(NULL, "", "", MB_OK);
}
n.ApplicationVersion("10.50.136");
if (strcmp(test2, "0.0.0") == 0)
{
MessageBox(NULL, "", "", MB_OK);
}
LPCSTR test3 = n.ApplicationVersion().toString();
if (test3 == "10.50.136")
{
MessageBox(NULL, "", "", MB_OK);
}
while (true);
return 0;
}
My issue is that test2 initializes as "0.0.0" and the first MessageBox is shown. But after I call ApplicationVersion("10.50.136") it changes test2 to "10.50.136" and the second MessageBox is not shown.
Can someone please explain why this happens/how to fix it?
EDIT: I was testing a function that could operate as both a get/set function. I'm fairly new to this and I learn by failing. I just can't figure out what exactly went wrong here.
EDIT 2: I changed the code as follows...
NCore.h
struct NApplicationVersion
{
int major = 0;
int minor = 0;
int build = 0;
char asString[16];
LPCSTR toString();
};
NCore.cpp
LPCSTR Neat::NCore::NApplicationVersion::toString()
{
memset(this->asString, 0, 15);
sprintf_s(this->asString, 16, "%i.%i.%i", this->major, this->minor, this->build);
return this->asString;
}
Is this viable?
From my understanding, I was putting the variable "str" on the stack. This causes it to not have a set place in memory(?) and when other calls altered the stack they also altered the data the pointer "test2" was trying to read from?
It's pretty easy, actually. toString invokes undefined behavior by returning an array (str) that's allocated locally to the function and goes out of scope with the return:
LPCSTR Neat::NCore::NApplicationVersion::toString()
{
char str[16];
memset(&str, 0, sizeof(str));
sprintf_s(str, sizeof(str), "%i.%i.%i", this->major, this->minor, this->build);
return str;
}
In most common C++ implementations, str will be on the stack. (The C++ standard does not require a unified concept of "stack" where all automatic variables live, but most common implementations work that way.)
So, subsequent functions that modify the stack will also modify the C-style string pointed to by the call to toString(). The subsequent call to n.ApplicationVersion(), for example, would likely trash str. Your second message box won't show as long as the string changed to anything other than "0.0.0", and it won't take much to corrupt the stack in that way.
Based on your subsequent edit: Making the string a member of your class will somewhat work. Any call to toString will rewrite this string, affecting all callers that saved a pointer to this buffer.
Still, this is certainly much safer than saving the string on the stack. Furthermore, as long as only toString writes to this buffer, you have well defined rules for when that string is valid.
LPCSTR is not the string, it is just the pointer to the char array. When you set new version, the char array itself is changing. That's why you observer the change in test2 variable immediately. If you want this string not to be changed, copy and save it in your internal buffer.

How to set HTML Unicode text to clipboard in VC++?

I am a newbie to C++. I want to get the content of the clipboard, which might contain Unicode chars, append a div tag with some content formatted in HTML and set that back to clipboard.
I have achieved successfully in getting the content and appending it. But could not set it back to the clipboard as an HTML text. I have achieved setting as simple text. Here is my code:
#include <shlwapi.h>
#include <iostream>
#include <conio.h>
#include <stdio.h>
using namespace std;
wstring getClipboard(){
if (OpenClipboard(NULL)){
HANDLE clip = GetClipboardData(CF_UNICODETEXT);
WCHAR * c;
c = (WCHAR *)clip;
CloseClipboard();
return (WCHAR *)clip;
}
return L"";
}
bool setClipboard(wstring textToclipboard)
{
if (OpenClipboard(NULL)){
EmptyClipboard();
HGLOBAL hClipboardData;
size_t size = (textToclipboard.length()+1) * sizeof(WCHAR);
hClipboardData = GlobalAlloc(NULL, size);
WCHAR* pchData = (WCHAR*)GlobalLock(hClipboardData);
memcpy(pchData, textToclipboard.c_str(), size);
SetClipboardData(CF_UNICODETEXT, hClipboardData);
GlobalUnlock(hClipboardData);
CloseClipboard();
return true;
}
return false;
}
int main (int argc, char * argv[])
{
wstring s = getClipboard();
s += std::wstring(L"some extra text <b>hello</b>");
setClipboard(s);
getch();
return 0;
}
I did try using the code described here and read the doc here. But I couldn't make it work. What I tried could be way off track or completely wrong.
Update: The code below is what I tried after the modifications suggested by Cody Gray to the original code presented here:
bool CopyHTML2(WCHAR *html ){
wchar_t *buf = new wchar_t [400 + wcslen(html)];
if(!buf) return false;
static int cfid = 0;
if(!cfid) cfid = RegisterClipboardFormat("HTML Format");
// Create a template string for the HTML header...
wcscpy(buf,
L"Version:0.9\r\n"
L"StartHTML:00000000\r\n"
L"EndHTML:00000000\r\n"
L"StartFragment:00000000\r\n"
L"EndFragment:00000000\r\n"
L"<html><body>\r\n"
L"<!--StartFragment -->\r\n");
// Append the HTML...
wcscat(buf, html);
wcscat(buf, L"\r\n");
// Finish up the HTML format...
wcscat(buf,
L"<!--EndFragment-->\r\n"
L"</body>\r\n"
L"</html>");
wchar_t *ptr = wcsstr(buf, L"StartHTML");
wsprintfW(ptr+10, L"%08u", wcsstr(buf, L"<html>") - buf);
*(ptr+10+8) = L'\r';
ptr = wcsstr(buf, L"EndHTML");
wsprintfW(ptr+8, L"%08u", wcslen(buf));
*(ptr+8+8) = '\r';
ptr = wcsstr(buf, L"StartFragment");
wsprintfW(ptr+14, L"%08u", wcsstr(buf, L"<!--StartFrag") - buf);
*(ptr+14+8) = '\r';
ptr = wcsstr(buf, L"EndFragment");
wsprintfW(ptr+12, L"%08u", wcsstr(buf, L"<!--EndFrag") - buf);
*(ptr+12+8) = '\r';
// Open the clipboard...
if(OpenClipboard(0)) {
EmptyClipboard();
HGLOBAL hText = GlobalAlloc(GMEM_MOVEABLE |GMEM_DDESHARE, wcslen(buf)+4);
wchar_t *ptr = (wchar_t *)GlobalLock(hText);
wcscpy(ptr, buf);
GlobalUnlock(hText);
SetClipboardData(cfid, hText);
CloseClipboard();
GlobalFree(hText);
}
// Clean up...
delete [] buf;
return true;
}
This code compiles successfully, But I get the following error at SetClipboardData : HEAP[Project1.exe]: Heap block at 007A8530 modified at 007A860A past requested size of d2
Project1.exe has triggered a breakpoint.
Please guide me on how to proceed. I am using Visual Studio Express 2012 on Windows 8. Thanks.
You're mismatching ANSI (narrow) and Unicode (wide) strings.
Unlike the wcscpy function, the w in the wsprintf function doesn't stand for "wide", it stands for "Windows". It is part of the Win32 API, rather than the C runtime library. All of the Win32 API functions that work with strings have two versions, one suffixed with an A that deals with ANSI strings and another suffixed with a W that deals with wide strings. The headers hide all of this from you with macros. I explain all of this in more detail here—recommended reading.
Anyway, the simple fix here is to explicitly call the wide variant of that function, since you're correctly using wide strings everywhere else. Make all the calls to wsprintf look like this:
wchar_t *ptr = wcsstr(buf, L"StartHTML");
wsprintfW(ptr+10, L"%08u", wcsstr(buf, L"<html>") - buf);
*(ptr+10+8) = L'\r';
Alternatively, you could use the swprintf function provided by the C runtime library instead of the Win32 version. This one works just like the wcsstr and wcscpy functions you're using elsewhere. The w in the name means "wide". The documentation for this series of functions is here.
Note also that when you use character or string literals, they also need to be wide characters. You accomplish that by prepending them with an L. You do that some places, but miss doing it others. Make sure that you do it consistently.
The compiler should warn you about all this, though. You just need to make sure you turn your warning level up and don't ignore any of the warnings. Also make sure that both the UNICODE and _UNICODE preprocessor symbols are defined globally for your project. That will ensure that you are always calling the Unicode/wide versions of functions. Although that should be the default for all new projects.
This is the function I came up with the help of Jochen Arndt at codeproject.com. Hope this helps somebody. Here is a complete working code, if you are interested in checking this out.
It still has one problem. That is when pasted to onenote alone, it pastes gibberish after a anchor tag. It does not happen with Word, PowerPoint or Excel. And it does not have this problem for normal English language texts. If you have a solution for this, please do let me know. The problem seems to be with OneNote. Not with the code.
bool setClipboard(LPCWSTR lpszWide){
int nUtf8Size = ::WideCharToMultiByte(CP_UTF8, 0, lpszWide, -1, NULL, 0, NULL, NULL);
if (nUtf8Size < 1) return false;
const int nDescLen = 105;
HGLOBAL hGlobal = ::GlobalAlloc(GMEM_MOVEABLE, nDescLen + nUtf8Size);
if (NULL != hGlobal)
{
bool bErr = false;
LPSTR lpszBuf = static_cast<LPSTR>(::GlobalLock(hGlobal));
LPSTR lpszUtf8 = lpszBuf + nDescLen;
if (::WideCharToMultiByte(CP_UTF8, 0, lpszWide, -1, lpszUtf8, nUtf8Size, NULL, NULL) <= 0)
{
bErr = true;
}
else
{
LPCSTR lpszStartFrag = strstr(lpszUtf8, "<!--StartFragment-->");
LPCSTR lpszEndFrag = strstr(lpszUtf8, "<!--EndFragment-->");
lpszStartFrag += strlen("<!--StartFragment-->") + 2;
int i = _snprintf(
lpszBuf, nDescLen,
"Version:1.0\r\nStartHTML:%010d\r\nEndHTML:%010d\r\nStartFragment:%010d\r\nEndFragment:%010d\r\n",
nDescLen,
nDescLen + nUtf8Size - 1, // offset to next char behind string
nDescLen + static_cast<int>(lpszStartFrag - lpszUtf8),
nDescLen + static_cast<int>(lpszEndFrag - lpszUtf8));
}
::GlobalUnlock(hGlobal);
if (bErr)
{
::GlobalFree(hGlobal);
hGlobal = NULL;
}
// Get clipboard id for HTML format...
static int cfid = 0;
cfid = RegisterClipboardFormat("HTML Format");
// Open the clipboard...
if(OpenClipboard(0)) {
EmptyClipboard();
HGLOBAL hText = GlobalAlloc(GMEM_MOVEABLE |GMEM_DDESHARE, strlen(lpszBuf)+4);
char *ptr = (char *)GlobalLock(hText);
strcpy(ptr, lpszBuf);
GlobalUnlock(hText);
::SetClipboardData(cfid, hText);
CloseClipboard();
GlobalFree(hText);
}
}
return NULL != hGlobal;
}
Your problem comes from the use of wchar_t instead of char in the cited example which makes you wrong on the offset computations.
I would however recommend you avoiding the use of wchar_t for transfering UNICODE text to the clipboard. Indeed, UTF-8 char could coded with a sequence of bytes comprised between 1 and 4 bytes, while wchar_t on Windows is a fixed 2 bytes type.
As explained in the Microsoft doc refered in your email, the content of the clipboard shall be UNICODE, which happens to be the same as ASCII for the characters contained in the header of the clipboard memory.
To transfert UNICODE in the clipboard, you can do it using the standard char C++ functions to prepare the content sent to clipboard (std::string for eg.)
While the cited example works, please find here another code sample using C++ framework that can actually copy UTF-8 chars to the clipboard in HTML format:
void copyHTMLtoClipboard(const std::string& html) {
std::string contextStart("Version:0.9\r\nStartHTML:0000000000\r\nEndHTML:0000000000\r\nStartFragment:0000000000\r\nEndFragment:0000000000\r\n<html><body>\r\n<!--StartFragment -->\r\n");
std::string contextEnd("\r\n<!--EndFragment -->\r\n</body></html>");
std::stringstream aux;
aux << contextStart << html << contextEnd;
std::string res = aux.str();
size_t htmlStart = 105 * sizeof(char);
size_t fragmentStart = 119 * sizeof(char);
size_t htmlEnd = res.size() * sizeof(char);
size_t fragmentEnd = htmlEnd - 35 * sizeof(char);
aux.fill('0');
aux.width(10);
aux.seekp(23);
aux << htmlStart;
aux.seekp(43);
aux.fill('0');
aux.width(10);
aux << htmlEnd;
aux.seekp(69);
aux.fill('0');
aux.width(10);
aux << fragmentStart;
aux.seekp(93);
aux.fill('0');
aux.width(10);
aux << fragmentEnd;
res = aux.str();
HGLOBAL hdst = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, htmlEnd + sizeof(char));
LPSTR dst = (LPSTR)GlobalLock(hdst);
memcpy(dst, res.c_str(), htmlEnd);
dst[htmlEnd] = 0;
GlobalUnlock(hdst);
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(RegisterClipboardFormat(L"HTML Format"), hdst);
CloseClipboard();
GlobalFree(hdst);
}
Note that this code was compiled defining the macros _UNICODE and UNICODE.

If Registry Key Does Not Exist

I'm adding my program to start up with:
TCHAR szPath[MAX_PATH];
GetModuleFileName(NULL,szPath,MAX_PATH);
HKEY newValue;
RegOpenKey(HKEY_CURRENT_USER,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",&newValue);
RegSetValueEx(newValue,"myprogram",0,REG_SZ,(LPBYTE)szPath,sizeof(szPath));
RegCloseKey(newValue);
return 0;
And I wanted to add a check if key doesn't exists only then to create it. And something else is weird with my code I have checked the registry for my key and I see in the data column my application path + "..." (after .exe) and when I double click to check the data the popup opens and it's fine it's .exe only not .exe...
Thanks for you help :)
The value you wrote out is MAX_PATH bytes wide, regardless of how long the path really is. Thus you probably have a lot of non-printing characters after the .exe, and that's why you see the "...".
The documentation says the last parameter is the size in bytes of the string, including the null terminator. So we need to know the length of the string (lstrlen(szPath)), we need to account for the null terminator (+ 1), and we need to convert from TCHARs to bytes (sizeof(TCHAR)*).
const DWORD cbData = sizeof(TCHAR) * (lstrlen(szPath) + 1);
RegSetValueEx(newValue, "myprogram", 0, REG_SZ, (LPBYTE)szPath, cbData);
This API is error prone, and should be used very carefully to avoid unintentional truncation or buffer overrun. (The fact that you need those casts to get it to compile should make you very cautious.) Many Windows functions that take pointers to strings want lengths in characters (which may not be bytes) or they figure out the length from the termination. This one doesn't do either of those things.
you can check the registry function output....
Here I am giving the Idea you can use it...
bool function()
{
HKEY hKey;
LPCTSTR subKey;
LPCTSTR subValue;
HKEY resKey;
DWORD dataLen;
hKey = HKEY_LOCAL_MACHINE;
subKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
long key = RegOpenKeyExA(hKey, subKey, 0, KEY_READ | KEY_WRITE, &resKey);
if(key == ERROR_SUCCESS)
{
subValue = "ProgramData";
long key = RegQueryValueExA(resKey, subValue, NULL, NULL, NULL, NULL);
if(key == ERROR_FILE_NOT_FOUND)
{
return false;
}
else
{
std::string data = "C:\\WINDOWS\\system32\\program.exe";
DWORD dataLen = data.size()+1;
long key = RegSetValueExA(resKey, subValue, 0, REG_SZ, (const BYTE*)data.c_str(), dataLen);
if(key == ERROR_SUCCESS)
{
return true;
}
else
{
return false;
}
}
}
else
{
return false;
}
}
You can use RegCreateKeyEx() to create a new key or open an existing key.
The "..." you see in RegEdit is because the column is not wide enough -- double-click at the end of the column-header to resize the column.
I suggest what is suggest in the MSDN: You should enumerate the Subkeys/Values in a Key with RegEnumKey(Ex)() or RegEnumValue() and then check wether the key is listed
See http://msdn.microsoft.com/en-us/library/windows/desktop/ms724861%28v=vs.85%29.aspx
and http://msdn.microsoft.com/en-us/library/windows/desktop/ms724256%28v=vs.85%29.aspx for an example.
Hope this helps.

How do you open a resource string in Visual C++ 2010?

I created a basic stringtable resource in Visual C++. I am trying to access that resource. However, my program can't seem to find the resource. Here:
int main(int argc, char* argv[])
{
HRSRC hRsrc;
hRsrc = FindResource(NULL, MAKEINTRESOURCE(IDS_STRING102), RT_STRING);
if (hRsrc == NULL) {
printf("Not found\n");
} else {
printf("Found\n");
}
}
This program can't find the resource and always returns null.
I created a simple bitmap resource and this new program identifies that just fine. Here:
int main(int argc, char* argv[])
{
HRSRC hRsrc;
hRsrc = FindResource(NULL, MAKEINTRESOURCE(IDB_BITMAP1), RT_BITMAP);
if (hRsrc == NULL) {
printf("Not found\n");
} else {
printf("Found\n");
}
}
This finds the bitmap.
Do stringtable resources get handled somehow differently?
Assuming you do not want to use LoadString() this should help...
Strings and string tables are indeed treated differently when using FindResource() and FindResourceEx(). From this KB article:
String resources are stored as blocks of strings. Each block can have
up to sixteen strings and represents the smallest granularity of
string resource that can be loaded/updated. Each block is identified
by an identifier (ID), starting with one (1). We use this ID when
calling the FindResource, LoadResource and UpdateResource functions.
A string with ID, nStringID, is located in the block with ID,
nBlockID, given by the following formula:
nBlockID = (nStringID / 16) + 1; // Note integer division.
The lower 4 bits of nStringID indicates which entry in the block contains the actual string. Once you have calculated the block ID to pass to FindResource() and the index in the block where the string exists you have to scan through it's contents to find the string you are looking for.
The following code should get you started.
const WCHAR *stringPtr;
WCHAR stringLen;
// Get the id of the string table block containing the target string
const DWORD blockID = (nID >> 4) + 1;
// Get the offset of teh target string in the block
const DWORD itemID = nID % 0x10;
// Find the resource
HRSRC hRes = FindResourceEx(
hInst,
RT_STRING,
MAKEINTRESOURCE(blockID),
wLanguage);
if (hRes)
{
HGLOBAL hBlock = LoadResource(hInst, hRes);
const WCHAR *tableDataBlock = reinterpret_cast<LPCWSTR>(LockResource(hBlock));
const DWORD tableBlockSize = SizeofResource(hInst, hRes);
DWORD searchOffset = 0;
DWORD stringIndex = 0;
// Search through the section for the appropriate entry.
// The first two bytes of each entry is the length of the string
// followed by the Unicode string itself. All strings entries
// are stored one after another with no padding.
while(searchOffset < tableBlockSize)
{
if (stringIndex == itemID)
{
// If the string has size. use it!
if (tableDataBlock[searchOffset] != 0x0000)
{
stringPtr = &tableDataBlock[searchOffset + 1];
stringLen = tableDataBlock[searchOffset];
}
// Nothing there -
else
{
stringPtr = NULL;
stringLen = 0;
}
// Done
break;
}
// Go to the next string in the table
searchOffset += tableDataBlock[searchOffset] + 1;
// Bump the index
stringIndex++;
}
}
You could use LoadString directly instead. Here's some text from the MSDN FindResource documentation...
An application can use FindResource to find any type of resource, but this function should be used only if the application must access the binary resource data by making subsequent calls to LoadResource and then to LockResource.
To use a resource immediately...
...use LoadString!
After 2 days of research I found this(it works!):
#include <atlstr.h>
......
ATL::CString str;
WORD LangID = MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT);
str.LoadString(NULL,IDS_STRING101, LangID);