200 分求打包程序 代码!建设性意见也算, 分不够可再(1000分封顶)

解决方案 »

  1.   

    不知中国人有没有对.exe结构很清楚的人
      

  2.   

    先找到应用程序调用了哪些动态链接库文件(.dll),分析哪些是系统自带的,如user32.dll,把不是自带的copy到程序文件夹下,再找到应用程序调用了哪些外部资源,把它们放到正确的程序搜索目录下,然后打成一个压缩包,再用CFile生成一个解压安装程序,这个程序运行后询问用户安装目录等后,解压压缩包并copy到安装目录中,并在桌面和开始菜单里加入一个快捷图标,并将该应用程序的一些信息记录到注册表中.
    个人认为实现起来非常困难
      

  3.   

    我有打包代码一个,不知道是不是你需要的那种[email protected]
      

  4.   

    直接使用InstallShield等就可以直接做打包程序了
      

  5.   

    最后一步生成 exe 的操作不要,应该说难道不高,但很多功能,要很多时间,很烦,只要注册表操作,覆盖文件操作, 这些
      

  6.   

    同意 flyelf(空谷清音),现在的打包程序很多,InstallShield就是VC自带的,网上也多的是。
      

  7.   

    打包程序是很多, 我想的是编写打包程序的一部分(除了最后生成 exe 程序这部分)
      

  8.   

    自动安装程序的实现算法和源代码
    李安东
    关键字: 自解压 自动安装程序    假如我的程序要调用一个setup.exe程序,自动安装一个软件,完成安装后再把临时文件全部删除,应怎样实现呢?虽然很简单,但有一个问题需要解决,就是如何判断何时已经安装完成了呢?当然可以用
    //Wait for until it terminated:
     while(GetExitCodeProcess(newinfo.hProcess,&dwExitCode)&&
      dwExitCode==STILL_ACTIVE);
        来等待setup.exe运行结束,但是问题可能并不这么简单,常常是setup.exe又调用了别的子进程(例如_delis和inst5176什么的),而setup.exe退出后,子进程并未退出,即任务仍未完成。因此这时删除临时文件和文件夹仍然会导致安装失败和删除文件失败。(我判断早期的WinRAR创建的TempMode自解压文件,在启动setup.ex后安装之所以会失败,可能就是因为判断错误,即在未完成安装时就把临时文件删除了。)
    这个问题可以按如下方法解决(供参考):
    1、用系统函数CreateEvent()创建一个事件hEvent;
    2、启动释放在临时目录(比如C:\WINDOWS\TEMP\MYTEMP)下的setup.exe后,然后执行如下语句:
    //Wait for the self-extract process exit:
     ::ResetEvent(hEvent);
     while(::WaitForSingleObject(hEvent,500)==WAIT_TIMEOUT)
     {  IsExit(); }
    即先将事件hEvent复位到无信号状态,并循环调用IsExit()函数;
    3、在IsExit()函数中列举系统中所有进程:
    (1)、调用系统函数CreateToolhelp32Snapshot()并指定TH32CS_SNAPPROCESS参数,获取一个系统中所有进程的列表(snapshot);
    (2)、调用系统函数Process32First()获取第一个进程的信息;
    (3)、循环调用系统函数Process32Next()获取其余进程的信息。
    上述函数调用中有一个参数lppe是一个PROCESSENTRY32类型的结构。lppe.th32ProcessID参数包含了获取的进程标识符;lppe.szExeFile为该进程的可执行文件路径和名称。
    因此在上述处理过程中,每次获取lppe后均判断lppe.szExeFile 中的路径是否是安装程序所在的临时目录,如果不存在这样的进程,则说明安装已经完成,则调用SetEvent()函数,将hEvent事件设置为有信号,从而使第二步中的循环结束;
    4、关闭事件句柄,删除安装程序的所有临时文件和文件夹(例如MYTEMP),完成安装。
    注意:在调用列举进程的函数时必须添加#include <Tlhelp32.h>指令。
    下面是示例代码(已调试通过):
    // MySfx.cpp : Defines the entry point for the application.
    //#include "stdafx.h"
    #include "resource.h"// Foward declarations of functions included in this code module:
    BOOL InitInstance(HINSTANCE, int);
    void RemoveThem(char *strPath);
    void IsExit();int APIENTRY WinMain(HINSTANCE hInstance,
                         HINSTANCE hPrevInstance,
                         LPSTR     lpCmdLine,
                         int       nCmdShow)

     // Perform application initialization:
     return InitInstance (hInstance, nCmdShow); 
    }//
    //   FUNCTION: InitInstance(HANDLE, int)
    //
    //   PURPOSE: Saves instance handle and creates main window
    //
    //   COMMENTS:
    //
    //        In this function, we save the instance handle in a global variable and
    //        create and display the main program window.
    //
    #if defined(_DEBUG)
    #define THISFILE_LENGTH 159785
    #else
    #define THISFILE_LENGTH 28672
    #endifchar sPath[256];
    HANDLE hEvent;BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
    {
     char sModule[256],sTemFile[256];
     //Gets temporary directory: 
     ::GetTempPath(255,sPath);
     strcat(sPath,"Mytemp"); 
     ::CreateDirectory(sPath,NULL);
     strcpy(sTemFile,sPath);
     strcat(sTemFile,"\\Sfx.exe");
     ::GetModuleFileName(NULL,sModule,255); //Opens the module file:
     HANDLE hFile=::CreateFile(sModule,GENERIC_READ,FILE_SHARE_READ,
        NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
     if(INVALID_HANDLE_VALUE==hFile)return 0;
     //Creates the temprory file:
     HANDLE hFileTemp=::CreateFile(sTemFile,GENERIC_WRITE|GENERIC_READ,
        0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
     if(INVALID_HANDLE_VALUE==hFileTemp)
     {
      ::CloseHandle(hFile); return 0;
     }
     ::SetFilePointer(hFile,THISFILE_LENGTH,NULL,FILE_BEGIN);
     //Now begin to read and write:
     while(TRUE)
     {
      BYTE buf[40*1024];
      DWORD dwNumberOfBytesRead;
      if(::ReadFile(hFile,buf,40*1024,&dwNumberOfBytesRead,NULL)==0)
       break;
      DWORD dwNumberOfBytesWritten;
      if(dwNumberOfBytesRead>0)
       if(!::WriteFile(hFileTemp,buf,dwNumberOfBytesRead,
        &dwNumberOfBytesWritten,NULL))break;
      if(dwNumberOfBytesRead<40*1024)break;
     }//while(TRUE)
     ::CloseHandle(hFile);
     ::CloseHandle(hFileTemp); //Prepare to extract files and setup the application:
     //Creates a auto-reset event object:
     hEvent=::CreateEvent(
       NULL, // SD
       FALSE,                       // reset type
       FALSE,                      // initial state
       NULL                          // object name
       ); //Executes self-extract file to extract files:
     STARTUPINFO info;
     PROCESS_INFORMATION newinfo;
     ::GetStartupInfo(&info);
     ::CreateProcess(sTemFile,NULL,NULL,NULL,FALSE,
       CREATE_DEFAULT_ERROR_MODE,NULL,sPath,&info,&newinfo);
      

  9.   

    //Wait for the self-extract process exit:
     ::ResetEvent(hEvent);
     while(::WaitForSingleObject(hEvent,500)==WAIT_TIMEOUT)
     {
      IsExit();
     } //Executes setup:
     strcpy(sTemFile,sPath);
     strcat(sTemFile,"\\Setup.exe");
     ::CreateProcess(sTemFile,NULL,NULL,NULL,FALSE,
       CREATE_DEFAULT_ERROR_MODE|CREATE_NO_WINDOW,
       NULL,sPath,&info,&newinfo);
     //Wait for setup process and other started by it exit:
     ::ResetEvent(hEvent);
     while(::WaitForSingleObject(hEvent,500)==WAIT_TIMEOUT)
     {
      IsExit();
     }
     ::CloseHandle(hEvent);
     
     //Remove tempary files and folders:
     RemoveThem(sPath);
     
       return FALSE;
    }void RemoveThem(char *strPath)
    {
     char strTemFile[256];
     strcpy(strTemFile,strPath);
     strcat(strTemFile,"\\*.*");
     WIN32_FIND_DATA FindFileData;
     HANDLE hFind=FindFirstFile(strTemFile,&FindFileData);
     if(hFind!=INVALID_HANDLE_VALUE)
     while(TRUE)
     {
      if(FindFileData.cFileName[0]=='.')
      {
       if(!FindNextFile(hFind,&FindFileData))break;
       continue;
      }
      strcpy(strTemFile,strPath);
      strcat(strTemFile,"\\");
      strcat(strTemFile,FindFileData.cFileName);
      if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        RemoveThem(strTemFile);//recursive call if it's a subdirectory.
      else ::DeleteFile(strTemFile);//Delete it if it's a file.
      if(!FindNextFile(hFind,&FindFileData))break;
     }
     ::CloseHandle(hFind);
     ::RemoveDirectory(strPath);
    }void IsExit()
    {
     //Enumerate current processes:
     //This process don't exit until the processes belonged to setup are all terminated:
     HANDLE hSnapshot;
     PROCESSENTRY32 pe;
     pe.dwSize=sizeof(pe);
     BOOL blExist=FALSE;
     size_t len=strlen(sPath); hSnapshot=::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
     if(hSnapshot<0) goto L1;
     
     if(::Process32First(hSnapshot,&pe)==FALSE)
     {
      ::CloseHandle(hSnapshot); goto L1;
     }
     if(_strnicmp(sPath,pe.szExeFile,len)==0)
         blExist=TRUE;
      
     while(blExist==FALSE && ::Process32Next(hSnapshot,&pe))
     {
      if(_strnicmp(sPath,pe.szExeFile,len)==0)
      {
       blExist=TRUE; break;
      } 
     }
     ::CloseHandle(hSnapshot);L1: if(blExist==FALSE) ::SetEvent(hEvent); 
    }本文的意图不是要开发一个工具软件(因为市面上已有此类工具),其主要目的是想与有兴趣的朋友一起切磋一下实现思路,说不定对某位朋友也许会有一点帮助(如需要完整代码可来信索取)。
     *****************************************************************附注:1、感谢朋友们的鼓励,因为要源码的朋友较多,现在请您直接到下面去下载源代码:http://www.csdn.net/cnshare2、使用Sleep()函数确实更加简便,谢谢高手指点。
      

  10.   

    创建安装程序的两种方法  ● 彭 进 赵 昕   创 建 安 装 程 序 是 程 序 员 经 常 遇 到 的 问 题 之 一。 本 文 仅 探 讨 在Windows 平 台 上 创 建 安 装(Setup) 程 序 的 两 种 方 法。
       一、 使 用Visual C++ 编 程 生 成Setup 程 序
       生 成Setup 程 序 最 直 接 的 方 法 当 然 是 通 过 编 程 来 实 现。 对 于Windows 平 台 来 说, 没 有 比Visual C++ 更 好 的 开 发 工 具 了( 原 因 很 简 单, 有 谁 能 比Microsoft 更 了 解Windows 平 台 呢 ?)。 下 面 的 例 程 就 是 使 用Visual C++ 5.0 编 译 完 成 的。
      Setup 程 序 主 要 处 理 两 个 方 面 的 问 题 :
      (1) 用 户 界 面。 评 价 一 个Setup 程 序 的 优 劣 时, 用 户 界 面 是 否 美 观 是 其 中 的 一 个 重 要 因 素。 此 外, 通 过 交 互 式 界 面 还 应 能 够 获 得 用 户 的 相 关 信 息( 比 如 目 标 目 录)。
      (2) 文 件 拷 贝 与 程 序 组 的 生 成。 也 就 是 按 照 用 户 输 入 的 信 息, 生 成 相 应 的 目 录 并 完 成 文 件 拷 贝 功 能( 这 要 涉 及 到 解 压 缩 问 题)。 一 般 来 说, 还 应 包 括 将 可 执 行 文 件 的 图 标 添 加 到 指 定 的 程 序 组 中。
      1、 为Setup 程 序 设 置 背 景
      Setup 程 序 的 用 户 界 面 以 对 话 框 为 主, 不 过 若 有 美 丽 的 背 景 则 能 为 你 的 程 序 增 色 不 少。 你 可 以 选 择 一 个 合 适 的BMP 文 件, 将 它 插 入 到 工 程 文 件(project) 中, 并 通 过 重 载 主 窗 口 类 的OnPaint() 函 数 显 示 出 来。 值 得 注 意 的 是, 背 景 图 片 不 应 过 于 眩 目, 否 则 会 有 喧 宾 夺 主 之 感。 例 如, 要 加 入 的BMP 文 件 的ID 号 是IDB_BIT。 下 面 给 出 应 加 在OnPaint() 中 的 函 数。  void Background(CDC *pDC)  {    CDC * pmem;
       CBitmap * pback;
       CBitmap * pold;
       BITMAP ff;
       pmem=new CDC;
       pbit=new CBitmap;
       pbit->LoadBitmap(IDB_BIT);
       pmem->CreateCompatibleDC(pDC);
       pold=(CBitmap *) pmem->SelectObject(pbit);
       pbit->GetObject(sizeof(ff),&ff);  pDC->BitBlt(0,0,bm.bmWidth,bm.bmHeight,pmem,0,0, MERGECOPY );   delete pmem->SelectObject(pold);   delete pmem;   return;  }  2、 显 示 全 屏 效 果   一 般 的 主 窗 口 都 有 边 界(border), 如 果 你 更 欣 赏DOS 界 面 中 的 全 屏 效 果, 则 最 好 在 重 载CWnd:: PreCreateWindow(CREATESTRUCT&cs) 时 保 持cs.style 的 缺 省 值, 并 且 在 创 建 主 窗 口 时 使 用CreateEx(WS_EX_TO PMOST,AfxRegisterWndClass(CS_VREDRAW), NULL,WS_VISIBLE|WS_POPUP,0,0,(GetSystemMetrics (SM_CXSCREEN)),(GetSystemMetrics(SM_CYSCREEN)), HWND_DESKTOP,0);。  3、 保 存 公 用 参 量   通 过 对 话 框, 可 与 使 用 者 交 换 信 息。 那 么, 如 何 将 程 序 运 行 时 必 需 的 参 量( 比 如 说 安 装 目 录) 保 存 起 来 呢 ? 当 然, 可 以 生 成 一 个 配 置 文 件, 不 过 更 为 专 业 的 作 法 是 将 相 关 信 息 存 入 到 系 统 的INI 文 件 中。 如 果 开 发 平 台 是Windows 95, 则 一 切 都 将 变 得 很 简 单, 因 为 你 面 对 的 就 是win.ini 文 件, 该 文 件 在Windows 目 录 下。Visual C++ 提 供 了 如 下 的 一 组 函 数 来 操 作 该 文 件。  (1)CWinApp::GetProfileString  CString GetProfileString( LPCTSTR lpszSection, LPCTSTR lpszEntry, LPCTSTR lpszDefault = NULL );   用 来 读 取lpszSection 区 域 内 的lpszEntry 参 数, 其 缺 省 值 为lpszDefault。  (2)CWinApp::WriteProfileString  BOOL WriteProfileString( LPCTSTR lpszSection, LPCTSTR lpszEntry, LPCTSTR lpszValue );   用 来 写 入lpszSection 区 域 内 的lpszEntry 参 数, 其 值 为lpszValue。   如 果 你 的 开 发 平 台 是Winnt, 则 要 麻 烦 一 点。 假 如 你 的 变 量 保 存 在e:\\winnt\sdi.ini 中, 则 在 使 用 以 上 函 数 之 前 必 须 调 用 :  free((void*)m_pszProfileName);  m_pszProfileName=_tcsdup(_T("e:\\winnt\\sdi.ini"));   如 果 要 存 取 的 变 量 并 不 多, 则 也 可 将 它 们 保 存 在 注 册 关 键 字(registry key) 中。 假 如 你 的 变 量 保 存 在Moon 注 册 关 键 字( 一 般 是 你 公 司 的 名 字) 下, 则 在 使 用 以 上 函 数 之 前 必 须 调 用 :  free((void*)m_pszRegistryKey);  m_pszRegistryKey=_tcsdup(_T("HKEY_CURRENT_ USER\\Software\\moon"));  4、 文 件 的 拷 贝 与 解 压 缩   如 果 你 的 安 装 程 序 是 针 对 软 盘 的, 则 存 储 空 间 就 成 了 必 须 考 虑 的 问 题。 原 封 不 动 地 把 应 用 程 序 拷 贝 上 去 未 免 太 傻 了, 较 为 合 适 的 方 法 是 将 应 用 程 序 压 缩 在 软 盘 中, 安 装 时 再 解 压 缩 到 相 应 的 目 标 目 录 中。Visual C++ 提 供 了 一 套 以LZ 开 头 的 函 数, 用 来 操 作 用Compress 命 令 压 缩 的 文 件 : 你 可 以 用LzOpenFile() 打 开 原 文 件 和 目 标 文 件, 然 后 用LzCopy() 解 压 缩 拷 贝。 该 组 函 数 调 用Windows 系 统 的lzexpand.dll 动 态 链 接 库, 所 以 编 译 链 接 时 一 定 要 注 意 加 入 头 文 件lzexpand.h, 并 插 入 接 口 库lz32.lib。   还 有 个 更 为 简 单 的 办 法, 即 先 用 像arj.exe 这 类 的 共 享 压 缩 程 序 压 缩 原 程 序, 然 后 调 用system("arj ...") 来 完 成 解 压 缩, 整 个 解 压 缩 过 程 在 后 台 进 行, 用 户 不 会 知 道 你 到 底 用 的 是 什 么 方 法。  5、 显 示 拷 贝 进 度   很 多 专 业 安 装 程 序 在 进 行 文 件 拷 贝 时 都 会 显 示 进 度 栏, 甚 至 还 带 有 动 画。 如 何 实 现 这 种 效 果 呢 ? 办 法 是 在 拷 贝 的 同 时 创 建 一 个modeless 对 话 框。 这 种 对 话 框 用create() 函 数 创 建, 类 似 于 创 建 了 一 个 与 主 线 程 独 立 的 新 线 程。 这 样 就 可 在 拷 贝 文 件 的 同 时 在 对 话 框 中 显 示 出 拷 贝 进 度。Visual C++ 提 供 了 两 个 很 有 用 的 控 件, 即CAnimateCtrl 和 CProgressCtrl。 可 在 对 话 框 中 画 出 这 两 个 控 件, 并 用ClassWizard 加 入 相 应 的 类。 其 中,CAnimateCtrl 是 用 来 显 示.avi 文 件 的, 使 用 它 即 可 在 每 个 文 件 拷 贝 成 功 后 启 动 一 个“ 小 纸 片 飞 过” 的 文 件 ;CProgressCtrl 用 来 显 示 拷 贝 进 度, 当 各 个 拷 贝 成 功 后 使 表 示 进 度 的 蓝 条 前 进 到 适 当 的 位 置。  6、 程 序 组 和 程 序 项 的 生 成   程 序 组 的 生 成 实 际 上 是 与 负 责 管 理 程 序 组 的Program Manager 进 行DDE 对 话, 你 可 以 通 过 该 对 话 来 添 加 相 应 的 程 序 组 和 应 用 程 序 的 图 标。 同 时 由 于 该 对 话 是 单 向 的, 所 以 负 责 处 理 反 馈 消 息 的callback() 函 数 不 进 行 任 何 处 理。 下 面 给 出 其 源 程 序 及 其 用 法。  HDDEDATA CALLBACK DDECallback( UINT uType, // transaction type   UINT uFmt, // clipboard data format   HCONV hconv, // handle to the conversation   HSZ hsz1, // handle to a string   HSZ hsz2,   // handle to a string   HDDEDATA hdata, // handle to a global memory object   DWORD dwData1,   // transaction-specific data   DWORD dwData2   // transaction-specific data  )  {return NULL;}  SendDdeCmd(LPSTR cmd)  {   LPDWORD dwDDEInst=0L;   UINT ui;   HSZ hszService,hszTopic,hszItem;   HCONV hConv;   HDDEDATA hexecData;   ui=DdeInitialize((unsigned long *)&dwDDEInst, DDECallback,CBF_FAIL_ALLSVRXACTIONS,0L);   if(ui!=DMLERR_NO_ERROR)   {return FALSE;}   hszService=DdeCreateStringHandle( (DWORD) dwDDEInst,"PROGMAN",CP_WINANSI);   hszTopic=DdeCreateStringHandle((DWORD)dwDDEInst, "PROGMAN",CP_WINANSI);   hszItem=DdeCreateStringHandle((DWORD)dwDDEInst,"GROUP", CP_WINANSI);   hConv=DdeConnect((DWORD)dwDDEInst,hszService, hszTopic,NULL);  DdeFreeStringHandle((unsigned long)dwDDEInst, hszService);  DdeFreeStringHandle((unsigned long)dwDDEInst, hszTopic);  DdeFreeStringHandle((unsigned long)dwDDEInst, hszItem);  if(!hConv)  return FALSE;  hexecData=DdeCreateDataHandle((DWORD)dwDDEInst,(unsigned char *)cmd,lstrlen(cmd)+1,0,NULL,0,0);  DdeClientTransaction((unsigned char *)hexecData, (DWORD)-1,hConv,NULL,0,XTYP_EXECUTE,1000,NULL);  DdeDisconnect(hConv);  DdeUninitialize((unsigned long)dwDDEInst);  return TRUE;  }   如 果 你 想 加 入 一 个 名 叫“demo” 的 程 序 组, 其 中 包 含 有 名 称 为“test” 的 程 序 项, 并 执 行c:\demo\test.exe, 使 用 的 是“test.ico” 图 标, 则 可 按 如 下 方 式 调 用 该 函 数 :  SendDdeCmd("[CreateGroup (demo)] [AddItem (c:\demo\test.exe, test,c:\demo\test.ico)]")
      

  11.   

    Professional - Standard Edition
    这个打包软件不错!很多软件都是用这个软件打包的!!
      

  12.   

    谢谢上面的大侠指点:
    我对打包软件分析,我觉得难道有几个1. 工程文件结果的构造,支持再次加载,修改
    2. 打包脚本内容构造,涉及到 写一个 解释器
    3. 对各个操作系统的一些底层操作,前面  free_card(痛并快乐着) 已经提到一个方面,还有注册表、目录等这些操作(当然这些相当较简单)再请各位大侠指点
      

  13.   

    ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
      

  14.   

    InstallShield.自己到网上搜吧!!
      

  15.   

    怎么用自己写程序的方式更新一个 系统 的dll 文件如 shell32.dll ?:(
      

  16.   

    直接覆盖就可以了,如果是系统不能直接覆盖,
    那加两段代码1.把你的DOS操作发入autoexec.bat中
    2.在windows的注册表或启动项中加入1个执行程序
    程序功能1.删除autoexec.bat中的改变,删除注册表或启动项的改变
    2.删除自己,删除自己有方法打包程序我想你可以这样做
    1.选定你要打包的文件(相对目录路径),主要是主目录路径,而放入windows下system下的一些文件别外说明和要加入的东西入DAO的支持等
    1.使用CZip类对主要目录路径进行压缩生成一个文件方入widnowsTemp目录中
    对别外文什同样打包,并产生一个目录文件用来描述对应目录位置,
    2.再生成exe文件包含这个文件(生成exe文件方法类文件组合方法)
    3.exe文件作用,产生安装界面,把在exe文件中zip包提出放入在TEMP目录中,并根据在输入出的路径使用CUnZip类解压到指定目录,
      

  17.   

    将多个文件捆绑成一个可执行文件
    http://www.vckbase.com/vckbase/vckbase12/src/BindFileSrc.zip
    将以上文件改编一下就可以了,还可以先用CZip打一些文件先压缩,再捆绑。
      

  18.   

    直接使用InstallShield等就可以直接做打包程序了
      

  19.   

    其实自己写也行,很多大公司都是自己写安装程序的。比如金山的播放FLASH。豪杰的安装时播放音乐。
    我最近做完了项目,自己写了一个,实现文件目选择和文件COPY,然后加载运行微软的数据库组件,写注册表和创建快捷方式,使用VC的属性页做成向导。不过我的功能还差一点我不知道如何实现删除本应用程序。最近太忙。先不写了。我的是很容易的。就是把所有要用到的组件和程序都放到一个目录夹中。原封不动的实现了文件COPY。跟手工操作一样。楼主可以自己写写看。需要调试与反复测试。应该实现一般的应用程序安装是不难做的
      

  20.   

    用VC自带的InstallShield,即可,
    在超星上下载《win2000核心编程精解》里面有详细的例子。
    不妨一看。
      

  21.   

    Professional - Standard Edition
    这个打包软件不错!
      

  22.   

    我也使自己编写打包程序,关键好处是灵活,我的步骤是:
    1. 编写setup.exe
    2. 编写*.ini文件记录setup.exe必要信息
    3. 找一些工具将要安装的文件作成*.cab(此工具特点:必须提供cab的解压缩类或控件),此类工具很多