Hi
I have posted this problem already long time ago but I never found the time to test it again.
http://www.winehq.org/hypermail/wine-devel/2002/11/1530.html
But now I tried with the actual wine from cvs (2003-12-03) and it's still there. The problem is that if I try to create a table in a Access database the last char of the name gets lost. I now have a small program which shows this error, maybe someone can use it for finding this bug. It's a simple VC6 Wizard generated Win32 command line app which uses MFC for DAO.
The first time I tried was with SUSE 8.0, now I have Knoppix 3.3 (Debian).
Thanks
bye Fabi
---------------------------- mdbtest.cpp // mdbtest.cpp : Defines the entry point for the console application. //
#include "stdafx.h" #include <afxdao.h> // MFC DAO database classes
#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif
LPCTSTR lpszTableNodeTable = "Nodes"; LPCTSTR lpszTableNodeName = "NodeName"; LPCTSTR lpszTableNodeNodeType = "NodeType"; LPCTSTR lpszTableNodeBuildCL = "BuildCL"; LPCTSTR lpszTableNodeEditCL = "EditCL"; LPCTSTR lpszTableNodePath = "Path"; LPCTSTR lpszTableNodeFlags = "Flags"; LPCTSTR lpszTableNodeParam1 = "Param1"; LPCTSTR lpszTableNodeParam2 = "Param2"; LPCTSTR lpszTableNodeParam3 = "Param3"; LPCTSTR lpszTableNodeParam4 = "Param4";
///////////////////////////////////////////////////////////////////////////// // The one and only application object
CWinApp theApp;
using namespace std;
//-----------------------------------------------------------------------------
void CreateNodeTable(CDaoDatabase* pDB) // throws CDaoException and CMemoryException { CDaoTableDef nodeDef(pDB);
// attempt to create a new table def nodeDef.Create(lpszTableNodeTable);
CDaoFieldInfo DaoFieldInfo;
// init members DaoFieldInfo.m_strName = lpszTableNodeName; DaoFieldInfo.m_nType = dbText; DaoFieldInfo.m_lSize = 255; DaoFieldInfo.m_lAttributes = dbVariableField; DaoFieldInfo.m_nOrdinalPosition = 0; DaoFieldInfo.m_bRequired = FALSE; DaoFieldInfo.m_bAllowZeroLength = TRUE; DaoFieldInfo.m_lCollatingOrder = 0;
// create name field nodeDef.CreateField(DaoFieldInfo);
// create type field DaoFieldInfo.m_strName = lpszTableNodeNodeType; DaoFieldInfo.m_lSize = 30; nodeDef.CreateField(DaoFieldInfo);
// create path field DaoFieldInfo.m_strName = lpszTableNodePath; DaoFieldInfo.m_lSize = 255; nodeDef.CreateField(DaoFieldInfo);
// create flags field nodeDef.CreateField(lpszTableNodeFlags, dbLong, 4, dbFixedField);
// create build command line field DaoFieldInfo.m_strName = lpszTableNodeBuildCL; DaoFieldInfo.m_nType = dbMemo; nodeDef.CreateField(DaoFieldInfo);
// create edit command line field DaoFieldInfo.m_strName = lpszTableNodeEditCL; DaoFieldInfo.m_nType = dbMemo; nodeDef.CreateField(DaoFieldInfo);
DaoFieldInfo.m_nType = dbMemo; //DaoFieldInfo.m_lSize = 200; DaoFieldInfo.m_strName = lpszTableNodeParam1; nodeDef.CreateField(DaoFieldInfo); // create Param cl field DaoFieldInfo.m_strName = lpszTableNodeParam2; nodeDef.CreateField(DaoFieldInfo); // create Param cl field DaoFieldInfo.m_strName = lpszTableNodeParam3; nodeDef.CreateField(DaoFieldInfo); // create Param cl field DaoFieldInfo.m_strName = lpszTableNodeParam4; nodeDef.CreateField(DaoFieldInfo); // create Param cl field
nodeDef.Append(); // append table def to database
nodeDef.Close();
// end CreateNodeTable }
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { int nRetCode = 0;
// initialize MFC and print and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { // TODO: change error code to suit your needs cerr << _T("Fatal Error: MFC initialization failed") << endl; nRetCode = 1; } else { // TODO: code your application's behavior here. CString m_Path="test.mdb";
CDaoDatabase db; CDaoRecordset rsNode(&db); // rs node
try { // create database if not available cout << "Creating Database\n"; try { db.Create(m_Path); db.Close(); } catch (CDaoException* de) { // already exists I guess cout << "Database already existed\n"; de->Delete(); }
cout << "Opening Database\n"; // try to open database db.Open(m_Path, TRUE, FALSE, _T(""));
// --- delete old node and dependency tables and save new ones --- cout << "Deleting Tabledef\n"; try { // delete node table db.DeleteTableDef(lpszTableNodeTable); } catch (CDaoException* dde) { // catch error, maybe table def hasn't existed dde->Delete(); }
cout << "Creating new Tabledef\n"; // create node table CreateNodeTable(&db);
cout << "Opening Table\n"; // try to open node table rsNode.Open(dbOpenDynaset, CString(_T("Select * From [")) + lpszTableNodeTable + _T("]"), 0);
// look if recordset is updateable if (!rsNode.CanUpdate()) { cout << "No Update\n"; }
cout << "Closing Table\n"; // close tables rsNode.Close();
cout << "Closing Database\n"; // close database db.Close();
cout << "Deleting Database\n"; DeleteFile(m_Path); } catch (CDaoException* de) { // catch database exception char msg[256]; de->GetErrorMessage(msg, 256); cout << "Error:" << msg; de->Delete(); goto failure_exit; } catch (CMemoryException* me) { // catch memory exception char msg[256]; me->GetErrorMessage(msg, 256); cout << "Error:" << msg; me->Delete(); goto failure_exit; }
cout << "OK\n"; return TRUE;
failure_exit: // do some cleanup after failure if (rsNode.IsOpen()) rsNode.Close(); if (db.IsOpen()) db.Close();
}
cout << "ERROR!!\n";
return nRetCode; }
Hi,
On Wed, Dec 03, 2003 at 05:25:57PM +0100, Fabian Cenedese wrote:
Hi
I have posted this problem already long time ago but I never found the time to test it again.
http://www.winehq.org/hypermail/wine-devel/2002/11/1530.html
But now I tried with the actual wine from cvs (2003-12-03) and it's still there. The problem is that if I try to create a table in a Access database the last char of the name gets lost. I now have a small program which shows this error, maybe someone can use it for finding this bug. It's a simple VC6 Wizard generated Win32 command line app which uses MFC for DAO.
The first time I tried was with SUSE 8.0, now I have Knoppix 3.3 (Debian).
Why not fix it on your own? With a bit of luck, a +relay trace file should indicate which function fails to count properly...
Andreas Mohr
But now I tried with the actual wine from cvs (2003-12-03) and it's still there. The problem is that if I try to create a table in a Access database the last char of the name gets lost. I now have a small program which shows this error, maybe someone can use it for finding this bug. It's a simple VC6 Wizard generated Win32 command line app which uses MFC for DAO.
The first time I tried was with SUSE 8.0, now I have Knoppix 3.3 (Debian).
Why not fix it on your own?
Thanks for the advice. Of course I tried. I did one year ago ("Ein Jahr wie eine Ewigkeit..." - Falco) and I tried now. But I failed and that's why I'm calling for help.
With a bit of luck, a +relay trace file should indicate which function fails to count properly...
That's my problem, they all show up ok. strlen, SysAllocString etc all have the right length 5 (for "nodes") so I don't know how to continue. (Either I'm blind or it's not visible from these traces.)
bye Fabi
I have posted this problem already long time ago but I never found the time to test it again.
http://www.winehq.org/hypermail/wine-devel/2002/11/1530.html
But now I tried with the actual wine from cvs (2003-12-03) and it's still there. The problem is that if I try to create a table in a Access database the last char of the name gets lost. I now have a small program which shows this error, maybe someone can use it for finding this bug. It's a simple VC6 Wizard generated Win32 command line app which uses MFC for DAO.
The first time I tried was with SUSE 8.0, now I have Knoppix 3.3 (Debian).
I now tested my program with CodeWeavers' wine and it's the same error. My attempts to debug this were quite... unsuccessfull. If anyone wants to help me I could also send a compiled testprogram. I also found that it can happen not only on column names but also on table field names. But most work ok. So it can't be that a general error as a wrong string function (I guess a lot more programs would have problems if it were this basic). Another guess is Variants. There are quite some calls to Variant functions but I couldn't find a culprit.
Thanks
bye Fabi
I have posted this problem already long time ago but I never found the time to test it again.
http://www.winehq.org/hypermail/wine-devel/2002/11/1530.html
But now I tried with the actual wine from cvs (2003-12-03) and it's still there. The problem is that if I try to create a table in a Access database the last char of the name gets lost. I now have a small program which shows this error, maybe someone can use it for finding this bug. It's a simple VC6 Wizard generated Win32 command line app which uses MFC for DAO.
The first time I tried was with SUSE 8.0, now I have Knoppix 3.3 (Debian).
I also found that it can happen not only on column names but also on table field names. But most work ok. So it can't be that a general error as a wrong string function (I guess a lot more programs would have problems if it were this basic). Another guess is Variants. There are quite some calls to Variant functions but I couldn't find a culprit.
I think I found an error in SysStringLen. Why? Because my application started to work a lot better. Why not? I don't know if this is the right fix or other stuff is involved (as with the GetWindowLong).
* The length of the string (in bytes) is contained in a DWORD placed * just before the BSTR pointer */ bufferPointer = (DWORD*)str; bufferPointer--; return (int)(*bufferPointer/sizeof(WCHAR));
If I come here with a string like "Nodes" (normal string length 5) it will return (int)(5/2)=2. But 2 is definitely not enough (in WCHAR length). But if I do (int)((5+1)/2)=3 my name comes through. And also the other names that got truncated. So I would change it to:
return (int)((*bufferPointer+1)/sizeof(WCHAR));
Can anybody comment on this? Is this the right fix?
bye Fabi
I think I found an error in SysStringLen. Why? Because my application started to work a lot better. Why not? I don't know if this is the right fix or other stuff is involved (as with the GetWindowLong).
* The length of the string (in bytes) is contained in a DWORD placed * just before the BSTR pointer */
bufferPointer = (DWORD*)str; bufferPointer--; return (int)(*bufferPointer/sizeof(WCHAR));
If I come here with a string like "Nodes" (normal string length 5) it will return (int)(5/2)=2. But 2 is definitely not enough (in WCHAR length). But if I do (int)((5+1)/2)=3 my name comes through. And also the other names that got truncated. So I would change it to:
return (int)((*bufferPointer+1)/sizeof(WCHAR));
Can anybody comment on this? Is this the right fix?
Actually MSDN says about SysStringLen:
Return Value: The number of characters in bstr Comments: For a BSTR allocated with Sys[Re]AllocStringLen or SysAllocStringByteLen, this function always returns the number of characters specified in the cch parameter at allocation time
So this code looks strange. Either it should return *bufferPointer if not zero or then a real calculation _fstrlen(str)/sizeof(WCHAR). Or is MSDN wrong in this case?
bye Fabi
Hi,
On Tue, Dec 16, 2003 at 04:10:36PM +0100, Fabian Cenedese wrote:
I think I found an error in SysStringLen. Why? Because my application started to work a lot better. Why not? I don't know if this is the right fix or other stuff is involved (as with the GetWindowLong).
* The length of the string (in bytes) is contained in a DWORD placed * just before the BSTR pointer */
bufferPointer = (DWORD*)str; bufferPointer--; return (int)(*bufferPointer/sizeof(WCHAR));
If I come here with a string like "Nodes" (normal string length 5) it will return (int)(5/2)=2. But 2 is definitely not enough (in WCHAR length). But if I do (int)((5+1)/2)=3 my name comes through. And also the other names that got truncated. So I would change it to:
return (int)((*bufferPointer+1)/sizeof(WCHAR));
Can anybody comment on this? Is this the right fix?
Actually MSDN says about SysStringLen:
Return Value: The number of characters in bstr Comments: For a BSTR allocated with Sys[Re]AllocStringLen or SysAllocStringByteLen, this function always returns the number of characters specified in the cch parameter at allocation time
So this code looks strange. Either it should return *bufferPointer if not zero or then a real calculation _fstrlen(str)/sizeof(WCHAR). Or is MSDN wrong in this case?
Since SysAllocStringLen computes the amount of bytes needed for a given length of WCHAR characters, the DWORD before the string probably always should indicate twice as much as the string length for a WCHAR string. (I guess the DWORD before the string is to be seen strictly internal, right?) If you have a string like "Nodes", then the BSTR DWORD value should be 5*sizeof(WCHAR) == 5*2 == 10, *NOT* 5.
In other words, we don't have an even/odd division problem, but rather a problem with the string length, since it should have been twice as much.
Please try to find out which function created that BSTR with this wrong length calculation and try to fix it.
Thanks for your debugging!!
Andreas Mohr
that got truncated. So I would change it to:
return (int)((*bufferPointer+1)/sizeof(WCHAR));
Can anybody comment on this? Is this the right fix?
Actually MSDN says about SysStringLen:
Return Value: The number of characters in bstr Comments: For a BSTR allocated with Sys[Re]AllocStringLen or SysAllocStringByteLen, this function always returns the number of characters specified in the cch parameter at allocation time
So this code looks strange. Either it should return *bufferPointer if not zero or then a real calculation _fstrlen(str)/sizeof(WCHAR). Or is MSDN wrong in this case?
Since SysAllocStringLen computes the amount of bytes needed for a given length of WCHAR characters, the DWORD before the string probably always should indicate twice as much as the string length for a WCHAR string. (I guess the DWORD before the string is to be seen strictly internal, right?) If you have a string like "Nodes", then the BSTR DWORD value should be 5*sizeof(WCHAR) == 5*2 == 10, *NOT* 5.
In other words, we don't have an even/odd division problem, but rather a problem with the string length, since it should have been twice as much.
Please try to find out which function created that BSTR with this wrong length calculation and try to fix it.
Actually that's a normal WINAPI function called SysAllocStringByteLen. It's used in the DAO stuff from MFC. A COleVariant is created with explicit type VT_BSTRT. This happens in the constructor:
COleVariant::COleVariant(LPCTSTR lpszSrc, VARTYPE vtSrc) { USES_CONVERSION; ASSERT(vtSrc == VT_BSTR || vtSrc == VT_BSTRT); UNUSED(vtSrc);
vt = VT_BSTR; bstrVal = NULL;
if (lpszSrc != NULL) { #ifndef _UNICODE if (vtSrc == VT_BSTRT) { int nLen = lstrlen(lpszSrc); bstrVal = ::SysAllocStringByteLen(lpszSrc, nLen); } else #endif { bstrVal = ::SysAllocString(T2COLE(lpszSrc)); }
if (bstrVal == NULL) AfxThrowMemoryException(); } }
So not every BSTR is really a wide char string. That's why MSDN says that SysStringLen has to give back the allocated length if the BSTR was created with SysAllocStringByteLen.
I made a short test program:
COleVariant v1; VariantInit(&v1); v1=COleVariant("Nodes", VT_BSTRT); cout << SysStringLen(v1.bstrVal) << " " << (char*)v1.bstrVal << endl;
Output on Windows: 2 Nodes
Output on wine: 2 Node
(And you're right, the SysStringLen is ok, reports also 2 on Windows).
The SysAllocStringByteLen is ok, but the variant assignment is done with VariantCopy which is definitely wrong. The source string is still ok but the dest string is only "Node". But this function is above my knowledge. I don't know if the allocation/copy is wrong or if it needs a new case for these special non-WCHAR BSTRs.
(Another interesting test: What happens with one-char-strings...?)
bye Fabi
If you have a string like "Nodes", then the BSTR DWORD value should be 5*sizeof(WCHAR) == 5*2 == 10, *NOT* 5.
In other words, we don't have an even/odd division problem, but rather a problem with the string length, since it should have been twice as much.
Please try to find out which function created that BSTR with this wrong length calculation and try to fix it.
Actually that's a normal WINAPI function called SysAllocStringByteLen. It's used in the DAO stuff from MFC. A COleVariant is created with explicit type VT_BSTRT. This happens in the constructor:
COleVariant::COleVariant(LPCTSTR lpszSrc, VARTYPE vtSrc) { USES_CONVERSION; ASSERT(vtSrc == VT_BSTR || vtSrc == VT_BSTRT); UNUSED(vtSrc);
vt = VT_BSTR; bstrVal = NULL; if (lpszSrc != NULL) {
#ifndef _UNICODE if (vtSrc == VT_BSTRT) { int nLen = lstrlen(lpszSrc); bstrVal = ::SysAllocStringByteLen(lpszSrc, nLen); } else #endif { bstrVal = ::SysAllocString(T2COLE(lpszSrc)); }
if (bstrVal == NULL) AfxThrowMemoryException(); }
}
So not every BSTR is really a wide char string. That's why MSDN says that SysStringLen has to give back the allocated length if the BSTR was created with SysAllocStringByteLen.
I made a short test program:
COleVariant v1; VariantInit(&v1); v1=COleVariant("Nodes", VT_BSTRT); cout << SysStringLen(v1.bstrVal) << " " << (char*)v1.bstrVal << endl;
Output on Windows: 2 Nodes
Output on wine: 2 Node
(And you're right, the SysStringLen is ok, reports also 2 on Windows).
The SysAllocStringByteLen is ok, but the variant assignment is done with VariantCopy which is definitely wrong. The source string is still ok but the dest string is only "Node". But this function is above my knowledge. I don't know if the allocation/copy is wrong or if it needs a new case for these special non-WCHAR BSTRs.
(Another interesting test: What happens with one-char-strings...?)
Ok, I just tried and changed the code a bit. This now works with normal wide char strings as well as binary data (may as well be an ASCII string) with type BSTR. Though it works for me it may not be the right solution.
bye Fabi
Index: wine/dlls/oleaut32/variant.c =================================================================== RCS file: /home/wine/wine/dlls/oleaut32/variant.c,v retrieving revision 1.83 diff -u -r1.83 variant.c --- wine/dlls/oleaut32/variant.c 6 Jan 2004 22:08:34 -0000 1.83 +++ wine/dlls/oleaut32/variant.c 8 Jan 2004 10:05:25 -0000 @@ -727,7 +727,7 @@ { if (V_BSTR(pvargSrc)) { - V_BSTR(pvargDest) = SysAllocStringLen(V_BSTR(pvargSrc), SysStringLen(V_BSTR(pvargSrc))); + V_BSTR(pvargDest) = SysAllocStringByteLen((char*)V_BSTR(pvargSrc), SysStringByteLen(V_BSTR(pvargSrc))); if (!V_BSTR(pvargDest)) hres = E_OUTOFMEMORY; }