最近做在Windows XP X64,VS2005环境下做32位程序编译为64位程序的工作,遇到了一些64位编程中可能遇到的问题:如内联汇编(解决方法改为C/C++代码),long类型的变化,最关键的遇到了64位进程需要调用32位dll的问题。由于有一些32位dll没有源代码,无法重新编译为64位dll,所以只能想办法解决64位进程调用32位dll问题,这个问题让我很是挠头了几天。

相关资料:
微软公司的官方网站针对这个问题描述如下:
在64位的windows系统中,一个64位进程不能加载一个32位dll,同理一个32位进程也不能加载一个64位dll。但是,64位windows支持64位和32位进程(包括本机或跨机)间进程间通信(RPC)。在64位windows中,一个进程外32位COM服务器能够与64位客户端进行通信,同样一个进程外64位COM服务器也能与32位客户端进行通信。因此,如果你有一个32位COM无法识别的DLL,你可以将它封装到一个进程外COM服务器中并在一个64位进程中用COM配置调用DLL。(最后一句我也看不太懂!!哈哈哈)

验证:
工作流程
1.创建一个进程外COM服务器(EXE)。
2.将32位dll的接口函数封装为COM服务器的相关接口。
3.注册COM服务器*.exe /regserver  (注销 *.exe /unregserver)。
4.64位进程调用32位COM服务器接口,成功。从而曲线实现了64位进程调用32位dll。

具体步骤:
我首先创建了一个简单的dll工程,只输出一个函数int c = add(int a,int b); 生成lib和dll
然后创建一个进程外COM(EXE类型),内部链接dll,添加方法Method: Add(long *c)
{ *c = add(1,2);}编译生成。
然后注册COM,*.exe /regserver
最创建一个64位WIN32工程验证64位环境下方法调用是否正确,经验证正确!!!

结论:以上方法可以解决64位进程调用32位dll的问题

32位进程调用64位dll应该也可以通过这种方法解决,原因64位windows系统下安装了32位和64位两套COM系统

程序64位化带来的问题和思考

1. 指针和long的转换
这是最基本的处理部分,由于32位系统下地址是32位,所以很多代码里都会存在这样的转换:
void* pData;
LONG lData;
lData = (LONG)pData;
现在地址是64位了,所以原来的这种转换就会导致地址高4Byte丢弃的问题.
这种转换向来被认为不安全,但还是大量出现,实际程序员代码的时候只要用一个指针保留就可以,完全没有必要用一个long保留,同样的问题也会出现在函数指针的保留.
思考:
所谓的存在即合理的思维在作怪吧,很多程序员认为这么做程序运行的好好的,所以不管是否有风险还照样这么写,实际上改成安全写法代码多不了多少.
当然不排除很多程序员对16位、32位、64位完全没有概念的.
还有一点可能是微软的windows消息给人以误导,消息的WPARAM和LPARAM中常会夹带数据指针,然后强制类型转换,但是看一下它们的定义,其中隔了一层,虽然只是语法上的小小手脚,但是绝对是有先见之明, 而很多人只会依样画葫芦而未明白期间的巧妙.

2. PE Import和Export Table的变化
程序中用到了PE hack,由于之前有人发现vista 32下原来所需要替换的一个import table中的函数找不到了,结果竟然去修改了export table, 姑且不谈修改export table的危险吧,结果vista 64的时候发现PE格式有了小小的变更.
MS将Import table中的地址转成了64bit,但是export table却还是32bit,估计其认为代码不可能大于4GB吧,这样就会发现更改export table变得很难,因为不同的dll会被load在不同的地址段,其间差距会大于4GB,所以只有将自己的dll强制定义在和目标dll相同的4GB范围内.
回头来说这种更改是极其危险的,因为对于export table OS会尽量保持全局唯一,所以一旦你的dll退出未能正确恢复原有的值,会造成其他所有使用到这个dll的程序crash.
最终我发现需要hack的那个函数放在了delayload import table中,简单的修改就解决了问题.
如果这个问题最终找不到而还是采用修改export table的方法,那么就会很惨,测试部门已经发现了经常性的系统crash.
思考:
这里出现了弯路,这个弯路项目的时间压迫难辞其咎,但是程序员未能自己去分析问题是更大的问题,vista在很多方面都保留了很好的兼容性,去分析下windows的目录就会发现基本和xp区别不大,所以很多基础dll的功能也没有变化(这一点让我感觉到vista并未如其发表的那样70%代码重写),在分析dll的时候只要稍微仔细点就会找到问题很简单.
另一个问题就是MS在定义PE格式的时候的确有很高的前瞻性,很好的保持了松耦合的能力,其实PE格式在2001年后就基本没有什么大的更改,那时大家还基本没有64位的概念,这一点是需要学习的,但是同样对于export table没有扩展到64bit我还是保留意见,难道真的认为4GB不会超过?当年盖茨大叔不是宣称640KB就够了吗^.^

3. DelayLoad的问题
DelayLoad这个特性在VC6开始出现,一般大家不会去接触,包括我自己,要不是这次机会也不会去看.
推荐一篇文章
http://www.microsoft.com/msj/0200/hood/hood0200.aspx
有详细的阐述,有兴趣可以自己看一下,其实其原理很简单,主要是在使用到某个dll的接口的时候再去load这个dll,这样可以节约空间,因为有些dll中的接口可能在程序周期中永远不会用到, 而且如果某个dll没有,如果没有用到,那么整个程序也能继续执行.
这真的是一个巧妙的设定, 虽然在第一次呼叫的时候会有小小的性能损失,并且代码也会稍微大一点,但是可能会带来更大的空间节省.
但是随即带来的思考, 为什么这么好的一个东西会那么生疏呢?个人考虑有以下原因:
1. 这种技术显然对于很多人来说过于底层了,有多少人在意编译出来的程序是怎么样存放,又是如何被加载运行的呢?
2. DelayLoad带来的好处对很多人不可见,只要我的程序编译通过,并且可以正确执行就可以了,节省了那么些空间或者扩展兼容有必要考虑吗?
3. 现在的硬件太好了,何必要去做这种优化呢?内存消耗多了,没什么嘛去买1GB内存插上不久解决了?
4. 编译系统不够智能,可能Visual Studio做的更加智能些,自动去分析然后产生delay load会更好吧?

64位进程调用32位dll的解决方法 / 程序64位化带来的问题和思考的更多相关文章

  1. 64位进程调用32位dll的解决方法

    64位进程调用32位dll的解决方法   最近做在Windows XP X64,VS2005环境下做32位程序编译为64位程序的工作,遇到了一些64位编程中可能遇到的问题:如内联汇编(解决方法改为C/ ...

  2. JAVA判断32位还是64位,调用不同的DLL(转)

    源:JAVA判断32位还是64位,调用不同的DLL 通过获取sun.arch.data.model可判断是32还是64的JAVA 将32或者64位的DLL放不同的目录,实现自适应调用DLL Prope ...

  3. CLR调试报错“Visual Studio远程调试监视器 (MSVSMON.EXE) 的 64 位版本无法调试 32 位进程或 32 位转储。请改用 32 位版本”的解决

    Win7 64位电脑上进行visual studio的数据库项目的CLR存储过程进行调试时,报错: ---------------------------Microsoft Visual Studio ...

  4. WAMP安装提示缺少 msvcr100.dll文件解决方法

    WAMP安装提示缺少wamp msvcr100.dll文件解决方法 因为wamp基于vs c++2010开发,需要提前安装这个组件才可以正常运行 微软官方组件下载地址: 32位:http://www. ...

  5. 在启动php时,无法启动此程序,由于计算机中丢失MSVCR110.dll的解决方法

    在启动php时,运行RunHiddenconsole.exe php-cgi.exe -b 127.0.0.1:9000 -c时,出现错误:无法启动此程序,由于计算机中丢失MSVCR110.dll 方 ...

  6. vmware 传输(vmdb)错误-32:pipe:read failed 解决方法

    摘自: http://www.myzhenai.com.cn/post/1088.html 传输(vmdb)错误-32:pipe:read failed 解决方法   原创内容,转载请注明出处:htt ...

  7. vc的环境变量配置和缺少mspdb60.dll的解决方法

    vc的编译器是cl.exe,我们如果在vc中编译就不用配置环境,但是如果要在任何位置用命令提示符打开编译器cl.exe来编译程序,那么就要配置环境了. 下面我就讲讲vc的环境变量配置和缺少mspdb6 ...

  8. 64位.net调用32位com服务(c++)

    说明: 因64位.net无法调用32位dll,才采用调用进程外com形式. 该项目必须为release时编译才不会报错. 项目调用时,添加引用->com中找到该com服务,添加即可. 部署: 启 ...

  9. Win10 64位系统ADO方式操作数据库失败解决方法

    VC操作Access数据库一般通过ODBC.ADO.DAO等方式,但在我的Win10 64位操作系统中,通过ADO方式操作数据库会失败,无法读取数据.解决方法:1.首先确保Win10操作系统ado目录 ...

随机推荐

  1. 042、用volume container 共享数据 (2019-03-05 周二)

    参考https://www.cnblogs.com/CloudMan6/p/7188479.html   volume container 是专门为其他容器提供 volume 的容器,他提供的卷也可以 ...

  2. python模块之序列化

    序列化 什么是序列化 序列化是将字典.列表等数据类型转化成一个字符串的过程 序列化的目的 1. 以某种存储形式使自定义对象持久化(存储) 2. 将对象从一个地方传递到另一个我地方(传输) 3. 使程序 ...

  3. jqweui Picker使用一个小问题

    地址:http://jqweui.com/extends#picker加了Display Value后,会产生改变值后,Picker显示Value而不显示Text情况.需要在OnClose里做如下处理 ...

  4. C# WinForm 多线程 应用程序退出的方法 结束子线程

    1.this.Close(); 只是关闭当前窗口,若不是主窗体的话,是无法退出程序的,另外若有托管线程(非主线程),也无法干净地退出: 2.Application.Exit(); 强制所有消息中止,退 ...

  5. 学习 vue 源码 -- 响应式原理

    概述 由于刚开始学习 vue 源码,而且水平有限,有理解或表述的不对的地方,还请不吝指教. vue 主要通过 Watcher.Dep 和 Observer 三个类来实现响应式视图.另外还有一个 sch ...

  6. python中yield的用法

    ---"在python中,当你定义一个函数,使用了yield关键字时,这个函数就是一个生成器" (也就是说,只要有yield这个词出现,你在用def定义函数的时候,系统默认这就不是 ...

  7. vue之简单的todoList(一)

    <!-- 根组件 --> <!-- vue的模板内,所有内容要被一个根节点包含起来 --> <template> <div id="app" ...

  8. git详细介绍

    Git管理我们的代码会经历三个不过程 1. 工作区:没有提交的代码就是存放的工作区 2. 暂存区:通过 git add 文件名 命令提交代码该文件就放在暂存区 3. 历史区:通过 git commit ...

  9. python中时间的转换和使用datetime

    模块 一个完整大型的python程序是由模块和包的形式组织起来的,可见模块在python中的重要性.模块是一种组织型式,它许多有关联(关系)的代码组织放到单独的独立文件中.简单的说,可以把模块理解为一 ...

  10. Leetcode#561. Array Partition I(数组拆分 I)

    题目描述 给定长度为 2n 的数组, 你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), ..., (an, bn) ,使得从1 到 n 的 min(ai, bi) 总和最 ...