MFC ODBC SQLConfigDataSource() Unhandled Exception - c++

I'm trying to update an old c++ MFC program and SQLConfigDataSource is causing an unhandled exception when I try to create a DSN. The error message says:
Unhandled exception at 0x00007FFC97D89129 (KernelBase.dll):
0x0000087A (parameters: 0xFFFFFFFF887A0001, 0x0000000000000053)
The code runs correctly and functions as it should, but I can't seem to get rid of this error.
int mlen;
char* szDesc = new char[256];
sprintf_s(szDesc, 256, "DSN=%s?DBQ=%s?FIL=MicrosoftAccess?",
IV_DATABASE_NAME, // DSN name
sDBPath); // full file name for accdb file
mlen = strlen(szDesc);
for (int i = 0; i < mlen; i++) {
if (szDesc[i] == '?')
szDesc[i] = '\0';
}
SQLConfigDataSource(NULL, ODBC_ADD_DSN,
"Microsoft Access Driver (*.mdb, *.accdb)",
(LPCSTR)szDesc);
delete szDesc;

The arguments for SQLConfigDataSource are ill formed. See:
SQLConfigDataSource Function
And:
ConfigDSN Function
Each pair is terminated with a null byte, and the entire list is
terminated with a null byte. (That is, two null bytes mark the end of
the list.)
I've had this working for over a decade now and call it like:
if( ! bOk )//failed to open the default database for lack of DSN
{
TRACE("Could not find DSN\n");
BOOL ret= SQLConfigDataSource(
NULL,
ODBC_ADD_DSN,
(LPSTR) "Microsoft Access Driver (*.mdb)\0",
(LPSTR) "DSN=MS Access Database\0"
"Description=MS Access Database\0"
);
if( ! ret )
{
AfxMessageBox( _T("The 'Data Source Name' failed to install\nPlease call My Company\n800-555-5555") );
return FALSE;
}
}
Note that the first two extra nulls are redundant, but they do no harm. But the last line follows the two null, end of list, rule.
"Description=MS Access Database\0"
You should be checking the return value of SQLConfigDataSource. And you can call SQLInstallerError as described in Diagnostics. Also, the comments state that the keywords and values should not contain ?.

The error was occurring when Microsoft Access Database Engine 2016 Redistributable was installed. I installed the 2010 Redistributable and it runs perfectly without any issues.

Related

Cannot set Scanner Capability because L_TwainStartCapsNeg returns error -84

I'm trying to use the Leadtools API version 21 for automatically scanning some documents and here is a sudo code of what I have done (it runs in a secondary thread and the unlock has been done in the main thread):
void CheckRetCode(int rc)
{
if (SUCCESS != rc)
{
L_TCHAR errMsg[1024];
memset(errMsg, 0, sizeof(errMsg));
L_GetFriendlyErrorMessage(rc, errMsg, 1024, L_FALSE);
throw TLeadException(errMsg, rc);
}
}
void OnThreadExecute(void)
{
HTWAINSESSION hSession = nullptr;
APPLICATIONDATA appData;
L_INT nRet;
L_TCHAR pszTwnSourceName[1024];
LTWAINSOURCE sInfo;
memset(&appData, 0, sizeof(APPLICATIONDATA));
appData.uStructSize = sizeof(APPLICATIONDATA);
appData.hWnd = hWnd;// hWnd is valid handle of my main window
appData.uLanguage = TWLG_ENGLISH_USA;
appData.uCountry = TWCY_USA;
wcscpy(appData.szManufacturerName, L"MyCompanyName");
wcscpy(appData.szAppProductFamily, L"MyProductName");
wcscpy(appData.szAppName, appData.szAppProductFamily);
wcscpy(appData.szVersionInfo, L"Version 0.1.0.1");
nRet = L_TwainInitSession2(&hSession, &appData, LTWAIN_INIT_MULTI_THREADED);
CheckRetCode(nRet);// the exception gets catched elsewhere but no error reported here
memset(pszTwnSourceName, 0, sizeof(pszTwnSourceName));
wcscpy(pszTwnSourceName, L"EPSON Artisan837/PX830"); // the name of the scanner is verifyed
sInfo.uStructSize = sizeof(LTWAINSOURCE);
sInfo.pszTwainSourceName = pszTwnSourceName;
CheckRetCode(L_TwainSelectSource(hSession, &sInfo)); // No error reported here
CheckRetCode(L_TwainStartCapsNeg(hSession)); // in here I get the return value -84 which is reported as "TWAIN DS or DSM reported error, app shouldn't (no need for your app to report the error)."
// the rest of the code but we cannot get there since above code reports error
}
Can anyone tell me what I'm doing wrong? Is there a step that I'm missing here?
EditThe function L_TwainSelectSource() make no effort to make sure the supplied source is valid and does not even return an error. As result, if you set the selected source to a garbage name, it will act as if it accepted it. From that point on if you try to Get/Set anything or try to acquire an image, every function returns -84.
Thank you
Sam
To test your code, I put the main window’s handle in a global variable:
globalhWnd = hWnd;
And modified your function to use that handle like this:
void OnThreadExecute(void *)
{
...
appData.hWnd = globalhWnd; // hWnd is valid handle of my main window
...
}
Then created a thread for it from the main program like this:
globalhWnd = hWnd;
_beginthread(OnThreadExecute, 0, 0);
I tried this with 5 different Twain sources: 2 virtual and 3 physical scanners (one of them an old Epson). All 5 drivers returned SUCCESS when calling L_TwainStartCapsNeg() from within the thread.
Two possibilities come to mind:
The problem might be caused by something else in your code other than the thread function.
Or the problem could be specific to your Twain driver.
To rule out the first possibility, I suggest creating a small test project that only creates a similar thread and does nothing else and trying it with different scanners. If it causes the same problem with all scanners, send that test project (not your full application) to support#leadtools.com and our support engineers with test it for you.
If the problem only happens with a specific Twain driver, try contacting the scanner’s vendor to see if they have an updated driver.

GetFIleVersionInfoSize() succeeds but returns incorrect size

I am trying to get the version of a file. I want to look at the version number of this file to determine which OS is installed on a non-booted drive (I'll actually be doing this from a Win PE environment and trying to determine if the main drive has Windows XP or Windows 7 installed). Anyway, I have the following
wchar_t *fileName;
fileName = new wchar_t[255];
lstrcpy(fileName, hdds[HardDriveIndexes::SystemDrive].driveLetter.c_str());
lstrcat(fileName, L"Windows\\System32\\winload.exe");
TCHAR *versionInfoBuffer;
DWORD versionDataSize;
if (versionDataSize = GetFileVersionInfoSize(fileName, NULL) != 0)
{
versionInfoBuffer = new TCHAR[versionDataSize];
BOOL versionInfoResult = FALSE;
versionInfoResult = GetFileVersionInfo(fileName, NULL, versionDataSize, versionInfoBuffer);
if (versionInfoResult == FALSE)
{
wprintf(L"The last error associated with getting version info is: %d\n", GetLastError());
}
}
else
{
wprintf(L"The last error associated with gettting version info size is: %d\n", GetLastError());
}
The problem is that GetFileVersionInfoSize() succeeds but always returns 1 as the size. This causes GetFileVersionInfo() to fail with error 122. So far I have only tested this on a Windows 7 system. There is another function GetFileVersionInfoSizeEx() that works as expected, but it is only supported from Vista onwards. I would like to keep XP support if possible (some of our old Win PE images are still based on XP).
Is GetFileVersionInfoSize() deprecated and I somehow can't find that information, am I using it incorrectly, etc.?
The problem isn't with the call, it's with your assignment; you need parens around it:
if ( ( versionDataSize = GetFileVersionInfoSize(fileName, NULL) ) != 0)
What you had written assigns the value of the expression size != 0, which is 1 for true.

windows C++ opening printer with documentproperties get C6836 "Write Overrun" Code analysis warning

In the following code:
// If GetPrinter didn't fill in the DEVMODE, try to get it by calling
// DocumentProperties...
if (pi2->pDevMode == NULL)
{
dwNeeded = DocumentProperties(NULL, hPrinter,
printerName,
NULL, NULL, 0);
if (dwNeeded <= 0)
{
GlobalFree(pi2);
ClosePrinter(hPrinter);
return FALSE;
}
pDevMode = (DEVMODE *)GlobalAlloc(GPTR, dwNeeded);
if (pDevMode == NULL)
{
GlobalFree(pi2);
ClosePrinter(hPrinter);
return FALSE;
}
lFlag = DocumentProperties(NULL, hPrinter,
printerName,
pDevMode, NULL,
DM_OUT_BUFFER);
if (lFlag != IDOK || pDevMode == NULL)
{
GlobalFree(pDevMode);
GlobalFree(pi2);
ClosePrinter(hPrinter);
return FALSE;
}
pi2->pDevMode = pDevMode;
}
On the line
lFlag = DocumentProperties(NULL, hPrinter,
printerName,
pDevMode, NULL,
DM_OUT_BUFFER);
When I run Visual Studio 2012 "Code analysis" feature it throws warning:
C6386 Write overrun Buffer overrun while writing to 'pDevMode': the writable size is 'dwNeeded' bytes, but '220' bytes might be written. Invalid write to 'pDevMode', (outside its writable range)
The code functions fine, but wondering how to fix this warning from occurring (preferably without disabling warning)
The help page for this error does not seem to apply (or I can't figure how it does) http://msdn.microsoft.com/query/dev11.query?appId=Dev11IDEF1&l=EN-US&k=k(C6386)&rd=true
The SAL annotation for DocumentProperties cannot express that DEVMODE is a structure than might be larger than its declared size. The function doesn't also take an argument that states the passed size of the DEVMODE. Nor does the structure have a single field that states the size. So an annotation like __out_bcount_opt or __out_bcount_part could not be used.
It is an issue with all winapi functions that take a DEVMODE. It is a structure that dates from the stone age, long before SAL was ever on the horizon. If Microsoft could do it all over again then they would do this differently. Too late now.
Nothing you can do about it beyond knowing that you got it right and the tool got it wrong. It is just a warning.

How does _stat() under Windows exactly work

In my code I try to get the permissions for a file with _stat(). Currently I want to run it under Windows. The method is as follows:
bool CFile::Private::checkPermissions(std::string sFilename, CFile::EOpenmode iOpenmode)
{
std::string sErrMsg = "";
bool bResult = true;
struct _stat buf;
int iResult = 0;
// Get data associated with "crt_stat.c":
iResult = _stat( sFilename.c_str(), &buf );
// Check if statistics are valid:
if( iResult != 0 )
{
switch (errno)
{
case ENOENT:
sErrMsg = "File: " + sFilename + " not found.";
break;
case EINVAL:
sErrMsg = "Invalid parameter to _stat(filename, &buf).";
break;
default:
/* Should never be reached. */
sErrMsg = "Unexpected error in _stat(filename, &buf).";
}
throw std::runtime_error(sErrMsg);
}
else
{
if((iOpenmode & CFile::Read) && (!(buf.st_mode & S_IREAD)))
{
bResult = false;
}
if((iOpenmode & CFile::Write) && (!(buf.st_mode & S_IWRITE)))
{
bResult = false;
}
}
return bResult;
}
The only way to get 'false' for permission is to set the file's attribute 'read only'. Instead of this, set the security properties of the user (reject writing and reading) will get 'true' for checkPermissions(...). How to check both, the attributes and the user permissions for Windows?
Rumo
_stat is a function that is not native to Windows. It's a helper function to ease the porting of UNIX programs to Windows. But the UNIX file model just doesn't apply to Windows, so not all fields make sense. For instance, Windows has real ACL's, not rwx bits. There's just no way to fit all the ACL information in st_mode.
If you want to test ACL permissions, the proper way is to just try: call CreateFile() and check GetLastError(). Trying to get file permissions up front is not reliable as they can change at any time.
If we're talking about the same _stat() it's pretty clear from this MSDN article exactly what it does. Basically, you supply it a path to a file in question and a pointer to a _stat struct and it will dump the permissions to the struct if it returns 0.
The example C++ code in the article is pretty good.
As for testing user permissions, IsUserAnAdmin() does the job pretty well. You may be able to use this MSDN article for a different approach.
I hope this helps!

Why does GetLastError() return different codes during debug vs "normal" execution?

try
{
pConnect = sess->GetFtpConnection(ftpArgs.host, ftpArgs.userName, ftpArgs.password, port, FALSE );
}
catch (CInternetException* pEx)
{
loginErrCode = GetLastError();
printf("loginErrCode: %d\n", loginErrCode);
if(loginErrCode == 12013)
{
printf("Incorrect user name!\n");
exit(0);
}
else if(loginErrCode == 12014)
{
printf("Incorrect password!\n");
exit(0);
}
else if(loginErrCode == 12007)
{
printf("Incorrect server name!\n");
exit(0);
}
else //display all other errors
{
TCHAR sz[1024];
pEx->GetErrorMessage(sz, 1024);
printf("ERROR! %s\n, sz);
pEx->Delete();
exit(0);
}
When this code runs from visual studio with an intentional incorrect user name, GetLastError() returns 12014 (expected).
However, when running the same code from the command line (with the same exact incorrect username) GetLastError() returns 2? (the GetErrorMessage() does return incorrect password)
I do not understand what the difference is.
In addition, I ran the program from the command line while attaching the process to it in visual studio, to debug. I received 12014.
Whenever the debugger is involved, I get 12014. When I run the executable "normally" with the same parameters, I get 2.
Are the WinInet error codes not being found when I run the program outside of the debugger? Do I need to compile the program differently?
Any help is appreciated. Thank You.
My memory is a little hazy in this regard, but what happens if you use the m_dwError field of the CInternetException object instead of calling GetLastError()?
My guess is that something is causing the error code to be reset between when the actual error and your call to GetLastError(). I don't know why this happens when running outside the debugger but not inside the debugger. However, MFC caches the error code that caused the exception in the thrown object, so you should be able to use the cached value regardless of what API calls have happened since the exception was thrown.
GetErrorMessage() returns the correct error string because it uses this m_dwError field, rather than calling GetLastError().