面对现实,超越自己
逆水行舟,不进则退
posts - 269,comments - 32,trackbacks - 0

安装程序的使用

生成的安装程序和卸载程序接受一些命令行的参数。这些参数可以让用户在安装过程中对安装程序作部分控制。

公共选项

  • /NCRC 禁止 CRC 检测,除非在脚本里强制使用了 CRCCheck force
  • /S 静默运行安装程序或卸载程序。更多信息请参看 section 4.12
  • /D 指定默认的安装目录 ($INSTDIR), 可以越过 InstallDirInstallDirRegKey 。这个参数必须是最后一个参数并且不能带任何引号即使路径带有空格。仅支持绝对路径。

卸载程序特殊选项

  • _?= 指定 $INSTDIR。这也会阻止卸载程序把它自己复制到临时文件夹再运行。这将可以使用 ExecWait 来运行卸载程序并等待它完成。这个参数必须是最后一个参数并且不能带任何引号即使路径带有空格。

例子

1 installer.exe /NCRC
2 installer.exe /S
3 installer.exe /D=C:\Program Files\NSIS
4 installer.exe /NCRC //D=C:\Program Files\NSIS
5 uninstaller.exe /S _?=C:\Program Files\NSIS
# 静默卸载旧版本并等待它完成后才继续
1 ExecWait '"$INSTDIR\uninstaller.exe" /S _?=$INSTDIR'


以上信息来自NSIS帮助。下面为实例代码:

 1 !define APPLOCALPATH "Test.exe"
 2 !define APPPATH "$TEMP\${APPLOCALPATH}"
 3 
 4 OutFile "Test ${__DATE__}.exe"
 5 
 6 Section "Silent MakeNSIS"
 7         SetOutPath "$TEMP"
 8         File /r  "install\*.*"
 9         ExecWait '$TEMP\${APPLOCALPATH} /S /D=F:\Program Files\NSIS'
10         Delete '$TEMP\${APPLOCALPATH}'
11 SectionEnd


 

posted @ 2012-05-25 09:30 王海光 阅读(8446) | 评论 (0)编辑 收藏
本文转自:http://www.cppblog.com/humanchao/default.html?page=2

已知前序和中序:

struct NODE 
{
    NODE 
*pLeft;
    NODE 
*pRight;
    
char chValue;
};

int  CharInStrFirstPos(char ch, char *str, int nLen)
{
    
char *pOrgStr = str;
    
while (nLen > 0 && ch != *str)
    {
        str
++;
        nLen
--;
    }
    
    
return (nLen > 0? (str - pOrgStr) : -1;
}

void ReBuild_PreIn(char *pPreOrder, char *pInOrder, int nTreeLen, NODE **pRoot)
{
    
if (pPreOrder == NULL || pInOrder == NULL)
    {
        
return;
    }

    NODE 
*pTemp = new NODE;
    pTemp
->chValue = *pPreOrder;
    pTemp
->pLeft = NULL;
    pTemp
->pRight = NULL;

    
if (*pRoot == NULL)
    {
        
*pRoot = pTemp;
    }

    
if (nTreeLen == 1)
    {
        
return;
    }

    
int nLeftLen = CharInStrFirstPos(*pPreOrder, pInOrder, nTreeLen);
    assert(nLeftLen 
!= -1);
    
int nRightLen = nTreeLen - nLeftLen -1;

    
if (nLeftLen > 0)
    {
        ReBuild_PreIn(pPreOrder 
+ 1, pInOrder, nLeftLen, &((*pRoot)->pLeft));
    }

    
if (nRightLen > 0)
    {
        ReBuild_PreIn(pPreOrder 
+ nLeftLen + 1, pInOrder + nLeftLen + 1,
            nRightLen, 
&((*pRoot)->pRight));
    }
}

已知后序和中序:


void ReBuild_AftIn(char *pAftOrder, char *pInOrder, int nTreeLen, NODE **pRoot)
{
    
if (pAftOrder == NULL || pInOrder == NULL)
    {
        
return;
    }
    
    NODE 
*pTemp = new NODE;
    pTemp
->chValue = *pAftOrder;
    pTemp
->pLeft   = NULL;
    pTemp
->pRight  = NULL;
    
    
if (*pRoot == NULL)
    {
        
*pRoot = pTemp;
    }
    
    
if (nTreeLen == 1)
    {
        
return;
    }
    
    
int nLeftLen = CharInStrFirstPos(*pAftOrder, pInOrder, nTreeLen);
    assert(nLeftLen 
!= -1);
    
int nRightLen = nTreeLen - nLeftLen -1;
    
    
if (nLeftLen > 0)
    {
        ReBuild_AftIn(pAftOrder 
+ nRightLen + 1, pInOrder, nLeftLen, &((*pRoot)->pLeft));
    }
    
    
if (nRightLen > 0)
    {
        ReBuild_AftIn(pAftOrder 
+ 1, pInOrder + nLeftLen + 1,
            nRightLen, 
&((*pRoot)->pRight));
    }
}

我上传了一个工VC的工程,有兴点此下载趣的朋友。代码参考于《编程之美》。
posted @ 2012-05-24 13:57 王海光 阅读(860) | 评论 (1)编辑 收藏
本文转自:http://www.cnblogs.com/jillzhang/archive/2007/09/16/894936.html


递归算法学习系列二(归并排序)

      归并排序是利用递归和分而治之的技术将数据序列划分成为越来越小的半子表,再对半子表排序,最后再用递归步骤将排好序的半子表合并成为越来越大的有序序列,归并排序包括两个步骤,分别为:

      1)划分子表

      2)合并半子表 

     首先我们来讨论归并算法,归并算法将一系列数据放到一个向量中,索引范围为[first,last],这个序列由两个排好序的子表构成,以索引终点(mid)为分界线,以下面一个序列为例

    7,10,19,25,12,17,21,30,48

   这样的一个序列中,分为两个子序列 7,10,19,25  和 12,17,21,30,48,如下图所示:

   image 

再使用归并算法的时候的步骤如下:

 第一步:比较v[indexA]=7和v[indexB]=12,将较小的v[indexA]取出来放到临时向量tempArray中,然后indexA加1

  image

 

 第二步:比较v[indexA]=10和v[indexB]=12,将较小的10放到临时变量tempArray中,然后indexA++;

  image

第三步:比较v[indexA]=19与v[indexB]=12,将较小的12存放到临时变量tempArray中,然后indexB++;

   image

第四步到第七步:按照以上规则,进行比对和存储,得到如下结果:

   image

最后一步:将子表b中剩余项添加到临时向量tempArray中

   image 

然后将临时变量中的值按照索引位置,拷贝回向量v中,就完成了对向量v的归并排序

算法函数为:

 1 public void Merger(int[] v, int first, int mid, int last)
 2        {
 3            Queue<int> tempV = new Queue<int>();
 4            int indexA, indexB;
 5            //设置indexA,并扫描subArray1 [first,mid]
 6            //设置indexB,并扫描subArray2 [mid,last]
 7            indexA = first;
 8            indexB = mid;
 9            //在没有比较完两个子标的情况下,比较 v[indexA]和v[indexB]
10            //将其中小的放到临时变量tempV中
11            while (indexA < mid && indexB < last)
12            {
13                if (v[indexA] < v[indexB])
14                {
15                    tempV.Enqueue(v[indexA]);
16                    indexA++;
17                }
18                else
19                {
20                    tempV.Enqueue(v[indexB]);
21                    indexB++;
22                }
23            }
24            //复制没有比较完子表中的元素
25            while (indexA < mid)
26            {
27                tempV.Enqueue(v[indexA]);
28                indexA++;
29            }
30            while (indexB < last)
31            {
32                tempV.Enqueue(v[indexB]);
33                indexB++;
34            }
35            int index = 0;
36            while (tempV.Count > 0)
37            {
38                v[first+index] = tempV.Dequeue();
39                index++;
40            }
41        } 

    实现归并排序;归并排序算法分为两步,第一步:先将原来的数据表分成排好序的子表,然后调用 Merger  对子表进行归并,使之成为有序表,例如有如下向量:

  25,10,7,19,3,48,12,17,56,30,21

对此序列进行归并排序的步骤为:

   image

    归并算法函数为

 1 public void MergerSort(int[] v, int first, int last)
 2        {
 3            if (first + 1 < last)
 4            {
 5                int mid = (first + last) / 2;
 6                MergerSort(v, first, mid);
 7                MergerSort(v, mid, last);
 8                Merger(v, first, mid, last);
 9            }
10        } 

    归并算法的划分子表和归并子表与原数据序列次序无关,因此算法的最坏情况,最坏情况和平均情况时间复杂度是一样的

下面是归并算法的函数调用图

       image

示例程序: MergerSort.rar

posted @ 2012-05-23 14:01 王海光 阅读(505) | 评论 (0)编辑 收藏
通过API函数::GetPrinter()来获取打印机的相关信息。
 1 HANDLE hPrinter; 
 2 DWORD dwNeeded=0
 3 PRINTER_INFO_2* pPrinterInfo = NULL;
 4 PPRINTER_NOTIFY_INFO pNotify = NULL;
 5 ::OpenPrinter((LPTSTR)(LPCTSTR)"打印机名称"&hPrinter, NULL);
 6 ::GetPrinter(hPrinter,2,0,0,&dwNeeded);
 7 if (dwNeeded <= 0)
 8 {
 9     LOG("获取打印机信息长度dwNeeded=%d失败,添加打印机消息处理失败", dwNeeded);
10     return 0;
11 }
12 pPrinterInfo = new PRINTER_INFO_2[dwNeeded]; 
13 if(!::GetPrinter(hPrinter,2,(LPBYTE)pPrinterInfo,dwNeeded,&dwNeeded))
14 
15     DWORD dError = GetLastError();
16     LOG("获取打印信息失败,错误代码:%d,添加打印机消息处理失败", dError);
17     return 0;
18                          
19 
20 ::ClosePrinter(hPrinter); 
21 delete []pPrinterInfo; 

posted @ 2012-05-23 11:00 王海光 阅读(1053) | 评论 (0)编辑 收藏
本文转自:http://www.cppblog.com/fztcjjl/archive/2012/05/02/173434.html

Linux开发工具视频

工欲善其事必先利其器,《Linux开发工具使用》视频讲解Linux底下C/C++开发工具的使用。主要包括编译工具gcc、调试工具gdb、make(Makefile)工具以及当前流行的自动化编译工具cmake工具。

gcc入门(上)
本节课的主要内容有:
什么是gcc
gcc特点
gcc编译过程
gcc常用选项
gcc编译多文件
在线播放地址:http://www.cppcourse.com/gcc1.html
gcc入门(下)
本节课的主要内容有:
使用外部库
静态库与共享库
生成静态库
生成动态库
在线播放地址:http://www.cppcourse.com/gcc2.html
Makefile(上)
本节课的主要内容有:
make与Makefile介绍
Makefile基本规则
简单的Makefile编写
Make自动化变量
Makefile编译多个可执行文件
在线播放地址:http://www.cppcourse.com/makefile1.html
Makefile(下)
本节课的主要内容有:
make常用内嵌函数
多级目录Makefile
实用的Makefile
在线播放地址:http://www.cppcourse.com/makefile2.html
gdb入门(上)
本章的主要内容有:
什么是gdb
gdb功能
运行程序
查看源码
设置断点
单步调试
gdb常用命令总结
在线播放地址:http://www.cppcourse.com/gdb1.html
gdb入门(下)
本章的主要内容有:
查看运行时数据
程序错误
gdb调试逻辑错误
gdb调试段错误
core文件调试
在线播放地址:http://www.cppcourse.com/gdb2.html
posted @ 2012-05-22 11:29 王海光 阅读(391) | 评论 (0)编辑 收藏
     摘要: 用GetPrivateProfileString和WritePrivateProfileString函数读写文件操作。以及转载的一篇关于文件操作的文章  阅读全文
posted @ 2012-05-17 16:46 王海光 阅读(7111) | 评论 (0)编辑 收藏
本文转自:http://topic.csdn.net/u/20120329/15/ee6b04a0-a797-4f59-b538-5d8be71db9a4.html

大家都知道,当编写一个win32 console application时,当运行此类程序的时候默认情况下会有一个类似dos窗口的console窗口,但是有的时候我们只想在程序中运行一段功能代码,不希望显示这个console窗口,让代码执行完毕之后程序自动退出.下面就介绍一下,如何隐藏win32 console application的console窗口 .

因为此种方法是通过设置编译器的链接开关来实现,所以让我们来看一下编译器的链接开关选项(也就是linker选项).

首先我们来看一下linker的 /subsystem 选项该选项的语法形式如下:
/subsystem:{console|efi_application|efi_boot_service_driver|
efi_rom|efi_runtime_driver|native|posix|windows|windowsce}
[,major[.minor]]

这个链接选项告诉操作系统如何运行可执行文件

console: win32 字符模式应用程序,此种类型的应用程序在运行的时候会产生一个类似dos窗口的控制台窗口,如果在应用程序的主函数为main()或者wmain(),在默认情况下该应用程序就是一个控制台应用程序.

extensible firmware interface 和cpu具体架构相关的一个参数选项,并不常用,在这里暂不详细介绍.
如果对此有兴趣的可以访问intel主页来查看相关内容

native;设备驱动器选项,如果/driver:wdm选项被设定的话,该链接选项(native)就为默认选项

posix: 在windows nt 种运行在posix子系统上的应用程序

windows:该类型的应用程序不产生console窗口,该类型的应用程序的窗口由用户自己创建,简而言之
就是一个标准的win32 application,其入口地址为winmain()函数或者wwinmain()函数的地址
如果你在应用程序种定义的主函数为winmain或者wwinmain,在默认情况下该应用程序就是一个
win32 application !

windowsce:运行在windows ce上的应用程序

major and minor (optional):主版本号和次版本号,该选项为可选,该选项为0~65535之间的十进制整数

从上面可以看出如果我们建立一个win32 console application的话,linker的/subsystem选项应该为
console,可以在vc开发环境的project->setting->link->project option中看到!

接下来我们再看看应用程序是如何运行的!

我们知道用vc编写的程序,运行的时候是需要 cc 运行库支持的.当我们运行一个c/c 程序的时候链接器会首先寻找应用程序的启动函数,例如:

如果你建立了一个console程序的话,编译器得链接开关会是以下这种形式
/subsystem:console /entry:maincrtstartup (ansi)
/subsystem:console /entry:wmaincrtstartuup (unicode)

如果你建立了一个win32 application,编译器得链接开关则会是一下形式
/subsystem:windows /entry:winmain (ansi)
/sbusystem:windows /entry:wwinmain (uincode)

上面的两种形式可以再project->setting->link->project option中看到,上面的subsystem和entry并不需要都设置,如果你只设置了/subsystem:console的话,那么默认的entry开关在默认情况下应为/entry:maincrtstartup.反之,如果你在应用程序中定义了main函数的话,默认情况下,你/subsystem开关应该为/system:console.

在默认情况下/subsystem 和/entry开关是匹配的,也就是console对应maincrtstartup或者wmaincrtstartup.windows对应winmain或者wwinmain.但是我们也可以通过手动改动的方式使他们不匹配.例如我们可以这样改动

#pragma comment( linker, /subsystem:windows /entry:maincrtstartup ) // 设置入口地址
int main(int argc, char* argv[])
{
messagebox(null, hello, notice, mb_ok);
return 0;
}

在默认情况下链接器看到/subsystem下是windows选项的时候,它会自动寻找winmain或者wwinmain
但我们强制指定入口地址,这样运行程序的时候默认的console窗口就会隐藏!

上面是在代码中使用#pragma指令来设置,还有一种就是直接在开发环境的
project->setting->link->project option中手工改动!

总结如下:
1:如果console程序已经写好了,不能改了,也可以。写一个API程序,不要画窗口,然后用CreateProcess调用写好的console程序,把属性设成SW_HIDE即可。
2:不能用控制台来写(CONSLOE),要用WINMAIN做入口就可以了,不画窗口,别人就都看不见了. 你只用想想办法把你的进程在任务栏里面隐藏住就可以.
3:如果是console程序,用API函数GetStdHandle()获得控制台程序的窗口句柄,然后在隐藏窗口
4://这一句隐藏控制台
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )
你要写控制台程序就用这个.在控制台程序中隐藏控制台窗口!
posted @ 2012-05-17 16:10 王海光 阅读(521) | 评论 (0)编辑 收藏
查找指定进程:
 1 BOOL Test::FindProcess(const CString &sProcessName)
 2 {
 3     BOOL bResult = FALSE;
 4     HANDLE handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);//
 5     //PROCESSENTRY32 *info = new PROCESSENTRY32; 
 6     //info->dwSize=sizeof(PROCESSENTRY32); 
 7 
 8     PROCESSENTRY32 info; 
 9     info.dwSize = sizeof(PROCESSENTRY32); 
10     Process32First(handle, &info); 
11     while (Process32Next(handle, &info) != FALSE) 
12     {   
13         if (sProcessName.CompareNoCase(info.szExeFile) == 0)   
14         {   
15             bResult = TRUE;
16             break;
17         }   
18     } 
19     CloseHandle(handle);
20 
21     return bResult;
22 }

posted @ 2012-05-10 14:20 王海光 阅读(1060) | 评论 (0)编辑 收藏
     在MFC中,向线程传递CString变量参数时,很容易犯一个错误,就是使用一个超出生存期的变量,在主函数中定义的CString变量是局部变量,当主函数结束时,这个CString变量就会被析构,而却在另一个线程中继续使用,参数传递就会出现问题。
     解决此问题,可以在主函数中new一个CString,在线程结束时在delete一下释放内存。或者声明一个全局变量,保证CString变量不会超出生存期,这样传递进线程的参数就可以正常使用了。
     下面为示例代码: 
 1 DWORD WINAPI test::ProcessNotifyThread(LPVOID pParam)
 2 {
 3 
 4     CString* sTest = (CString*)pParam;
 5     //AfxMessageBox(*sTest);
 6     delete sTest;
 7     return 0 ;
 8 }
 9 
10 void test::OnBnClickedButton1()
11 {
12     // TODO: Add your control notification handler code here
13      CString *sTest = new CString;
14      *sTest = "hello";
15      LPVOID lpvoid=sTest;
16 
17     CWinThread* pThread = AfxBeginThread((AFX_THREADPROC)test::ProcessNotifyThread, lpvoid);
18 }
     CString类是很好用,但在多线程时最好不要用CString,因为MSDN明确说了,CString类是非线程安全的。
     下面为摘抄的:

     CString只保证类级的线程安全, 
     要做到对象级别的线程安全,需要你自己进行同步, 也就是说, 可以同时有N个线程在读, 但是写的时候,必须保证没有任何线程"正在"读和写 才可以写入.

1 CString str; 
2 
3 CCriticalSection cs; 
4 cs->Lock( ); 
5 str+="abcdefg";.. 
6 do anything you want 
7 cs->Unlock( );


线程传递char*

 1 DWORD WINAPI test::ProcessNotifyThread(LPVOID pParam)
 2 {
 3 
 4     char *pSectionName = (char *)lpvoid;
 5     CString sSectionName;
 6     sSectionName.Format("%s", pSectionName);     return 0 ;
 7  }
 8  
 9  void test::OnBnClickedButton1()
10  {
11      // TODO: Add your control notification handler code here
12       CString str = _T("aaaa");
13       const char *pSectionName = (LPCTSTR)str;     
14       CWinThread* pThread = AfxBeginThread((AFX_THREADPROC)test::ProcessNotifyThread, (LPVOID)pSectionName);
15  }

 

posted @ 2012-05-10 10:20 王海光 阅读(2575) | 评论 (0)编辑 收藏
    在Windows 2000下调试程序时,碰到一个问题:在导入一个win32 DLL时,LoadLibrary()函数返回值为空,GetLastError返回值为127 (找不到指定的程序)。其他DLL文件调用都正常。最后发现这个DLL文件中一个API函数WTSGetActiveConsoleSessionId()不支持2000系统,所以导致DLL文件总是导入不成功。
    加载dll失败,原因大致有:
    1.dll初始化时失败,这是因为dll本身有问题;
    2.dll需要的其他dll文件不存在;
    3.调用的dll再本地机器上不存在。
    
    解决方案: 用动态加载的方式
   
 1 typedef DWORD (WINAPI *LoadWTSGetActiveConsoleSessionId)(VOID);
 2 
 3             HMODULE hModKrl  =  LoadLibrary(TEXT("Kernel32.dll"));
 4             if (!hModKrl)  
 5             {  
 6                 LOG("导入 kernel32.dll 失败");
 7                 return;
 8             }  
 9 
10             LoadWTSGetActiveConsoleSessionId fWTSGetActiveConsoleSessionId  =  (LoadWTSGetActiveConsoleSessionId)GetProcAddress(hModKrl, "WTSGetActiveConsoleSessionId");
11             if (!fWTSGetActiveConsoleSessionId)
12             {
13                 LOG("调用WTSGetActiveConsoleSessionId 失败");
14                 return;
15             } 
16             dwSessionId = fWTSGetActiveConsoleSessionId();
17 
18             if (hModKrl)  
19             {  
20                 FreeLibrary(hModKrl);  
21             }
    使用动态加载dll,在Windows2000系统下成功,但获取WTSGetActiveConsoleSessionId函数入口地址失败。查找msdn发现此函数不支持2000系统。

   Requirements     

Minimum supported client

Windows XP

   Minimum supported server

Windows Server 2003

   Header

Winbase.h (include Windows.h)

   Library

Kernel32.lib

   DLL

Kernel32.dll
      

posted @ 2012-05-09 14:59 王海光 阅读(10546) | 评论 (0)编辑 收藏
仅列出标题
共27页: First 19 20 21 22 23 24 25 26 27