在上篇文章开发 windows mobile 上的今日插件时,我发现 wince 平台上不支持例如 GetPrivateProfileString 等相关 API 函数。在网络上我并没有找到令我满意的相应代码,因此我手工自己写了相应的方法。命名规则是,在 PC API 函数的名称前面加上 “Ce” 前缀,这是为了在 PC 平台上调试和使用时,不和系统的 API 函数发生冲突。值得注意的是,在写 CeWritePrivateProfileString 方法时,如果改写后的 ini 文件应该比改写前的文件小,文件尾部将会是一些不确定内容(来自于原来文件)。在 PC 上我们可以通过 <io.h> 中的 _chsize 函数重新设置文件大小,但是很遗憾的是,这些底层的文件操作函数在 wince 平台上依然不被支持,但是幸运的是,可以使用 coredll.dll 中提供的SetEndOfFile 函数去完成相同功能(感谢88上的 kghost 的提示)。
  另外我额外提供了一个函数:CeGetPrivateProfileKeyNames,用于读取某个 section 的所有 key 名称。
  当然,如果是在 PC 平台,我们就没有必要使用这里我所提供的代码,因为有系统 API 可以调用。
  需要注意的是,我提供的代码和 PC 端 API 相比,基本功能,参数意义完全相同,但具有以下一些额外要求:
  (1)大小写敏感。(当然也可以通过修改代码,令其大小写不敏感)
  (2)每一行,section, key, value, “=” 的前后不允许有空格。
  (3)注释行用英文分号“;"起始。允许存在空行。
  (4)每一行的字符数不能超过 260 字符(取决于代码中的宏定义)。
  (5)函数代码同时适用 unicode 和多字节字符串 的环境。
  (6)由于采用标准文件操作函数,因此 CeGetPrivateProfileSectionNames 函数并不保证原子性。(这一点和 PC API 不同)

  下面是相关函数代码:
  (a) IniFile.h

  1. /***************************************
  2.  * IniFile.h
  3.  * 说明:在WinCe平台读写 INI 文件
  4.  * by hoodlum1980
  5.  * 2009.08.03 
  6.  ***************************************/
  7. #ifndef __INIFILE_H_BY_HOODLUM1980
  8. #define __INIFILE_H_BY_HOODLUM1980
  9.  
  10. //是否在WINCE平台上
  11. #ifndef WINCE
  12. #define WINCE
  13. #endif
  14.  
  15. #include "StdAfx.h"
  16. #ifndef WINCE
  17.     #include <io.h>        //for _sopen
  18.     #include <fcntl.h>    //for _O_RDWT
  19.     #include <share.h>    // for _SH_DENYRW
  20. #endif
  21.  
  22. #ifdef  UNICODE   // r_winnt
  23.     #define t_sopen            _wsopen        //注意WinCe上不支持!
  24.     #define t_fopen         _wfopen
  25.     #define t_fgets            fgetws
  26.     #define t_fprintf        fwprintf    //文件格式化写入
  27.     #define t_sprintf        swprintf    //格式化文本
  28.     #define t_strcpy        wcscpy
  29.     #define t_strncpy        wcsncpy        //拷贝指定个数的字符
  30.     #define t_strcat        wcscat        //append a string
  31.     #define t_strtol        wcstol
  32.     #define t_strlen        wcslen
  33.     #define t_strcmp        wcscmp
  34.     #define t_stricmp        _wcsicmp    //忽略大小写的字符串比较
  35.     #define t_strncmp        wcsncmp        //比较n个字符
  36.     #define t_strchr        wcschr        //find a character in a string
  37.     #define t_strrchr        wcsrchr        //从结尾向前查找字符
  38.  
  39. #else  //ASCII CODE
  40.  
  41.     #define t_sopen            _sopen        //注意WinCe上不支持!
  42.     #define t_fopen         fopen
  43.     #define t_fgets            fgets        //读取一行文本
  44.     #define t_fprintf        fprintf        //文件格式化写入
  45.     #define t_sprintf        sprintf        //格式化文本
  46.     #define t_strcpy        strcpy
  47.     #define t_strncpy        strncpy        //拷贝指定个数的字符
  48.     #define t_strcat        strcat        //append a string
  49.     #define t_strtol        strtol        //把字符串转换成long(int32)
  50.     #define t_strlen        strlen
  51.     #define t_strcmp        strcmp        //比较字符串
  52.     #define t_stricmp        _stricmp    //忽略大小写的字符串比较
  53.     #define t_strncmp        strncmp        //比较n个字符
  54.     #define t_strchr        strchr        //查找字符
  55.     #define t_strrchr        strrchr        //从结尾向前查找字符
  56.  
  57. #endif
  58.  
  59. //CeWritePrivateProfileString 方法用到的辅助标记
  60. #define MODE_DELETE_SECTION        11
  61. #define MODE_OVERWRITE_SECTION    12
  62. #define MODE_APPEND_SECTION        13
  63. #define MODE_DELETE_KEY            21
  64. #define MODE_OVERWRITE_KEY        22
  65. #define MODE_APPEND_KEY            23
  66.  
  67. #define LINESIZE                260    //行缓冲区大小
  68.  
  69. DWORD CeGetPrivateProfileString(
  70.     LPCTSTR lpAppName,                //section name: [lpAppName]
  71.     LPCTSTR lpKeyName,                //lpKeyName=lpReturnedString
  72.     LPCTSTR lpDefault,                //未找到时的默认值
  73.     LPTSTR lpReturnedString,        //[out] 查找到的结果
  74.     DWORD nSize,                    //[in]lpReturnedString的字符数,注意单位不是字节!
  75.     LPCTSTR lpFileName
  76.     );
  77.  
  78. UINT CeGetPrivateProfileInt(
  79.     LPCTSTR lpAppName,
  80.     LPCTSTR lpKeyName,
  81.     int nDefault,
  82.     LPCTSTR lpFileName
  83.     );
  84.  
  85. DWORD CeGetPrivateProfileSection(
  86.     LPCTSTR lpAppName,
  87.     LPTSTR lpReturnedString,
  88.     DWORD nSize,
  89.     LPCTSTR lpFileName
  90.     );
  91.  
  92. DWORD CeGetPrivateProfileSectionNames(
  93.     LPTSTR lpszReturnBuffer,
  94.     DWORD nSize,
  95.     LPCTSTR lpFileName
  96.     );
  97.  
  98. //在PC平台上可以调用_chsize函数调整文件大小,但是在WINCE平台上
  99. //由于不支持,所以必须注意当文件尺寸应该缩小时,文件尾部内容不确定!!!!
  100. BOOL CeWritePrivateProfileString(
  101.     LPCTSTR lpAppName,
  102.     LPCTSTR lpKeyName,    //要修改的KEY,如果为NULL,会删除整个Section
  103.     LPCTSTR lpString,    //要写入的值,如果为NULL,则会删除这个KEY
  104.     LPCTSTR lpFileName
  105.     );
  106.  
  107. //重写某个Section,注意和 PC API 的区别是,这里不保证原子性操作
  108. BOOL CeWritePrivateProfileSection(
  109.     LPCTSTR lpAppName,    //section name
  110.     LPCTSTR lpString,    //key1=val1 \0 key2=val2 \0\0
  111.     LPCTSTR lpFileName
  112.     );
  113.  
  114. //==============================================
  115. //        以下是我增加的函数(在API中没有)
  116. //==============================================
  117. DWORD CeGetPrivateProfileKeyNames(
  118.     LPCTSTR lpAppName,
  119.     LPTSTR lpReturnedString,
  120.     DWORD nSize,                //缓冲区的字符数
  121.     LPCTSTR lpFileName
  122.     );
  123.  
  124. #endif

  (b) IniFile.cpp

  1. //适用于 char* 和 UNICODE,
  2. //所有字符串必须使用 TEXT("aa") 或者 _T("aa") 的格式(自动适应 char* 或 UNICODE)
  3. //所有相关函数加t_前缀
  4. //IniFile: 读取INI FILE的简单解析!所谓简单,也就是解析代码简单,但对文件格式要求更高
  5. //[1]任何字符串前后不要有空格(使解析代码可以不考虑前后的trim)
  6. //            例如允许"Key1=Val", 而不允许" Key1 = Val "
  7. //[2]允许有注释,第一个字符必须是英文分号';'
  8. //
  9. #include "StdAfx.h"
  10. #include "IniFile.h"
  11.  
  12. //从appname(section)中读取string类型key
  13. DWORD CeGetPrivateProfileString(
  14.     LPCTSTR lpAppName,                //section name: [lpAppName]
  15.     LPCTSTR lpKeyName,                //lpKeyName=lpReturnedString
  16.     LPCTSTR lpDefault,                //未找到时的默认值
  17.     LPTSTR lpReturnedString,    //[out] 查找到的结果
  18.     DWORD nSize,                            //[in]lpReturnedString的字符数,注意单位不是字节!
  19.     LPCTSTR lpFileName
  20.     )
  21. {
  22.     DWORD ret = 0;
  23.     FILE *stream;
  24.     bool bFindVal = false;
  25.     bool bFindSection = false;
  26.     TCHAR line[ LINESIZE ];
  27.     size_t sectionLength, keyLength, lineLength;
  28.     
  29.     stream = t_fopen(lpFileName, _T("r"));
  30.     if(stream == NULL)
  31.     {
  32.         //设置默认值
  33.         t_strcpy(lpReturnedString, lpDefault);
  34.         ret = t_strlen(lpReturnedString); 
  35.         return ret;
  36.     }
  37.     
  38.     sectionLength = t_strlen(lpAppName);
  39.     
  40.     while(t_fgets(line, LINESIZE, stream) != NULL)
  41.     {
  42.         //忽略注释行和空行
  43.         if(line[0] == 0 || line[0] == ';') continue;
  44.         lineLength = t_strlen(line);
  45.         //注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
  46.         if(line[ lineLength - 1 ] == 0x0a)
  47.         {
  48.             line[ lineLength - 1 ] = 0;
  49.             lineLength--;
  50.             //注意此时可能会成为空字符串
  51.             if(lineLength == 0) continue;
  52.         }
  53.         
  54.         //尝试寻找到 section
  55.         if(!bFindSection)
  56.         {
  57.             if(line[0] != '[') continue; //本行是否是 [section]
  58.             //这里是我们想要的Section吗?
  59.             //检查这一行的宽度是否正好是section长度加2, [lpAppName]
  60.             if(line[sectionLength + 1] != ']') continue;
  61.             if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
  62.             //Now Section will appear on next line 
  63.             //读取section前求出 Key 的长度
  64.             keyLength = t_strlen(lpKeyName);
  65.             bFindSection = true;            
  66.             continue;
  67.         }
  68.         
  69.         //查找Key, Section End?
  70.         if(line[0]=='[') break; //遇到了下一个
  71.             
  72.         if(lineLength < keyLength+1 || line[keyLength] != '=') continue; //"KeyName="
  73.         if(t_strncmp(line, lpKeyName, keyLength)!=0) continue;
  74.         //Now We Get the Key! 
  75.         t_strcpy(lpReturnedString, line + keyLength + 1);
  76.         //Now It's done.
  77.         bFindVal = true;
  78.         break;
  79.     }
  80.     
  81.     fclose(stream);
  82.     if(!bFindVal)
  83.     {
  84.         //设置默认值
  85.         t_strcpy(lpReturnedString, lpDefault); 
  86.     }
  87.     ret = t_strlen(lpReturnedString); 
  88.     return ret;
  89. }
  90.  
  91. //读取一个int值
  92. UINT CeGetPrivateProfileInt(
  93.     LPCTSTR lpAppName,
  94.     LPCTSTR lpKeyName,
  95.     int nDefault,
  96.     LPCTSTR lpFileName
  97.     )
  98. {
  99.     long ret = nDefault; //返回值
  100.     FILE *stream;
  101.     bool bFindVal = false;
  102.     bool bFindSection = false;
  103.     TCHAR line[ LINESIZE ];
  104.     size_t sectionLength, keyLength, lineLength;
  105.     
  106.     stream = t_fopen(lpFileName, _T("r"));
  107.     if(stream == NULL)
  108.     {
  109.         //设置默认值
  110.         return nDefault;
  111.     }
  112.     
  113.     sectionLength = t_strlen(lpAppName);
  114.     
  115.     while(t_fgets(line, LINESIZE, stream) != NULL)
  116.     {
  117.         //忽略注释行和空行
  118.         if(line[0] == 0 || line[0] == ';') continue;
  119.         lineLength = t_strlen(line);
  120.         //注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
  121.         if(line[ lineLength - 1 ] == 0x0a)
  122.         {
  123.             line[ lineLength - 1 ] = 0;
  124.             lineLength--;
  125.             //注意此时可能会成为空字符串
  126.             if(lineLength == 0) continue;
  127.         }
  128.         
  129.         //尝试寻找到 section
  130.         if(!bFindSection)
  131.         {
  132.             if(line[0] != '[') continue; //本行是否是 [section]
  133.             //这里是我们想要的Section吗?
  134.             //检查这一行的宽度是否正好是section长度加2, [lpAppName]
  135.             if(line[sectionLength + 1] != ']') continue;
  136.             if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
  137.             //Now Section will appear on next line 
  138.             //读取section前求出 Key 的长度
  139.             keyLength = t_strlen(lpKeyName);
  140.             bFindSection = true;            
  141.             continue;
  142.         }
  143.         
  144.         //查找Key, Section End?
  145.         if(line[0]=='[') break; //遇到了下一个
  146.             
  147.         if(lineLength < keyLength+1 || line[keyLength] != '=') continue; //"KeyName="
  148.         if(t_strncmp(line, lpKeyName, keyLength)!=0) continue;
  149.         //Now We Get the Key! 
  150.         TCHAR *pStopChar = NULL;
  151.         ret = t_strtol(line + keyLength + 1, &pStopChar, 10); //默认为10进制
  152.         //Now It's done.
  153.         bFindVal = true;
  154.         break;
  155.     }
  156.     
  157.     fclose(stream);
  158.     return ret;
  159. }
  160.  
  161. //获取某个Section下面的所有“key=value”形式的字符串集合,以0字符分割
  162. //结尾使用两个0字符
  163. //缓冲区写入:"key1=value1 \0 key2=value2 \0 \0 "
  164. //返回值表示写入缓冲区的字符数, 不包括结尾的0字符。
  165. //如果缓冲区不够容纳所有的键值对,则返回值 = (nSize-2)
  166. DWORD CeGetPrivateProfileSection(
  167.     LPCTSTR lpAppName,
  168.     LPTSTR lpReturnedString,
  169.     DWORD nSize,                //缓冲区的字符数
  170.     LPCTSTR lpFileName
  171.     )
  172. {
  173.     DWORD ret = 0; //返回值,拷贝的字符数量
  174.     DWORD remainSize = nSize - 2; //缓冲区当前所能能够接纳的字符数量
  175.     DWORD copySize;                //本次循环中需要拷贝的字符数量
  176.     FILE *stream;
  177.     bool bFindSection = false; //是否已经找到Section
  178.     TCHAR line[ LINESIZE ];   //行缓冲区
  179.     LPTSTR pItem; //指向当前键值对的写入地址
  180.     size_t sectionLength, lineLength;
  181.     
  182.  
  183.     pItem = lpReturnedString; //指向缓冲区起始地址
  184.  
  185.     stream = t_fopen(lpFileName, _T("r"));
  186.     if(stream == NULL)
  187.     {
  188.         return ret;
  189.     }
  190.     
  191.     sectionLength = t_strlen(lpAppName);
  192.     
  193.     while(t_fgets(line, LINESIZE, stream) != NULL)
  194.     {
  195.         //缓冲区是否还有剩余空间?
  196.         if(remainSize <= 0) break;
  197.  
  198.         //忽略注释行和空行
  199.         if(line[0] == 0 || line[0] == ';') continue;
  200.         lineLength = t_strlen(line);
  201.         //注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
  202.         if(line[ lineLength - 1 ] == 0x0a)
  203.         {
  204.             line[ lineLength - 1 ] = 0;
  205.             lineLength--;
  206.             //注意此时可能会成为空字符串
  207.             if(lineLength == 0) continue;
  208.         }
  209.         
  210.         //尝试寻找到 section
  211.         if(!bFindSection)
  212.         {
  213.             if(line[0] != '[') continue; //本行是否是 [section]
  214.             //这里是我们想要的Section吗?
  215.             //检查这一行的宽度是否正好是section长度加2, [lpAppName]
  216.             if(line[sectionLength + 1] != ']') continue;
  217.             if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
  218.             //Now Section will appear on next line 
  219.             bFindSection = true;            
  220.             continue;
  221.         }
  222.         
  223.         //查找Key, Section End?
  224.         if(line[0]=='[') break; //遇到了下一个
  225.  
  226.         //copy the line to buffer, 注意ncpy不会复制结尾的0字符
  227.         copySize = min( remainSize, lineLength );
  228.         t_strncpy(pItem, line, copySize);
  229.         //追加一个0字符
  230.         pItem[copySize] = 0;
  231.  
  232.         //缩小缓冲区剩余字符数量remainSize,和当前写入位置pItem
  233.         ret += (copySize + 1); //加1是为了统计结尾的0字符
  234.         remainSize -= (copySize + 1);
  235.         pItem += (copySize + 1);
  236.     }
  237.     
  238.     fclose(stream);
  239.  
  240.     if(bFindSection)
  241.     {
  242.         //再次对缓冲区追加一个0 字符
  243.         *pItem = 0;
  244.     }
  245.     return ret;
  246. }
  247.  
  248. //获取一个ini文件中所有section的name,拷贝到缓冲区
  249. //注意和系统API的区别是,系统API的读取是原子性的,即读取时不允许修改ini文件的内容
  250. //而我们的函数未必保证这一点
  251. DWORD CeGetPrivateProfileSectionNames(
  252.     LPTSTR lpszReturnBuffer,
  253.     DWORD nSize,
  254.     LPCTSTR lpFileName
  255.     )
  256. {
  257.     DWORD ret = 0;                    //返回值,拷贝的字符数量
  258.     DWORD remainSize = nSize - 2;    //缓冲区当前所能能够接纳的字符数量
  259.     DWORD copySize;                    //本次循环中需要拷贝的字符数量
  260.     TCHAR line[ LINESIZE ];            //行缓冲区
  261.     TCHAR *pSectionEndChar;            //']'字符指针
  262.     LPTSTR pItem;                    //指向当前键值对的写入地址
  263.     FILE *stream;                    //流指针
  264.     size_t lineLength;                //行字符长度
  265.     
  266.     pItem = lpszReturnBuffer; //指向缓冲区起始地址
  267.  
  268.     stream = t_fopen(lpFileName, _T("r"));
  269.     if(stream == NULL)
  270.     {
  271.         return ret;
  272.     }
  273.     
  274.     while(t_fgets(line, LINESIZE, stream) != NULL)
  275.     {
  276.         //缓冲区是否还有剩余空间?
  277.         if(remainSize <= 0) break;
  278.  
  279.         //忽略注释行和空行
  280.         if(line[0] == 0 || line[0] == ';') continue;
  281.         lineLength = t_strlen(line);
  282.         //注意:把LF(0xa)字符替换成0,这在 UNICODE 环境下可能出现结尾是LF)
  283.         if(line[ lineLength - 1 ] == 0x0a)
  284.         {
  285.             line[ lineLength - 1 ] = 0;
  286.             lineLength--;
  287.             //注意此时可能会成为空字符串
  288.             if(lineLength == 0) continue;
  289.         }
  290.  
  291.         if(line[0] != '[') continue; //本行是否是 [section]
  292.         //找到了一个Section,开始拷贝
  293.         //copy the section name to buffer, 注意ncpy不会复制结尾的0字符
  294.  
  295.         //LINE: "[sectionName]"
  296.         //       |           |
  297.         //     line      pSectionEndChar
  298.  
  299.         //找出‘=’字符的位置
  300.         pSectionEndChar = t_strchr(line, ']');
  301.         if(pSectionEndChar != NULL)
  302.         {
  303.             //找到了‘=’字符,(pEqualChar - line)是key的长度
  304.             copySize = min( remainSize,  pSectionEndChar - line - 1 );
  305.         }
  306.         else
  307.         {
  308.             //本行中不存在‘]’字符,对于合法文件来说不会出现此种情况
  309.             copySize = min( remainSize, lineLength - 1 );
  310.         }
  311.  
  312.         t_strncpy(pItem, line+1, copySize);
  313.  
  314.         //追加一个0字符
  315.         pItem[copySize] = 0;
  316.  
  317.         //缩小缓冲区剩余字符数量remainSize,和当前写入位置pItem
  318.         ret += (copySize + 1); //加1是为了统计结尾的0字符
  319.         remainSize -= (copySize + 1);
  320.         pItem += (copySize + 1);
  321.     }
  322.  
  323.     fclose(stream);
  324.     //再次对缓冲区追加一个0 字符
  325.     *pItem = 0;
  326.     return ret;
  327. }
  328.  
  329. //
  330. BOOL CeWritePrivateProfileString(
  331.     LPCTSTR lpAppName,
  332.     LPCTSTR lpKeyName,    //要修改的KEY,如果为NULL,会删除整个Section
  333.     LPCTSTR lpString,    //要写入的值,如果为NULL,则会删除这个KEY
  334.     LPCTSTR lpFileName
  335.     )
  336. {
  337.     FILE *stream;
  338.     void *pVoid = NULL; //文件的后半部分
  339.     bool bFindKey = false;
  340.     bool bFindSection = false;
  341.     TCHAR line[ LINESIZE ];
  342.     size_t sectionLength, keyLength, lineLength, nBytesRead = 0;
  343.     LONG nInsertPos = -1, nCopyPos = -1, nFileEndPos, nPos; //文件指针位置
  344.     LONG nSectionBegin = -1, nKeyBegin = -1, nNextKey = -1, nNextSection = -1;
  345.     BYTE mode = 0;
  346.     
  347.     //如果 sectionName 为NULL,返回成功
  348.     if(lpAppName == NULL)
  349.         return true;
  350.  
  351.     //r+: Opens for both reading and writing. (The file must exist.)
  352.     stream = t_fopen(lpFileName, _T("r+"));
  353.     if(stream == NULL)
  354.     {
  355.         return false;
  356.     }
  357.  
  358.     //先取一次mode的默认值
  359.     if(lpKeyName == NULL)
  360.         mode = MODE_DELETE_SECTION;
  361.     else if(lpString == NULL)
  362.         mode = MODE_DELETE_KEY;
  363.     else
  364.         mode = MODE_OVERWRITE_KEY;
  365.  
  366.     sectionLength = t_strlen(lpAppName);
  367.     
  368.     //每次读行前,保存文件指针位置
  369.     while(nPos = ftell(stream), t_fgets(line, LINESIZE, stream) != NULL)
  370.     {
  371.         //忽略注释行和空行
  372.         if(line[0] == 0 || line[0] == ';') continue;
  373.         lineLength = t_strlen(line);
  374.         //注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
  375.         if(line[ lineLength - 1 ] == 0x0a)
  376.         {
  377.             line[ lineLength - 1 ] = 0;
  378.             lineLength--;
  379.             //注意此时可能会成为空字符串
  380.             if(lineLength == 0) continue;
  381.         }
  382.         
  383.         //尝试寻找到 section
  384.         if(!bFindSection)
  385.         {
  386.             if(line[0] != '[') continue; //本行是否是 [section]
  387.             //这里是我们想要的Section吗?
  388.             //检查这一行的宽度是否正好是section长度加2, [lpAppName]
  389.             if(line[sectionLength + 1] != ']') continue;
  390.             if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
  391.             //Now Section will appear on next line 
  392.             //读取section前求出 Key 的长度
  393.             if(lpKeyName != NULL)
  394.                 keyLength = t_strlen(lpKeyName);
  395.             
  396.             nSectionBegin = nPos;
  397.  
  398.             bFindSection = true;        
  399.             continue;
  400.         }
  401.         
  402.         //Section找到了,
  403.         //Section End ?
  404.         if(line[0]=='[') 
  405.         {
  406.             nNextSection = nPos;
  407.             break; //遇到了下一个
  408.         }
  409.  
  410.         //是否需要查找KEY?
  411.         if(lpKeyName != NULL)
  412.         {            
  413.             if(lineLength < keyLength+1 || line[keyLength] != '=') continue; //"KeyName="
  414.             if(t_strncmp(line, lpKeyName, keyLength) != 0) continue;
  415.             //Now We Get the Key! 
  416.             nKeyBegin = nPos;
  417.             nNextKey = ftell(stream); //要拷贝的起始位置
  418.             //Now It's done.
  419.             bFindKey = true;
  420.             break;
  421.         }
  422.     }
  423.  
  424.     //如果已经到达文件尾部,则追加换行
  425.     if(feof(stream))
  426.         t_fprintf(stream, _T("\r\n"));
  427.  
  428.     if(nNextSection < 0) nNextSection = ftell(stream);
  429.     if(nNextKey < 0) nNextKey = ftell(stream);
  430.  
  431.     //遍历后再次更新mode值
  432.     if(mode == MODE_DELETE_SECTION)
  433.     {
  434.         if(!bFindSection)
  435.         {
  436.             fclose(stream);
  437.             return true;
  438.         }
  439.         else
  440.         {
  441.             nInsertPos = nSectionBegin;
  442.             nCopyPos = nNextSection;
  443.         }
  444.     }
  445.     if(mode == MODE_DELETE_KEY)
  446.     {
  447.         if(!bFindKey)
  448.         {
  449.             fclose(stream);
  450.             return true;
  451.         }
  452.         else
  453.         {
  454.             nInsertPos = nKeyBegin;
  455.             nCopyPos = nNextKey;
  456.         }
  457.     }
  458.     if(mode == MODE_OVERWRITE_KEY)
  459.     {
  460.         if(!bFindSection)
  461.         {
  462.             mode = MODE_APPEND_SECTION;
  463.         }
  464.         else
  465.         {
  466.             if(bFindKey)
  467.             {
  468.                 nInsertPos = nKeyBegin;
  469.                 nCopyPos = nNextKey;
  470.             }
  471.             else
  472.             {
  473.                 mode = MODE_APPEND_KEY;
  474.                 nInsertPos = nNextSection;
  475.                 nCopyPos = nNextSection;
  476.             }
  477.         }
  478.     }
  479.  
  480.     //追加一个新的Section
  481.     if(mode == MODE_APPEND_SECTION)
  482.     {
  483.         t_fprintf(stream, _T("\r\n[%s]\r\n%s=%s\r\n"), lpAppName, lpKeyName, lpString);
  484.         fclose(stream);
  485.         return true;
  486.     }
  487.  
  488.     //先把文件的后半部分拷贝到内存
  489.     fseek(stream, 0, SEEK_END);
  490.     nFileEndPos = ftell(stream);
  491.     if(nCopyPos >= 0 && nCopyPos < nFileEndPos)
  492.     {
  493.         //分配内存作为缓冲区
  494.         pVoid = malloc(nFileEndPos - nCopyPos + 1);
  495.  
  496.         if(pVoid == NULL)
  497.         {
  498.             fclose(stream);
  499.             return false; //堆内存不足
  500.         }
  501.         fseek(stream, nCopyPos, SEEK_SET);
  502.         nBytesRead = fread(pVoid, 1, nFileEndPos - nCopyPos + 1, stream);
  503.     }
  504.  
  505.     //写入新的value值
  506.     fseek(stream, nInsertPos, SEEK_SET);
  507.     if(lpKeyName != NULL && lpString != NULL)
  508.         t_fprintf(stream, _T("%s=%s\r\n"), lpKeyName, lpString);
  509.  
  510.     //现在把文件的后半部分写回文件中
  511.     if(pVoid != NULL && nBytesRead > 0)
  512.     {
  513.         fwrite(pVoid, 1, nBytesRead, stream);
  514.         free(pVoid);
  515.     }
  516.  
  517.     //此时结尾可能还有一些内容,属于原来的ini文件
  518.     //我们把它写成注释
  519.     nPos = ftell(stream);
  520.     fclose(stream);
  521.  
  522.     //如果文件变小了,那么我们需要更改文件大小
  523.     if(nPos < nFileEndPos)
  524.     {
  525. #ifdef WINCE    //WINCE平台
  526.         HANDLE handle = CreateFile(
  527.             lpFileName, //LPCTSTR lpFileName
  528.             GENERIC_WRITE, //DOWRD dwDesiredAccess,
  529.             0, //DWORD dwShareMode, 非共享模式
  530.             NULL, //LPSECURITY_ATTRIBUTES lpSecurityAttributes, ignored
  531.             OPEN_EXISTING, //DWORD dwCreationDispostion, 
  532.             FILE_ATTRIBUTE_NORMAL, //DWORD dwFlagsAndAttributes, 
  533.             NULL//HANDLE hTemplateFile, ignored
  534.             ); 
  535.  
  536.         if(handle != NULL)
  537.         {
  538.             //移动文件指针
  539.             SetFilePointer(handle, nPos, NULL, FILE_BEGIN);
  540.             //设置EOF
  541.             SetEndOfFile(handle);
  542.             //关闭
  543.             CloseHandle(handle);
  544.         }
  545.  
  546. #else            //PC 平台
  547.         int handle = t_sopen(lpFileName, _O_RDWR, _SH_DENYRW);
  548.         if(handle > 0)
  549.         {
  550.             //修改文件大小
  551.             _chsize(handle, nPos);
  552.             //关闭文件
  553.             _close(handle);
  554.         }
  555. #endif //
  556.     }
  557.     return TRUE;
  558. }
  559.  
  560. //重写某个Section,注意和 PC API 的区别是,这里不保证原子性操作
  561. BOOL CeWritePrivateProfileSection(
  562.     LPCTSTR lpAppName,    //section name
  563.     LPCTSTR lpString,    //key1=val1 \0 key2=val2 \0\0
  564.     LPCTSTR lpFileName
  565.     )
  566. {
  567.     FILE *stream;
  568.     void *pVoid = NULL; //文件的后半部分
  569.     bool bFindSection = false;
  570.     TCHAR line[ LINESIZE ]; //行缓冲区
  571.     LPCTSTR pItem = lpString;
  572.     size_t sectionLength, lineLength, nBytesRead = 0;
  573.     LONG nFileEndPos, nPos; //文件指针位置
  574.     LONG nSectionBegin = -1, nNextSection = -1;
  575.     
  576.     //如果 sectionName 为NULL,返回失败
  577.     if(lpAppName == NULL || lpString == NULL)
  578.         return false;
  579.  
  580.     //r+: Opens for both reading and writing. (The file must exist.)
  581.     stream = t_fopen(lpFileName, _T("r+"));
  582.     if(stream == NULL)
  583.     {
  584.         return false;
  585.     }
  586.  
  587.     sectionLength = t_strlen(lpAppName);
  588.     
  589.     //每次读行前,保存文件指针位置
  590.     while(nPos = ftell(stream), t_fgets(line, LINESIZE, stream) != NULL)
  591.     {
  592.         //忽略注释行和空行
  593.         if(line[0] == 0 || line[0] == ';') continue;
  594.         lineLength = t_strlen(line);
  595.         //注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
  596.         if(line[ lineLength - 1 ] == 0x0a)
  597.         {
  598.             line[ lineLength - 1 ] = 0;
  599.             lineLength--;
  600.             //注意此时可能会成为空字符串
  601.             if(lineLength == 0) continue;
  602.         }
  603.         
  604.         //尝试寻找到 section
  605.         if(!bFindSection)
  606.         {
  607.             if(line[0] != '[') continue; //本行是否是 [section]
  608.             //这里是我们想要的Section吗?
  609.             //检查这一行的宽度是否正好是section长度加2, [lpAppName]
  610.             if(line[sectionLength + 1] != ']') continue;
  611.             if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
  612.             //Now Section will appear on next line 
  613.             nSectionBegin = nPos;
  614.             bFindSection = true;        
  615.             continue;
  616.         }
  617.         
  618.         //Section找到了,
  619.         //Section End ?
  620.         if(line[0]=='[') 
  621.         {
  622.             nNextSection = nPos;
  623.             break; //遇到了下一个
  624.         }
  625.     }
  626.  
  627.     //如果已经到达文件尾部,则追加换行
  628.     if(nNextSection < 0) nNextSection = ftell(stream);
  629.  
  630.     //追加一个新的Section
  631.     if(!bFindSection)
  632.     {
  633.         nSectionBegin = ftell(stream);
  634.     }
  635.  
  636.     //覆写Section
  637.     //先把文件的后半部分拷贝到内存
  638.     fseek(stream, 0, SEEK_END);
  639.     nFileEndPos = ftell(stream);
  640.     if(nNextSection >= 0 && nNextSection < nFileEndPos)
  641.     {
  642.         //分配内存作为缓冲区
  643.         pVoid = malloc(nFileEndPos - nNextSection + 1);
  644.  
  645.         if(pVoid == NULL)
  646.         {
  647.             fclose(stream);
  648.             return false; //堆内存不足
  649.         }
  650.         fseek(stream, nNextSection, SEEK_SET);
  651.         nBytesRead = fread(pVoid, 1, nFileEndPos - nNextSection + 1, stream);
  652.     }
  653.     
  654.     //逐行写入key = val
  655.     fseek(stream, nSectionBegin, SEEK_SET);
  656.     //再次写入[section],如果不存在就会追加
  657.     t_fprintf(stream, _T("[%s]\r\n"), lpAppName);
  658.     while(*pItem)
  659.     {
  660.         t_fprintf(stream, _T("%s\r\n"), pItem);
  661.         pItem += t_strlen(pItem) + 1; //移动到下一行
  662.     }
  663.  
  664.     //现在把文件的后半部分写回文件中
  665.     if(pVoid != NULL)
  666.     {
  667.         fwrite(pVoid, 1, nBytesRead, stream);
  668.         free(pVoid);
  669.     }
  670.  
  671.     //此时结尾可能还有一些内容,属于原来的ini文件
  672.     //我们把它写成注释
  673.     nPos = ftell(stream); //当前文件位置
  674.     fclose(stream);
  675.  
  676.     //如果文件变小了,那么我们需要更改文件大小
  677.     if(nPos < nFileEndPos)
  678.     {
  679. #ifdef WINCE    //WINCE平台
  680.         HANDLE handle = CreateFile(
  681.             lpFileName, //LPCTSTR lpFileName
  682.             GENERIC_WRITE, //DOWRD dwDesiredAccess,
  683.             0, //DWORD dwShareMode, 非共享模式
  684.             NULL, //LPSECURITY_ATTRIBUTES lpSecurityAttributes, ignored
  685.             OPEN_EXISTING, //DWORD dwCreationDispostion, 
  686.             FILE_ATTRIBUTE_NORMAL, //DWORD dwFlagsAndAttributes, 
  687.             NULL//HANDLE hTemplateFile, ignored
  688.             ); 
  689.  
  690.         if(handle != NULL)
  691.         {
  692.             //移动文件指针
  693.             SetFilePointer(handle, nPos, NULL, FILE_BEGIN);
  694.             //设置EOF
  695.             SetEndOfFile(handle);
  696.             //关闭
  697.             CloseHandle(handle);
  698.         }
  699.  
  700. #else            //PC 平台
  701.         int handle = t_sopen(lpFileName, _O_RDWR, _SH_DENYRW);
  702.         if(handle > 0)
  703.         {
  704.             //修改文件大小
  705.             _chsize(handle, nPos);
  706.             //关闭文件
  707.             _close(handle);
  708.         }
  709. #endif //
  710.     }
  711.     return TRUE;
  712. }
  713.  
  714. //===========================================================
  715. //        以下是我增加的函数(API中没有)
  716. //===========================================================
  717.  
  718. //
  719. //获取某个section下的所有的Key名,
  720. //获取某个Section下面的所有“key形式的字符串集合,以0字符分割
  721. //结尾使用两个0字符
  722. //缓冲区写入:"key1 \0 key2 \0 \0 "
  723. //返回值表示写入缓冲区的字符数, 不包括结尾的0字符。
  724. //如果缓冲区不够容纳所有的键值对,则返回值 = (nSize-2)
  725.  
  726. //注意:此函数是在桌面 API 中也没有的。而是我单独添加的
  727. //
  728. DWORD CeGetPrivateProfileKeyNames(
  729.     LPCTSTR lpAppName,
  730.     LPTSTR lpReturnedString,
  731.     DWORD nSize,                //缓冲区的字符数
  732.     LPCTSTR lpFileName
  733.     )
  734. {
  735.     DWORD ret = 0;                    //返回值,拷贝的字符数量
  736.     DWORD remainSize = nSize - 2;    //缓冲区当前所能能够接纳的字符数量
  737.     DWORD copySize;                    //本次循环中需要拷贝的字符数量
  738.     bool bFindSection = false;        //是否已经找到Section
  739.     TCHAR line[ LINESIZE ];            //行缓冲区
  740.     LPTSTR pItem;                    //指向当前键值对的写入地址
  741.     TCHAR *pEqualChar;                //等号字符的在行中的位置
  742.     FILE *stream;                    //流指针
  743.     size_t sectionLength, lineLength;
  744.  
  745.     
  746.     pItem = lpReturnedString; //指向缓冲区起始地址
  747.  
  748.     stream = t_fopen(lpFileName, _T("r"));
  749.     if(stream == NULL)
  750.     {
  751.         return ret;
  752.     }
  753.     
  754.     sectionLength = t_strlen(lpAppName);
  755.     
  756.     while(t_fgets(line, LINESIZE, stream) != NULL)
  757.     {
  758.         //缓冲区是否还有剩余空间?
  759.         if(remainSize <= 0) break;
  760.  
  761.         //忽略注释行和空行
  762.         if(line[0] == 0 || line[0] == ';') continue;
  763.         lineLength = t_strlen(line);
  764.         //注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
  765.         if(line[ lineLength - 1 ] == 0x0a)
  766.         {
  767.             line[ lineLength - 1 ] = 0;
  768.             lineLength--;
  769.             //注意此时可能会成为空字符串
  770.             if(lineLength == 0) continue;
  771.         }
  772.         
  773.         //尝试寻找到 section
  774.         if(!bFindSection)
  775.         {
  776.             if(line[0] != '[') continue; //本行是否是 [section]
  777.             //这里是我们想要的Section吗?
  778.             //检查这一行的宽度是否正好是section长度加2, [lpAppName]
  779.             if(line[sectionLength + 1] != ']') continue;
  780.             if(t_strncmp(line+1, lpAppName, sectionLength) != 0) continue;
  781.             //Now Section will appear on next line 
  782.             bFindSection = true;            
  783.             continue;
  784.         }
  785.         
  786.         //查找Key, Section End?
  787.         if(line[0]=='[') break; //遇到了下一个
  788.  
  789.         //copy the keyname to buffer, 注意ncpy不会复制结尾的0字符
  790.  
  791.         //LINE: "keyName = "
  792.         //       |       |
  793.         //     line   pEqualChar        
  794.  
  795.         //找出‘=’字符的位置
  796.         pEqualChar = t_strchr(line, '=');
  797.         if(pEqualChar != NULL)
  798.         {
  799.             //找到了‘=’字符,(pEqualChar - line)是key的长度
  800.             copySize = min( remainSize,  pEqualChar - line );
  801.         }
  802.         else
  803.         {
  804.             //本行中不存在‘=’字符,对于合法文件来说不会出现此种情况
  805.             copySize = min( remainSize, lineLength );
  806.         }
  807.  
  808.         t_strncpy(pItem, line, copySize);
  809.         //最佳一个0字符
  810.         pItem[copySize] = 0;
  811.  
  812.         //缩小缓冲区剩余字符数量remainSize,和当前写入位置pItem
  813.         ret += (copySize + 1); //加1是为了统计结尾的0字符
  814.         remainSize -= (copySize + 1);
  815.         pItem += (copySize + 1);
  816.     }
  817.     
  818.     fclose(stream);
  819.  
  820.     if(bFindSection)
  821.     {
  822.         //再次对缓冲区追加一个0 字符
  823.         *pItem = 0;
  824.     }
  825.     return ret;
  826. }

在 CeWritePrivateProfileString 函数中,用到的几个文件指针的含义是:假设我们要查找的 Section 是“section2”,Key 是“key2”;
      =============================
                                    ...
      nSectionBegin ->      [section2]
                                    ...
      nKeyBegin      ->      key2=value2
      nNextKey       ->      ...
                                    ...
      nNextSection  ->      [otherSection]
                                    ...
      =============================
      其他文件指针的含义是:nInsertPos - 新的KEY=Value开始写入位置; nCopyPos - 文件的后半部分在原始文件中的位置(整体不需要改写,但可能需要前移或后移),从这里到文件结尾的内容会在改写ini文件之前拷贝到内存,改写KEY后,再写回文件并附加到文件尾部。

  上面的代码中,包含 StdAfx.h 通常是因为默认设定,如果取消预编译头的选项,则可以不包含它。
  然后我们可以很方便对上面的代码进行测试:

#include <stdio.h>
#include "IniFile.h" int _tmain(int argc, _TCHAR* argv[])
{
    TCHAR buffer[128];
    int age;     CeGetPrivateProfileString(_T("section2"), _T("name"), _T("defaultValue"), buffer, t_strlen(buffer), _T("c:\\test.ini"));     age = CeGetPrivateProfileInt(_T("section2"), _T("age"), -1, _T("c:\\test.ini"));     //get section
    //CeGetPrivateProfileSection(_T("section2"), buffer, 128, _T("c:\\test.ini"));     //key names
    CeGetPrivateProfileKeyNames(_T("section2"), buffer, 128, _T("c:\\test.ini"));     //section names
    GetPrivateProfileSectionNames(buffer, 128, _T("c:\\test.ini"));     CeWritePrivateProfileString(_T("section2"), _T("key2"), _T("testValue"), _T("c:\\test.ini"));     wprintf(buffer);
    getchar();
    return 0;
} //假设 C:\\test.ini 的内容是:
//;testing ini file
//[section1]
//key1=aa
//[section2]
//name=myname
//age=20
//[section3]
//key1=bb

搜索

复制

在 WinCe 平台读写 ini 文件的更多相关文章

  1. c# 利用动态库DllImport("kernel32")读写ini文件(提供Dmo下载)

    c# 利用动态库DllImport("kernel32")读写ini文件 自从读了设计模式,真的会改变一个程序员的习惯.我觉得嘛,经验也可以从一个人的习惯看得出来,看他的代码编写习 ...

  2. VB读写INI文件的四个函数以及相关API详细说明

    WritePrivateProfileString函数说明  来源:http://blog.csdn.net/wjb9921/article/details/2005000 在我们写的程序当中,总有一 ...

  3. C# 读写INI 文件

    INI 格式: [Section1] KeyWord1 = Value1 KeyWord2 = Value2 ... [Section2] KeyWord3 = Value3 KeyWord4 = V ...

  4. WIN32读写INI文件方法

      在程序中经常要用到设置或者其他少量数据的存盘,以便程序在下一次执行的时候可以使用,比如说保存本次程序执行时窗口的位置.大小.一些用户设置的 数据等等,在 Dos 下编程的时候,我们一般自己产生一个 ...

  5. 读写ini文件

    C# 使用文件流来读写ini文件 背景 之前采用ini文件作为程序的配置文件,觉得这种结构简单明了,配置起来也挺方便.然后操作方式是通过WindowsAPI,然后再网上找到一个基于WindowsAPI ...

  6. Windows中读写ini文件

    .ini 文件是Initialization File的缩写,即初始化文件,是windows的系统配置文件所采用的存储格式,来配置应用软件以实现不同用户的要求.配置文件有很多种如ini配置文件,XML ...

  7. C#中读写INI文件

    C#中读写INI文件 c#的类没有直接提供对ini文件的操作支持,可以自己包装win api的WritePrivateProfileString和GetPrivateProfileString函数实现 ...

  8. Qt读写ini文件

    一 背景 1 ini文件介绍 .ini 文件是Initialization File的缩写,即初始化文件. 除了windows现在很多其他操作系统下面的应用软件也有.ini文件,用来配置应用软件以实现 ...

  9. 【python-ini】python读写ini文件

    [python-ini]python读写ini文件 本文实例讲述了Python读写ini文件的方法.分享给大家供大家参考.具体如下: 比如有一个文件update.ini,里面有这些内容:   1 2 ...

随机推荐

  1. js如何在指定页面跳转到另一指定页面

    要实现从一个页面A跳到另一个页面B,js实现就在A的js代码加跳转代码 JS跳转大概有以下几种方式: 第一种:(跳转到b.html)<script language="javascri ...

  2. JS总结之一:字符串的调用方法

    字符串的调用方法:var s="hello, world";document.write(s.charAt(0)); //第一个字符document.write(s.charAt( ...

  3. java代码中 路径符号的写法

    String path="D:\\新建文件夹\\2.png"; File file=new File(path); System.out.println(file.exists() ...

  4. lua学习

    在lua中,一切都是变量,除了关键字. 1.注释: 单行注释: 连续两个减号“--”表示注释的开始,一直延续到行末.相当于C语言中的“//” 多行注释:由“--[[”表示注释开始, “]]”表示注释结 ...

  5. unable to connect to :5555

    有可能批处理文件用的adb和eclipse的adb不兼容.把你的批处理文件用的adb换成eclipse的adb就可以了: 运行结果:

  6. MPEG1的码流层次与各层次的作用

    1. 序列层(Sequence layer) 序列层主要用于为随机播放提供全局参数支持,这些参数包括图像宽高.像素高宽比.帧率.码率.VBV大小.帧内量化矩阵.帧间量化矩阵. 2. 图像组层(Grou ...

  7. MVC验证生成的代码

  8. linux学习的哲学层面的思考-架构

    参考:http://blog.chinaunix.net/uid-26119273-id-3356414.html 学习Linux,准备做产品的话,不要把Linux当成了终极目标(当然,这是对应用而言 ...

  9. ActionResult的其它返回值

    我们上边所看到的Action都是return View();我们可以看作这个返回值用于解析一个aspx文件.而它的返回类型是ActionResult如 public ActionResult Inde ...

  10. POJ 3061 Subsequence 二分查找

    题目大意:给出长度为n的一个序列,给出一个数字S,求长度最短的序列和大于等于S的连续子序列,输出该长度,如果没有答案输出0. 题目思路:看数据范围,这道题就是卡时间的.我们可以用sum[i]记录前i项 ...