|
if statement is very wrong
'\0\0\0' quote will be used as a single '\0' character
and the ! sign not the equation modifies to != , it will be a bool negation of rgValue[i] character and gives true when rgValue[i] is '\0' and false otherwise and this bool value will be compared to the '\0\0\0' value what is false as bool type
I recommend this:
ndx = 0;
szRgVal = sizeof(rgValue);
for(int i=0; i < szRgVal; i++) {
if (rgValue[i] == '\0' && rgValue[i+1] == '\0' && rgValue[i+2] == '\0' && rgValue[i+3] == '\0') {
szInstance[ndx++] = '\0'; break;
}
else {
if (rgValue[i] == '\0' && rgValue[i+1] == '\0' && rgValue[i+2] == '\0') {
szInstance[ndx++] = ';'; i++;
}
else
szInstance[ndx++] = rgValue[i];
i++;
}
|
|
|
|
|
I thought about that, how can I compare 2 bytes in a row with i that represents 1 byte. Didn't think about i+1 and i+2 and so forth.
I had the right idea at least!
After I wrote the first version I posted, I started wondering if I was crazy, in the way I handled the returned value.
I wasn't expecting a code rewrite that is paste and go!, but I thank you for taking the time to do so. Nice to know I was on the right track, and I actually learned something this week.
Sincerely
jkirkerx
|
|
|
|
|
This part doesn't work, it never registers, I ran the code through debug and stepped through it, but it's hard to follow the 3 zero sequence.
Day is over, get back to it tomorrow, no worries I will figure it out.
else {
if (rgValue[i] == '\0' && rgValue[i+1] == '\0' && rgValue[i+2] == '\0') {
szInstance[ndx++] = ';';
i++;
}
|
|
|
|
|
One word: UNICODE.
Peter
Software rusts. Simon Stephenson, ca 1994.
|
|
|
|
|
I thought about that last night,
Should I just left it as unicode, so it don't run into the lang issue down the road when I make the french version.
|
|
|
|
|
Stop, stop!
You have totally misinterpreted the data you are looking at. This is a Unicode (aka UTF16) string where each character is 16 bits wide rather than the 8 used by ASCII. You must either convert the string to ASCII in order to process it correctly (via WideCharToMultiByte() [^]), or make your program Unicode aware. I would also be interested to know how you are extracting this value from the registry as I was under the impression that strings are automatically converted for non-Unicode programs.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
This is code I copied from the net and modified in the my first week of c++. In the other uses of the code, I used a CString, but this time i needed to modify the results. It didn't dawn on me till I was bowling last night that the result is Unicode, and I should leave it as Unicode, so I can support French and Spanish. I think SQL Server Instances can be named UniCode, thus that's why the registry values are stored that way.
Alright, lesson learned.
char* CA_Registry::_get_SQLServer_InstalledInstances( void )
{
HKEY keyHandle;
char rgValue[1024];
char szInstance[1024];
CString regPath = L"SOFTWARE\\Microsoft\\Microsoft SQL Server\\";
CString regReq = L"InstalledInstances";
DWORD size1 = sizeof(rgValue);
DWORD Type;
DWORD dwType = 0;
DWORD regStatus = REG_MULTI_SZ;
int ndx=0;
int szRgVal=0;
int maxStr=0;
bool doubleZero;
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regPath, 0, KEY_QUERY_VALUE, &keyHandle) == ERROR_SUCCESS) {
dwType = REG_MULTI_SZ;
regStatus = RegQueryValueEx( keyHandle, regReq, NULL, &Type, (LPBYTE)rgValue, &size1);
switch ( regStatus ) {
case ERROR_SUCCESS:
ndx = 0;
doubleZero = false;
szRgVal = sizeof(rgValue);
for(int i=0; i < szRgVal; i++) {
if (!rgValue[i] == '\0\0') {
szInstance[ndx++] = rgValue[i];
doubleZero = false;
}
else {
if (doubleZero == false) {
szInstance[ndx++] = ';';
doubleZero = true;
}
else {
szInstance[ndx++] = '\0';
break;
}
}
i++;
}
maxStr = strlen(szInstance);
szInstance[maxStr-0] = (char)'\0';
printf(" SQL Server Instance: %s %i\n", szInstance, strlen(szInstance) );
RegCloseKey(keyHandle);
cout << "key was read successfully\n";
break;
case ERROR_MORE_DATA:
cout << "Buffer too small\n";
break;
default:
cout << "Count not read key\n";
}
}
else {
cout << "Can not open key";
cin.ignore();
}
int strSize = sizeof(szInstance);
char *szReturnValue = new char[strSize];
for(int i = 0; i < strSize; i++) {
szReturnValue[i]=szInstance[i];
}
maxStr=strlen(szReturnValue);
szReturnValue[maxStr-0]=(char)'\0';
return szReturnValue;
delete [] szReturnValue;
}
|
|
|
|
|
You are making a dangerous mistake in this code, in that you are mixing ANSI and Unicode, but without fully understanding it. You should change all your char declarations to WCHAR or switch the project setting, Character Set in General properties, from Unicode to Multi-byte.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
That's the mistake from yesterday I posted in full.
Today I will fix the 2 registry programs, I was thinking wchar_t, but I'll go with WCHAR.
The following registry function , is ran after the one I posted above, so you can get an idea of what I'm doing. I wrote the char stuff by myself, with no help, hope it passes. I started changing the code to wchar so I know the code doesn't work, it's just foresight.
I run the first program, return the values and seperate the names, and then loop through the names with the program below to get the versions
wchar_t* CA_Registry::_get_SQLServer_Version( const wchar_t *szSQLInstance )
{
HKEY keyHandle;
char rgValue [1024];
char szVersion[1024];
char szRegPath[125];
char *szRegPath_Prefix = "SOFTWARE\\Microsoft\\Microsoft SQL Server\\";
char *szRegPath_Suffix = "\\MSSQLServer\\CurrentVersion\\";
int iRegPath_Prefix = strlen(szRegPath_Prefix);
int iRegPath_Suffix = strlen(szRegPath_Suffix);
strncpy_s(szRegPath, _countof(szRegPath), szRegPath_Prefix, iRegPath_Prefix);
strncat_s(szRegPath, _countof(szRegPath), szRegPath_Suffix, iRegPath_Suffix);
int buffSize = (int)strlen(szRegPath) + 1;
LPWSTR lpRegPath = new wchar_t[buffSize];
MultiByteToWideChar(CP_ACP, 0, szRegPath, buffSize, lpRegPath, buffSize);
LPWSTR lpRegReq = L"CurrentVersion";
DWORD size1 = sizeof(rgValue);
DWORD Type;
DWORD dwType = 0;
DWORD regStatus = REG_SZ;
int ndx=0;
int szRgVal;
int maxStr;
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpRegPath, 0, KEY_QUERY_VALUE, &keyHandle) == ERROR_SUCCESS) {
dwType = REG_SZ;
regStatus = RegQueryValueEx( keyHandle, lpRegReq, NULL, &Type, (LPBYTE)rgValue,&size1);
switch ( regStatus ) {
case ERROR_SUCCESS:
ndx=0;
szRgVal=sizeof(rgValue);
for(int i=0;i<szRgVal;i++) {
szVersion[ndx++]=rgValue[i];
i++;
}
maxStr=strlen(szVersion);
szVersion[maxStr-0]=(char)'\0';
printf(" Binary path: %s %i\n", szVersion, strlen(szVersion) );
RegCloseKey(keyHandle);
cout << "key was read successfully\n";
break;
case ERROR_MORE_DATA:
cout << "Buffer too small\n";
break;
default:
cout << "Count not read key\n";
}
}
else {
cout << "Can not open key";
cin.ignore();
}
delete [] lpRegPath;
int strLength = strlen(szVersion);
wchar_t *szReturnValue = new wchar_t[strLength];
for (int i = 0; i < strLength; i++) {
szReturnValue[i] = szVersion[i];
}
szReturnValue[ strLength ] = '\0';
return szReturnValue;
delete [] szReturnValue;
}
|
|
|
|
|
I rewrote the function for WCHAR. It works, I get the result that I wanted, and it's Unicode I believe. I'm pretty sure I got it right, but I need an expert to evaluate it. I'm not sure if the extra wchar array copy is needed at the bottom to pass the value back to the calling function. Plus I'm back to the Original question, but in hindsight, all it does is replaces the \0 with a ;, and re terminates the array.
WCHAR* CA_Registry::_get_SQLServer_InstalledInstances( void )
{
HKEY keyHandle;
WCHAR rgValue[1024];
WCHAR szInstance[1024];
CString regPath = L"SOFTWARE\\Microsoft\\Microsoft SQL Server\\";
CString regReq = L"InstalledInstances";
DWORD size1 = sizeof(rgValue);
DWORD Type;
DWORD dwType = 0;
DWORD regStatus = REG_MULTI_SZ;
int ndx;;
size_t szRgVal;
int maxStr;
bool doubleZero;
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regPath, 0, KEY_QUERY_VALUE, &keyHandle) == ERROR_SUCCESS) {
dwType = REG_MULTI_SZ;
regStatus = RegQueryValueEx( keyHandle, regReq, NULL, &Type, (LPBYTE)rgValue, &size1);
switch ( regStatus ) {
case ERROR_SUCCESS:
ndx = 0;
doubleZero = false;
szRgVal = sizeof(rgValue);
for(int i=0; i < szRgVal; i++) {
if (!rgValue[i] == L'\0') {
szInstance[ndx++] = rgValue[i];
doubleZero = false;
}
else {
if (doubleZero == false) {
szInstance[ndx++] = L';';
doubleZero = true;
}
else {
szInstance[ndx++] = L'\0\0';
break;
}
}
}
maxStr = wcslen(szInstance);
szInstance[maxStr-0] = (char)'\0';
printf(" SQL Server Instance: %s %i\n", szInstance, wcslen(szInstance) );
RegCloseKey(keyHandle);
cout << "key was read successfully\n";
break;
case ERROR_MORE_DATA:
cout << "Buffer too small\n";
break;
default:
cout << "Count not read key\n";
}
}
else {
cout << "Can not open key";
cin.ignore();
}
int strSize = sizeof(szInstance);
WCHAR *szReturnValue = new wchar_t[strSize];
for(int i = 0; i < strSize; i++) {
szReturnValue[i]=szInstance[i];
}
strSize=wcslen(szReturnValue);
szReturnValue[strSize-0]=(WCHAR)L'\0';
return szReturnValue;
delete [] szReturnValue;
}
|
|
|
|
|
You've pretty much nailed it, but I have done some tidying up and fixed a couple of minor issues in the code as follows:
WCHAR* CA_Registry::_get_SQLServer_InstalledInstances( void )
{
HKEY keyHandle;
WCHAR rgValue[1024];
WCHAR* regPath = L"SOFTWARE\\Microsoft\\Microsoft SQL Server\\";
WCHAR* regReq = L"InstalledInstances";
DWORD size1 = sizeof(rgValue);
DWORD dwType;
DWORD regStatus;
int ndx;
WCHAR *szReturnValue = NULL;
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regPath, 0, KEY_QUERY_VALUE, &keyHandle) == ERROR_SUCCESS) {
regStatus = RegQueryValueEx( keyHandle, regReq, NULL, &dwType, (LPBYTE)rgValue, &size1);
RegCloseKey(keyHandle); switch ( regStatus ) {
case ERROR_SUCCESS:
{
WCHAR* pszString = rgValue;
while (*pszString)
{
ndx = wcslen(pszString);
pszString += ndx;
if (pszString[1] != L'\0')
*pszString++ = L';'; }
}
ndx = wcslen(rgValue); wprintf(L" SQL Server Instance: %s %i\n", rgValue, ndx);
szReturnValue = new wchar_t[ndx + 1]; wcscpy_s(szReturnValue, ndx + 1, rgValue);
break;
case ERROR_MORE_DATA:
cout << "Buffer too small\n";
break;
default:
cout << "Could not read key\n";
}
}
else {
cout << "Can not open key";
cin.ignore();
}
return szReturnValue; }
I hope my comments make sense but please let me know if they don't. I have built and tested this code but I do not have the exact content of the registry value so it may need some minor tweaking.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
I had to read it a couple of times before it soaked in, those are huge refinements, but I get it, way more optimized, and it teaches me that alot of the stuff was not needed. Let me make the mods, and I will alter the other registry function I have as well. I didn't know I had to wprintf, I thought it was just for printing to the screen in console.
Thanks
|
|
|
|
|
Works like a charm, I had to move the pszString above the switch because the compiler complained. I changed the function by hand, and only copied the ERROR_Success because it was alot to type. So I have to delete somthing in the calling function, not sure what yet.
WCHAR* CA_Registry::_get_SQLServer_InstalledInstances( void )
{
HKEY keyHandle;
WCHAR rgValue[1024];
WCHAR *regPath = L"SOFTWARE\\Microsoft\\Microsoft SQL Server\\";
WCHAR *regReq = L"InstalledInstances";
DWORD size1 = sizeof(rgValue);
DWORD dwType;
DWORD regStatus;
int ndx;
WCHAR* pszString = rgValue;
WCHAR *szReturnValue = NULL;
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regPath, 0, KEY_QUERY_VALUE, &keyHandle) == ERROR_SUCCESS) {
regStatus = RegQueryValueEx( keyHandle, regReq, NULL, &dwType, (LPBYTE)rgValue, &size1);
RegCloseKey(keyHandle);
switch ( regStatus ) {
case ERROR_SUCCESS:
{
while (*pszString)
{
ndx = wcslen(pszString);
pszString += ndx;
if (pszString[1] != L'\0')
*pszString++ = L';';
}
}
ndx = wcslen(rgValue);
wprintf(L" SQL Server Instance: %s %i\n", rgValue, ndx);
szReturnValue = new wchar_t[ndx + 1];
wcscpy_s(szReturnValue, ndx + 1, rgValue);
break;
case ERROR_MORE_DATA:
cout << "Buffer too small\n";
break;
default:
cout << "Count not read key\n";
}
}
else {
cout << "Can not open key";
cin.ignore();
}
return szReturnValue;
}
This is the calling function just for reference, I wrote it from scratch, no help, lot cleaner than the registry functions I wrote. It's a rough draft, needs more detail, but in the end, after searching by wire, then search the local machine registry, if nothing is found, I will ask if they want to download the X86 or X64 version, and install it using an automated script that I write on the fly. It's starting to shape up now.
BOOL CA_SQLServer_Scan::_scan_Enumerate_Registry( HWND txtProgress, HWND lblServers, HWND pbProgress )
{
CA_Registry caReg;
BOOL bResult = FALSE;
BOOL bInstance = FALSE;
int instanceLength;
WCHAR *szComputerName = NULL;
WCHAR *szInstanceNames = NULL;
WCHAR *szCurrentVersion = NULL;
WCHAR szServerInstance[125];
szComputerName = caReg._get_ComputerName();
szInstanceNames = caReg._get_SQLServer_InstalledInstances();
instanceLength = wcslen(szInstanceNames);
if (instanceLength > 0) {
WCHAR *szInstanceName = NULL;
WCHAR *token1, *nextToken;
WCHAR seps[] = L";";
WCHAR vSeps[] = L".";
token1 = wcstok_s(szInstanceNames, seps, &nextToken);
do {
if (token1 == NULL) {
break;
}
else {
szInstanceName = token1;
szCurrentVersion = caReg._get_SQLServer_Version(szInstanceName);
int szVerValue = wcslen(szCurrentVersion);
if ( szVerValue > 1 ) {
WCHAR *szVerMajor = NULL, *szVerMinor = NULL, *szVerBuild = NULL;
WCHAR *vToken1, *vToken2, *vToken3, *vNextToken;
int iVerMajor=0, iVerMinor=0, iVerBuild=0;
vToken1 = wcstok_s(szCurrentVersion, vSeps, &vNextToken);
if (!vToken1 == NULL) {
szVerMajor = vToken1;
iVerMajor = _wtoi(szVerMajor);
}
vToken2 = wcstok_s(NULL, vSeps, &vNextToken);
if (!vToken2 == NULL) {
szVerMinor = vToken2;
iVerMinor = _wtoi(szVerMinor);
}
vToken3 = wcstok_s(NULL, vSeps, &vNextToken);
if (!vToken3 == NULL) {
szVerBuild = vToken3;
iVerBuild = _wtoi(szVerBuild);
}
WCHAR pbMessage[80];
wcsncpy_s(pbMessage, _countof(pbMessage), L"Registered: ", wcslen(L"Registered: "));
wcsncat_s(pbMessage, _countof(pbMessage), L"\\", wcslen(L"\\"));
wcsncat_s(pbMessage, _countof(pbMessage), szInstanceName, wcslen(szInstanceName));
SetWindowText(txtProgress, pbMessage);
Sleep(500);
switch (iVerMajor)
{
case 11:
bInstance = TRUE;
break;
case 10:
bInstance = TRUE;
break;
case 9:
bInstance = TRUE;
break;
default:
bInstance = FALSE;
break;
}
if (bInstance) {
int iComputerName = wcslen(szComputerName);
int iInstanceName = wcslen(szInstanceName);
wcsncpy_s(szServerInstance, _countof(szServerInstance), szComputerName, iComputerName);
wcsncat_s(szServerInstance, _countof(szServerInstance), L"\\", wcslen(L"\\"));
wcsncat_s(szServerInstance, _countof(szServerInstance), szInstanceName, iInstanceName);
iInstanceName=wcslen(szServerInstance);
szServerInstance[iInstanceName-0]=(WCHAR)L'\0';
int iCollectionCount;
iCollectionCount = sqlSrvCollection.Count();
if (iCollectionCount > 0) {
WCHAR *szComparision = NULL;
for (int i=0; i<iCollectionCount; i++) {
szComparision = sqlSrvCollection[i];
if (wcscmp(szServerInstance, szComparision) != 0) {
int iCollection = sqlSrvCollection.Add();
sqlSrvCollection[iCollection] = szServerInstance;
}
}
}
else {
int iCollection = sqlSrvCollection.Add();
sqlSrvCollection[iCollection] = szServerInstance;
}
}
}
token1 = wcstok_s(NULL, seps, &nextToken);
}
} while (TRUE);
}
return bResult;
}
|
|
|
|
|
jkirkerx wrote: I had to move the pszString above the switch because the compiler complained.
Must be a different version than mine. In that case you can delete the extra set of curly braces around the while() loop under case ERROR_SUCCESS: . Looking at the other code you posted I think you are well in control now, you have obviously worked hard on this - time for some R&R on the beach. Good luck.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
Well Thanks, I rewrote all my project code last night, and fixed everything in all my modules. I'm determined to get this program out there, so I can start getting people to use my other programs.
I still have the socket issue to fix, and the safe array with comm pointer. I will post those today.
I'll take this afternoon off and hit the sand when it warms up a little, it's 60F outside now.
jkirkerx
|
|
|
|
|
jkirkerx wrote: I'm determined to get this program out there
You must be getting close now.
jkirkerx wrote: socket issue ... and the safe array
I should think with what you have learned recently these will (should) be that much simpler.
jkirkerx wrote: it's 60F outside now.
Well the sun has just gone down here and it's about the same. Unseasonably mild for October in the UK.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
I'd appreciate it if you mark my last post as Good (or Bad) answer, depending on your viewpoint.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
I am developing a ham radio logging program and using registry ( my first usage of registry) to save configuration values.
The application is based on SDI MFC document / view architecture..
The “document” (in CDocument) is a CString extracted from user's entry in CFormView.
For this document to be valid an additional “configuration” data are required before the user enters any data into simple CEdit box.
The configuration data are stored in registry and than copied ( on each new start of application) to CDocument and displayed in the CFormView and in configuration ( modal) dialogs as required.
At present I am using SDI , however, would like to be able to add another views in future “improvements”.
Since this is my first usage of registry I would like to hear from anybody if this approach is feasible.
Somehow I feel it is a little too complicated.
As always, thanks for reading.
Vaclav
|
|
|
|
|
It's not really that hard once you do it a few times, so it's very feasible... but if there are other options, for example, you can have a file store the configuration. This gives you the added advantage to make it easy for the user to "share" his configuration (since they can just copy the file) and if you make it plain text (which is the best option, as opposed to writing file out in binary), then they can also edit it outside of the program.
Advantage of using the registry is that it doesn't matter where the executable is, it will always find the registry keys. So if the user decides to move the executable (on purpose or by accident), then their settings will still work.
|
|
|
|
|
Hello everybody,
i am trying to create a text file and trying to write something. my problem is with the file name.
FILE *fp;
fp = fopen("c:\test_1/2.txt,"wb");
this special character "/" is not accepting and fp is returning null, and it crashes the system if i try to write something in fp.
i need to keep the file name as like this only.
is there anyway to solve this issue?
Thanks in advance.
A. Gopinath.
|
|
|
|
|
if that is a file named test_1/2.txt (and not file 2.txt in some test_1 folder) then the filename IS invalid. You'll have to choose a different name for your file. See here[^].
|
|
|
|
|
tagopi wrote: is there anyway to solve this issue?
yeah, don't call fwrite if your fp is NULL.
|
|
|
|
|
tagopi wrote: is there anyway to solve this issue?
Seeing as how that file cannot be created outside of code (e.g., right-click desktop, new text document, type name), I doubt it.
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"Show me a community that obeys the Ten Commandments and I'll show you a less crowded prison system." - Anonymous
|
|
|
|
|
I think everyone here missed the real issue here.
"c:\test_1/2.txt"
There is a \t in the filename, which is converted to a tab character by the compiler. The file path getting passed into fopen is "c: est_1/2.txt"
You need to escape this backslash with another backslash, so your filename becomes "c:\\test_1/2.txt".
as for the forward slash, windows will interpret this as a path slash too, but you don't need to escape it because the compiler wont change it.
So, the path you are accessing is C: > test_1 > 2.txt
Additionally, fopen will not create the folder "test_1" if it does not exist, it will just fail to open it and return NULL.
|
|
|
|
|
That however wouldn't cause a crash.
Whereas not testing the return value of the method probably does.
|
|
|
|
|