最近两周,在做一个项目的移植,大部分时间是比较幸运的,大部分的API都被支持,多是一些VS2005语法要求更为严格后的一些常见错误,另外就是CE对完成例程的不支持,改为Select模型也就可以了,再就是ARM CPU平台下常见的内存对齐问题和据说也是常见的模板的兼容性问题。总的来说还是比较幸运的。 在此中间有两个小的问题,一个是WinCE下没有Ini文件的操作API,再就是没有UuidCreate函数创建通用的GUID。这两个问题都是比较普通常见的操作,于是开始我没有太重视,以为随便一搜索网上就会有一堆的现成代码可用,因为太普通了。但事实证明我是错误的,至少ini操作比较完善的工具函数我就没有找到合适的,GUID的创建倒是有个C#的版本,但C版本也没有见到,后来想,可能是目前做手机开发的资源和人还是较少吧。 后来就不得不放弃自己懒惰的想法了,没办法,既然没有就自己实现了。实际上我还是倾向于使用现成代码的,因为代码虽然简单不多,但自己写的话测试和调试还是有些费事,要保证可靠性还可能要考虑到一些自己可能会有些地方考虑不到,综合别人的代码可以弥补这些不足。现在我花了些时间实现,并基本详细测试过了,所以也就拿出来共享一下,以让后面用到的人能够比我现在能省事些,毕竟一直以来大都是从互联网拿,自己给予的太少,如果都和我这样,估计就比较可怕了,也该拿出些东西来了,否则心里老是不安。 首先是创建GUID,这个主要的一点是尽可能和WinAPI的特性保持一致,否则可能会有重复的风险,这是一个难点,所以主要是要参考Rfc和MSDN,代码如下: //------------------------------------------------------------------------------ // Win CE下生成Guid的文件:由于Win CE下不支持UuidCreate函数,因此创建Uuid需要 // 自己实现,仅仅用随机数的方式不能保证同其它系统(PC)下的兼容性。从MSDN上找到 // 了Guid的实现算法,据文档中描述,算法保持了同PC下的基本一致性。 // // 参考:http://msdn2.microsoft.com/en-us/library/aa446557.aspx // //------------------------------------------------------------------------------ #pragma once #include <wincrypt.h> class PocketGuid { private: // One to three bits of the clock sequence section are used to define the variant, or layout, // of the GUID. Windows and the PocketGuid class generate variant type 2 GUIDs. enum GuidVariant { ReservedNCS = 0x00, Standard = 0x02, ReservedMicrosoft = 0x06, ReservedFuture = 0x07 }; // The upper four bits of the timestamp section contain the GUID's version that specifies the content of // each section. Before Windows 2000, the CoCreateGuid function generated version 1 GUIDs. With Windows 2000, // Microsoft switched to version 4 GUIDs, since embedding the MAC address was viewed as a security risk. // The PocketGuid class also generates version 4 GUIDs. enum GuidVersion { TimeBased = 0x01, Reserved = 0x02, NameBased = 0x03, Random = 0x04 }; class Const { // number of bytes in guid public: static const int ByteArraySize = 16; // multiplex variant info static const int VariantByte = 8; static const int VariantByteMask = 0x3f; static const int VariantByteShift = 6; // multiplex version info static const int VersionByte = 7; static const int VersionByteMask = 0x0f; static const int VersionByteShift = 4; }; public: static HRESULT CreateGuid (GUID & guid) { HCRYPTPROV hCryptProv = NULL; HRESULT hr = S_OK; // holds random bits for guid BYTE * bits = new BYTE[Const::ByteArraySize]; if (bits == NULL) { hr = E_OUTOFMEMORY; goto ExitHere; } // get crypto provider handle if (!CryptAcquireContext (&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { hr = HRESULT_FROM_WIN32 (GetLastError ()); goto ExitHere; } // generate a 128 bit (16 byte) cryptographically random number if (!CryptGenRandom (hCryptProv, Const::ByteArraySize, bits)) { hr = HRESULT_FROM_WIN32 (GetLastError ()); goto ExitHere; } // set the variant bits[Const::VariantByte] &= Const::VariantByteMask; bits[Const::VariantByte] |= ((int)Standard << Const::VariantByteShift); // set the version bits[Const::VersionByte] &= Const::VersionByteMask; bits[Const::VersionByte] |= ((int)Random << Const::VersionByteShift); memcpy (&guid, bits, sizeof (GUID)); ExitHere: if (hCryptProv != NULL) CryptReleaseContext (hCryptProv, 0); if (bits != NULL) delete [] bits; return hr; }; };
ini文件操作相关函数,根据我的需要,只实现了4个,代码如下:
#ifdef UNDER_CE
//======================================================================================================= // // WinCE下不支持ini文件操作的API,根据MSDN的帮助和Win32下测试的API的特性,模拟实现。测试发现API在不同 // 系统下的特性不完全一致,但区别不大,这里实现的是大部分XP系统下的特性 // //=======================================================================================================
#define GetPrivateProfileSectionNames GetMyProfileSectionNames #define GetPrivateProfileSection GetMyProfileSection #define WritePrivateProfileString WriteMyProfileString #define GetPrivateProfileString GetMyProfileString
BOOL WriteMyProfileString (LPCTSTR section, LPCTSTR keyName, LPCTSTR value, LPCTSTR filePath) { CIniFile iniFile; CString str = _T ("");
CString strSection = section; CString strKeyName = keyName; CString strValue = value;
if (!iniFile.Open (filePath, CFile::modeRead | CFile::modeCreate | CFile::modeNoTruncate)) return FALSE;
CStringArray strArray; while (iniFile.ReadLine (str)) { strArray.Add (str); } iniFile.Close ();
BOOL foundKey = FALSE; BOOL addValue = FALSE; for (int i = 0; i < strArray.GetSize (); i++) { str = strArray[i]; str = str.TrimLeft ().TrimRight (); if (str.GetAt (0) == ',' || str.GetAt (0) == '#') continue;
if (!foundKey) { if (str.GetAt (0) == '[' && str.Right (str.GetLength () - 1).TrimLeft ().Find (strSection) == 0) { foundKey = TRUE; if (keyName == NULL) { strArray.RemoveAt (i); i --; } } } else { // enter new key's area if (str.GetAt (0) == '[') { if (keyName != NULL) { strArray.InsertAt (i, strKeyName + _T ("=") + strValue + _T ("\r")); addValue = TRUE; } break; } else if (keyName == NULL) { strArray.RemoveAt (i); i --; } else if (str.Find (strKeyName) == 0) { CString rightValue = str.Right (str.GetLength () - strKeyName.GetLength ()).TrimLeft (); if (rightValue.Find (_T ("=")) == 0) { if (value != NULL) { strArray[i] = strKeyName + _T ("=") + strValue + _T ("\r"); addValue = TRUE; } else { strArray.RemoveAt (i); } break; } } } } if (!addValue && keyName != NULL && value != NULL) { if (!foundKey) strArray.Add (_T ("[") + strSection + _T ("]") + _T ("\r")); strArray.Add (strKeyName + _T ("=") + strValue + _T ("\r")); }
if (!iniFile.Open (filePath, CFile::modeCreate | CFile::modeWrite)) return FALSE;
for (int i = 0; i < strArray.GetSize (); i++) { iniFile.WriteString (strArray[i]); } iniFile.Close (); return TRUE; }
void GetMyProfileString (LPCTSTR section, LPCTSTR keyName, LPCTSTR defaultStr, CString & returnedStr, LPCTSTR fileName) { CString strSection = section; CString strKeyName = keyName; returnedStr = defaultStr; if (strSection == _T ("") || strKeyName == _T ("")) return;
CIniFile iniFile; CString str = _T ("");
if (!iniFile.Open (fileName, CFile::modeRead)) return;
BOOL foundKey = FALSE; while (iniFile.ReadLine (str)) { str = str.Trim (); if (str.GetAt (0) == ',' || str.GetAt (0) == '#') continue;
if (!foundKey) { if (str.GetAt (0) == '[' && str.Right (str.GetLength () - 1).TrimLeft ().Find (strSection) == 0) foundKey = TRUE; } else { // enter new key's area if (str.GetAt (0) == '[') { foundKey = FALSE; break; } else if (str.Find (strKeyName) == 0) { CString rightValue = str.Right (str.GetLength () - strKeyName.GetLength ()).TrimLeft (); if (rightValue.Find (_T ("=")) == 0) { returnedStr = rightValue.Right (rightValue.GetLength () - 1).Trim (); break; } } } } iniFile.Close (); }
DWORD GetMyProfileString (LPCTSTR section, LPCTSTR keyName, LPCTSTR defaultStr, LPTSTR returnedString, DWORD size, LPCTSTR filePath) { if (returnedString == NULL || size == 0) return 0;
if (size == 1) { returnedString[0] = '\0'; return 0; }
CString str = _T (""); GetMyProfileString (section, keyName, defaultStr, str, filePath);
DWORD available = size - 1;
if (available < (DWORD)str.GetLength ()) str = str.Left (available);
wcscpy_s (returnedString, size, str.GetBuffer ()); int allocSize = str.GetLength ();
returnedString[allocSize + 1] = '\0'; return allocSize; }
DWORD GetMyProfileSectionNames (LPTSTR returnBuffer, DWORD size, LPCTSTR fileName) { if (returnBuffer == NULL || size == 0) return 0; if (size <= 2) { returnBuffer[0] = '\0'; return 0; }
CIniFile iniFile; if (!iniFile.Open (fileName, CFile::modeRead)) { returnBuffer[0] = '\0'; return 0; }
CString str = _T (""); DWORD allocSize = 0; DWORD available = size - 1; while (iniFile.ReadLine (str) && available > 1) { str = str.Trim (); if (str.GetAt (0) == ',' || str.GetAt (0) == '#') continue;
if (str.GetAt (0) == '[') { CString section = str.Right (str.GetLength () - 1).Trim (); int pos = section.Find (_T ("]")); if (pos >= 0) section = section.Left (pos).Trim ();
if (available < (DWORD)section.GetLength ()) section = section.Left (available - 1);
wcscpy_s (returnBuffer + allocSize, size - allocSize, section.GetBuffer ()); int curAlloc = section.GetLength () + 1; allocSize += curAlloc; ASSERT (available >= (DWORD)curAlloc); available -= curAlloc; } } returnBuffer[allocSize] = '\0'; return allocSize > 1 ? allocSize -1 : 0; }
DWORD GetMyProfileSection (LPCTSTR section, LPTSTR returnedString, DWORD size, LPCTSTR fileName) { if (returnedString == NULL || size == 0 || section == NULL) return 0;
if (size <= 2) { returnedString[0] = '\0'; return 0; }
CIniFile iniFile; if (!iniFile.Open (fileName, CFile::modeRead)) { returnedString[0] = '\0'; return 0; } CString strSection = section;
CString str = _T (""); DWORD allocSize = 0; DWORD available = size - 1; BOOL foundKey = FALSE; while (iniFile.ReadLine (str) && available > 1) { str = str.Trim (); if (str.GetAt (0) == ',' || str.GetAt (0) == '#') continue;
if (!foundKey) { if (str.GetAt (0) == '[' && str.Right (str.GetLength () - 1).TrimLeft ().Find (strSection) == 0) foundKey = TRUE; } else { // enter new key's area if (str.GetAt (0) == '[') { foundKey = FALSE; break; }
CString entry = _T (""); int pos = str.Find (_T ("=")); if (pos >= 0) { CString left = str.Left (pos).Trim (); CString right = str.Right (str.GetLength () - pos - 1).Trim (); entry = left + _T ("=") + right; } else { entry = str; }
if (available < (DWORD)entry.GetLength ()) entry = entry.Left (available - 1);
wcscpy_s (returnedString + allocSize, size - allocSize, entry.GetBuffer ()); int curAlloc = entry.GetLength () + 1; allocSize += curAlloc; ASSERT (available >= (DWORD)curAlloc); available -= curAlloc; } } returnedString[allocSize] = '\0'; return allocSize > 1 ? allocSize -1 : 0; }
//======================================================================================================= #endif //UNDER_CE
这里用到了读文件的函数,每次读一行,开始使用了CStdioFile的ReadString,PC下已经测试通过了,但真正到了手机上发现受骗了,呵呵,手机上的ReadString的特性变了,具体可以查看MFC中的源码,粗略读了一下它的代码,似乎CE下ReadString的特性是:如果文件大小不足128,则全部读出,否则只读一行;也就是可能超过一行,所以不符合需求了。于是简单的自己写了一个能支持真正读一行的类:
#ifdef UNDER_CE class CIniFile : public CStdioFile { public: CIniFile() {} ~CIniFile() {}
BOOL ReadLine (CString& rString) { ASSERT_VALID(this);
ULONGLONG nStartPosition = CFile::GetPosition(); bool fEndOfFile = false;
rString = _T(""); // empty string without deallocating const int nMaxSize = 128; LPTSTR lpsz = rString.GetBuffer(nMaxSize); int nLen = 0; for (;;) { // Reading is not buffered, just let the internal OS buffer handle it. UINT nRead = CFile::Read( reinterpret_cast<void*>(lpsz), (nMaxSize)*sizeof(*lpsz) // convert from characters to bytes );
nRead /= sizeof(*lpsz); // convert from bytes to characters
if(nRead != nMaxSize) { // reached end of file, terminate the string here. fEndOfFile = true; //nLen = nRead; //break; }
// check for end of line character for(nLen = 0 ; nLen < static_cast<int>(nRead) && lpsz[nLen] != '\n' ; ++nLen) ;
if(lpsz[nLen]=='\n') { // nLen is correct location of end of line, which should be replaced with a null terminator fEndOfFile = false; break; }
if (fEndOfFile) { nLen = nRead; break; }
rString.ReleaseBuffer();
nLen = rString.GetLength(); lpsz = rString.GetBuffer(nMaxSize + nLen) + nLen; }
lpsz[nLen] = '\0'; rString.ReleaseBuffer();
nLen = rString.GetLength();
// Reset the file pointer to the position immediately after the end of line character if(!fEndOfFile) { CFile::Seek(nStartPosition + (nLen + 1) * sizeof(TCHAR), begin); } return nLen != 0; } }; #endif
|