最近兩周,在做一個項目的移植,大部分時間是比較幸運的,大部分的API都被支持,多是一些VS2005語法要求更為嚴格后的一些常見錯誤,另外就是CE對完成例程的不支持,改為Select模型也就可以了,再就是ARM CPU平臺下常見的內(nèi)存對齊問題和據(jù)說也是常見的模板的兼容性問題。總的來說還是比較幸運的。 在此中間有兩個小的問題,一個是WinCE下沒有Ini文件的操作API,再就是沒有UuidCreate函數(shù)創(chuàng)建通用的GUID。這兩個問題都是比較普通常見的操作,于是開始我沒有太重視,以為隨便一搜索網(wǎng)上就會有一堆的現(xiàn)成代碼可用,因為太普通了。但事實證明我是錯誤的,至少ini操作比較完善的工具函數(shù)我就沒有找到合適的,GUID的創(chuàng)建倒是有個C#的版本,但C版本也沒有見到,后來想,可能是目前做手機開發(fā)的資源和人還是較少吧。 后來就不得不放棄自己懶惰的想法了,沒辦法,既然沒有就自己實現(xiàn)了。實際上我還是傾向于使用現(xiàn)成代碼的,因為代碼雖然簡單不多,但自己寫的話測試和調(diào)試還是有些費事,要保證可靠性還可能要考慮到一些自己可能會有些地方考慮不到,綜合別人的代碼可以彌補這些不足?,F(xiàn)在我花了些時間實現(xiàn),并基本詳細測試過了,所以也就拿出來共享一下,以讓后面用到的人能夠比我現(xiàn)在能省事些,畢竟一直以來大都是從互聯(lián)網(wǎng)拿,自己給予的太少,如果都和我這樣,估計就比較可怕了,也該拿出些東西來了,否則心里老是不安。 首先是創(chuàng)建GUID,這個主要的一點是盡可能和WinAPI的特性保持一致,否則可能會有重復的風險,這是一個難點,所以主要是要參考Rfc和MSDN,代碼如下: //------------------------------------------------------------------------------ // Win CE下生成Guid的文件:由于Win CE下不支持UuidCreate函數(shù),因此創(chuàng)建Uuid需要 // 自己實現(xiàn),僅僅用隨機數(shù)的方式不能保證同其它系統(tǒng)(PC)下的兼容性。從MSDN上找到 // 了Guid的實現(xiàn)算法,據(jù)文檔中描述,算法保持了同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文件操作相關(guān)函數(shù),根據(jù)我的需要,只實現(xiàn)了4個,代碼如下:
#ifdef UNDER_CE //======================================================================================================= // // WinCE下不支持ini文件操作的API,根據(jù)MSDN的幫助和Win32下測試的API的特性,模擬實現(xiàn)。測試發(fā)現(xiàn)API在不同 // 系統(tǒng)下的特性不完全一致,但區(qū)別不大,這里實現(xiàn)的是大部分XP系統(tǒng)下的特性 // //======================================================================================================= #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 這里用到了讀文件的函數(shù),每次讀一行,開始使用了CStdioFile的ReadString,PC下已經(jīng)測試通過了,但真正到了手機上發(fā)現(xiàn)受騙了,呵呵,手機上的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 |