⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 用自删除dll实现应用程序的安装.htm

📁 我在网上收集的一些有关vcDLL编程的文章。
💻 HTM
📖 第 1 页 / 共 3 页
字号:
  mso-bidi-font-family:宋体;mso-font-kerning:0pt'><o:p>&nbsp;</o:p></span></p>
  </td>
 </tr>
 <tr style='mso-yfti-irow:1;mso-yfti-lastrow:yes'>
  <td valign=top style='padding:0cm 0cm 0cm 0cm' id=fontzoom>
  <p class=MsoNormal align=left style='text-align:left;line-height:15.0pt;
  mso-pagination:widow-orphan'><span lang=EN-US style='font-size:10.0pt;
  font-family:宋体;mso-bidi-font-family:宋体;mso-font-kerning:0pt'><o:p>&nbsp;</o:p></span></p>
  <p class=MsoNormal align=left style='text-align:left;line-height:15.0pt;
  mso-pagination:widow-orphan'><span lang=EN-US style='font-size:10.0pt;
  font-family:宋体;mso-bidi-font-family:宋体;mso-font-kerning:0pt'>*</span><span
  style='font-size:10.0pt;font-family:宋体;mso-bidi-font-family:宋体;mso-font-kerning:
  0pt'>摘要<span lang=EN-US> <br>
  </span>   当我在编写<span lang=EN-US>“What To Do”</span>程序(这是作者编写的一个应用程序,小巧玲珑,很实用<span
  lang=EN-US>——</span>译者注)时,就想写一个自己的安装和卸载代码,主要目的是想随心所欲地控制整个安装<span lang=EN-US>/</span>卸载过程中用户所看到的画面。本文我们就来讨论如何利用自删除的动态链接库(<span
  lang=EN-US>DLL</span>)实现自删除的可执行程序,从而实现程序的安装<span lang=EN-US>/</span>卸载。相信很多朋友在编写<span
  lang=EN-US> Windows </span>程序时都想这么做,本文还将展示一些非常有用的相关技术,一定让你大开眼界<span
  lang=EN-US>...... <br>
  <br>
  *</span>实现自删除卸载程序的难点<span lang=EN-US> <br>
  </span>   编写卸载程序最具挑战性的部分是如何让卸载程序在删除完目标程序文件和相关目录之后自己删除自己。此外,卸载程序还必须能在所有<span
  lang=EN-US> Windows </span>操作系统平台(<span lang=EN-US>Windows 9x</span>、<span
  lang=EN-US>Windows NT</span>、<span lang=EN-US>Windows 2000</span>、<span
  lang=EN-US>Windows XP.....</span>)上运行,不需要用户下载任何附加组件。我在网上搜索了一番,找到一些相关的资料介绍如何自删除可执行程序文件,但是大多数所建议的解决方案都存在一个问题,那就是只能在某个版本的<span
  lang=EN-US> Windows </span>上工作。有些方法通过修改线程属性来实现,这样做一般都会导致定时问题。还有一些方法运行时出现严重错误,根本就不能用。我琢磨着寻求一种更好的解决方法来实现可执行程序的自删除功能:用自删除的<span
  lang=EN-US> DLL </span>实现自删除的可执行程序,从而突破上述诸方法的局限。<span lang=EN-US> <br>
  <br>
  *</span>实用程序<span lang=EN-US> rundll32.exe </span>介绍<span lang=EN-US> <br>
  </span>   从所周知,<span lang=EN-US>DLL</span>的代码通常需要先加载到内存之后才能执行,那么如何执行某个<span
  lang=EN-US>DLL</span>导出的代码而不用创建加载和调用该<span lang=EN-US> DLL </span>的<span
  lang=EN-US> EXE </span>文件呢?方法如下:从<span lang=EN-US> Windows 95 </span>开始的每个<span
  lang=EN-US> Windows </span>操作系统版本都附带一个系统实用程序:<span lang=EN-US>rundll32.exe</span>。利用它可以象下面这样执行某些<span
  lang=EN-US> DLL</span>(但不是所有)输出的任何函数:<span lang=EN-US> rundll32.exe DllName,ExportedfnName
  args <br>
  ExportedfnName </span>是<span lang=EN-US>DLL</span>输出的函数名。在编写供<span
  lang=EN-US> rundll32 </span>使用的<span lang=EN-US> DLL</span>时,可以象下面这样来声明输出函数:<span
  lang=EN-US>extern &quot;C&quot; __declspec(dllexport) void CALLBACK
  FunctionName ( <br>
  HWND hwnd, <br>
  HINSTANCE hInstance, <br>
  LPTSTR lpCmdLine, <br>
  int nCmdShow <br>
  ) <br>
  { ... } <br>
  rundll32.exe </span>根据函数参数列表对函数进行调用,但根据经验,实际上用得上的参数值只有一个,那就是<span lang=EN-US>
  lpCmdLine</span>,该参数接收运行<span lang=EN-US> rundll32.exe </span>时传入的参数值;<span
  lang=EN-US>__declspec(dllexport)</span>的目的是输出函数;<span lang=EN-US>extern
  &quot;C&quot; </span>使输出的函数名有修饰符,如:<span lang=EN-US>_FunctionName@16 </span>(函数名中被强制包含函数参数的大小,详细信息请参见<span
  lang=EN-US> MSDN </span>中有关<span lang=EN-US>DLL</span>输出函数调用规范说明)。<span
  lang=EN-US>rundll32.exe </span>加载指定的<span lang=EN-US> DLL </span>并调用通过<span
  lang=EN-US> args </span>参数传入的<span lang=EN-US> lpCmdLine </span>的值指定的输出函数。有关<span
  lang=EN-US> rundll32.exe </span>的正式文档参见<span lang=EN-US> MSDN </span>库相关资料(<span
  lang=EN-US>Q164787</span>):<span lang=EN-US>
  http://support.microsoft.com/default.aspx?scid=kb;en-us;164787 <br>
  <br>
  *</span>实现能自删除的<span lang=EN-US> DLL <br>
  </span>下面是实现自删除<span lang=EN-US>DLL</span>的示范代码:<span lang=EN-US> <br>
  <br>
  #include &lt;windows.h&gt; <br>
  HMODULE g_hmodDLL; <br>
  <br>
  extern &quot;C&quot; BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD reason,
  LPVOID) <br>
  { <br>
  if (reason == DLL_PROCESS_ATTACH) <br>
  g_hmodDLL = hinstDLL; <br>
  return TRUE; <br>
  } <br>
  <br>
  extern &quot;C&quot; __declspec(dllexport) void CALLBACK MagicDel(HWND, <br>
  HINSTANCE, <br>
  LPTSTR lpCmdLine, <br>
  int) <br>
  { <br>
  // </span>延时<span lang=EN-US>2</span>秒<span lang=EN-US> <br>
  Sleep(2000); <br>
  // </span>删除创建该进程的可执行文件<span lang=EN-US> <br>
  DeleteFile(lpCmdLine); <br>
  <br>
  // </span>删除<span lang=EN-US>DLL</span>自己<span lang=EN-US> <br>
  char filenameDLL[MAX_PATH]; <br>
  GetModuleFileName(g_hmodDLL, filenameDLL, sizeof(filenameDLL)); <br>
  <br>
  __asm <br>
  { <br>
  lea eax, filenameDLL <br>
  push 0 <br>
  push 0 <br>
  push eax <br>
  push ExitProcess <br>
  push g_hmodDLL <br>
  push DeleteFile <br>
  push FreeLibrary <br>
  ret <br>
  } <br>
  } <br>
  </span>   上面这段代码首先删除某个文件,然后自删除。<span lang=EN-US>DllMain </span>是<span
  lang=EN-US>DLL</span>的入口函数,当首次加载动态链接库时该函数被调用,此时将模块句柄赋值给全局变量<span lang=EN-US>
  g_hmodDLL</span>,以便梢后使用它来获取<span lang=EN-US> DLL </span>本身的文件名。在<span
  lang=EN-US> MagicDel </span>函数中,<span lang=EN-US>lpCmdLine </span>是<span
  lang=EN-US>DLL</span>要删除的可执行文件的名称(如:卸载程序的文件名)。要删除它很容易<span lang=EN-US>——</span>用<span
  lang=EN-US> Sleep </span>做一个延时,以便可执行程序的进程有时间退出并调用<span lang=EN-US> DeleteFile</span>。为了掌握<span
  lang=EN-US> MagicDel </span>的实现细节,你可以将可执行程序的进程句柄传给<span lang=EN-US>MagicDel</span>并在调用<span
  lang=EN-US> DeleteFile </span>之前做一个等待,看看会发生什么?<span lang=EN-US> <br>
  </span>   要让<span lang=EN-US> DLL </span>进行自删除需要一点诀窍。<span lang=EN-US>rundll32
  </span>调用<span lang=EN-US> LoadModule </span>将<span lang=EN-US> DLL </span>加载到它的地址空间。如果<span
  lang=EN-US> DLL </span>函数可以返回的话,<span lang=EN-US>rundll32 </span>将会退出,从而导致<span
  lang=EN-US> DLL </span>被释放(不是被删除)。为了解决这个问题,我们可以执行下面的代码:<span lang=EN-US>
  FreeLibrary(DLL module handle); <br>
  DeleteFile(DLL filename); <br>
  ExitProcess(0); <br>
  </span>  <span lang=EN-US> MagicDel </span>函数是不能按这样的顺序进行直接调用的,因为<span
  lang=EN-US> FreeLibary </span>会使代码页无效。为此,<span lang=EN-US> MagicDel </span>采用将等效的汇编指令压入堆栈,然后执行它们,后跟一个<span
  lang=EN-US> ret </span>指令,最后调用<span lang=EN-US> ExitProccess </span>以防止进程继续往下执行。我参考<span
  lang=EN-US> Gary Nebbit </span>在<span lang=EN-US> Windows </span>开发杂志(<span
  lang=EN-US>WDJ</span>)<span lang=EN-US>“Tech Tips”</span>栏目发表的文章编写了一个汇编代码块。如果你用<span
  lang=EN-US> Visual Studio </span>以默认选项生成<span lang=EN-US>DLL</span>,最终的二进制文件大约为<span
  lang=EN-US> 40K</span>。由于我们打算将<span lang=EN-US> DLL </span>作为可执行程序的资源,它的体积越小越好,为此,我们必须对它进行瘦身处理。思路是将无用的<span
  lang=EN-US> C </span>运行时代码从<span lang=EN-US>DLL</span>中删除掉,具体方法如下:<span
  lang=EN-US> <br>
  </span>本文例子使用<span lang=EN-US> Visual Studio.NET 2003 </span>中文版编译生成<span
  lang=EN-US> DLL</span>,先设置项目的编译<span lang=EN-US>/</span>链接选项:<span
  lang=EN-US> <br>
  </span>项目(<span lang=EN-US>P</span>)<span lang=EN-US>| [</span>项目名称<span
  lang=EN-US>] </span>属性(<span lang=EN-US>P</span>)<span lang=EN-US>... | </span>链接器<span
  lang=EN-US> | </span>输入<span lang=EN-US> | </span>忽略所有默认库:是(<span lang=EN-US>/NODEFAULTLIB</span>),此设置将<span
  lang=EN-US> /NODEFAULTLIB </span>选项传给链接器以便过滤掉运行时代码。<span lang=EN-US> <br>
  <br>
  </span>由于<span lang=EN-US> DLL </span>入口点(<span lang=EN-US>Entry Point</span>)通常是由运行时库提供(默认为<span
  lang=EN-US> DllMain</span>),所以完成上述第一步设置之后,还必须显式地将<span lang=EN-US> DLL</span>入口点设置为<span
  lang=EN-US> DllMain</span>:<span lang=EN-US> <br>
  </span>项目(<span lang=EN-US>P</span>)<span lang=EN-US>| [</span>项目名称<span
  lang=EN-US>] </span>属性(<span lang=EN-US>P</span>)<span lang=EN-US>... | </span>链接器<span
  lang=EN-US> | </span>高级<span lang=EN-US> | </span>入口点:<span lang=EN-US>DllMain</span>。<span
  lang=EN-US> <br>
  </span>如果此时编译生成<span lang=EN-US> DLL</span>,编译器会报如下两个 无法解析的外部符号<span
  lang=EN-US>( unresolved externals ) </span>错误:<span lang=EN-US> error
  LNK2019: </span>无法解析的外部符号<span lang=EN-US> ___security_cookie </span>,该符号在函数<span
  lang=EN-US> _MagicDel@16 </span>中被引用<span lang=EN-US> <br>
  error LNK2019: </span>无法解析的外部符号<span lang=EN-US> @__security_check_cookie@4 </span>,该符号在函数<span
  lang=EN-US> _MagicDel@16 </span>中被引用<span lang=EN-US> <br>
  </span>解决方法是进行下一步设置。<span lang=EN-US> <br>
  <br>
  <br>
  </span>项目(<span lang=EN-US>P</span>)<span lang=EN-US>| [</span>项目名称<span
  lang=EN-US>] </span>属性(<span lang=EN-US>P</span>)<span lang=EN-US>... | C/C |
  </span>代码生成<span lang=EN-US> | </span>缓冲区安全检查:否,<span lang=EN-US> <br>
  </span>该设置不会将<span lang=EN-US> /GS </span>标志传给编译器,从而摆脱<span lang=EN-US>
  unresolved externals </span>错误。<span lang=EN-US> <br>
  </span>好了,现在编译生成<span lang=EN-US> DLL</span>,最终的<span lang=EN-US> DLL </span>大小为<span
  lang=EN-US> 3K</span>,实际的文件大小只有<span lang=EN-US> 2.5K</span>。<span
  lang=EN-US> <br>
  <br>
  *</span>实现能自删除的可执行程序<span lang=EN-US> <br>
  </span>   这里所用的主要思路是将一个能自删除的<span lang=EN-US> DLL </span>作为资源保存在拟实现自删除的可执行程序中,然后在需要时重新创建它,同时,启动一个<span
  lang=EN-US> rundll32.exe </span>进程实现删除行为。<span lang=EN-US> <br>
  </span>   下面是用于将<span lang=EN-US>DLL</span>存储为资源的头文件和资源文件。资源类型值只要大于<span
  lang=EN-US> 256 </span>都可以,这是为用户定义类型预留的。此外还有一种可选方法是将<span lang=EN-US> DLL </span>二进制文件以字节数组的形式直接存储在源中:<span
  lang=EN-US> <br>
  </span>在资源中包含一个文件<span lang=EN-US> <br>
  <br>
  // SelfDelete.h <br>
  #define RC_BINARYTYPE 256 <br>
  #define ID_MAGICDEL_DLL 100 <br>
  <br>
  // SelfDelete.rc <br>
  #include &quot;SelfDelete.h&quot; <br>
  ID_MAGICDEL_DLL RC_BINARYTYPE MagicDel.dll <br>
  </span>下面是可执行程序关键代码:<span lang=EN-US>#include &lt;windows.h&gt; <br>
  #include &quot;SelfDelete.h&quot; <br>
  void WriteResourceToFile(HINSTANCE hInstance, <br>
  int idResource, <br>
  char const *filename) <br>
  { <br>
  // </span>存取二进制资源<span lang=EN-US> <br>
  HRSRC hResInfo = FindResource(hInstance, MAKEINTRESOURCE(idResource), <br>
  MAKEINTRESOURCE(RC_BINARYTYPE)); <br>
  HGLOBAL hgRes = LoadResource(hInstance, hResInfo); <br>
  void *pvRes = LockResource(hgRes); <br>
  DWORD cbRes = SizeofResource(hInstance, hResInfo); <br>
  <br>
  // </span>将二进制资源写到文件<span lang=EN-US> <br>
  HANDLE hFile = CreateFile(filename, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, <br>
  FILE_ATTRIBUTE_NORMAL, 0); <br>
  DWORD cbWritten; <br>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -