Electronic Joint Business

Solution for E-Business

vc++

在 Visual C++ 中使用嵌入汇编

文章评价:
如果你的应用对性能要求十分严苛,使用汇编语言来进行编程无疑是最佳选择。不过,编程技术发展到今天,代码动辄百万行,直接用汇编语言来进行编程,成本实在太高了。作为一种折中方案,你可以选择在部分代码里使用汇编语言 — Inline Assembly 以达到改善性能的目的。
 
所谓嵌入汇编 — Inline Assembly 也叫做内联汇编,即直接在 C/C++ 代码里加入汇编语言代码。同传统的汇编方式相比,我们可以完全避免那些烦琐的汇编和链接步骤,可以直接在汇编代码中使用 C 变量名和函数名 。这样一来汇编代码就能够很容易地 C 语言程序紧密而自然地结合在一起了。另外,由于是把汇编语句和 C 或者 C++ 语句混合在一起进行编程,我们还将可以实现许多原来光凭 C 或者 C++ 语句无法办到的事。GCC, Visual C++ 等都提供了各自的嵌入汇编的实现。在 Visual C++ 中使用嵌入汇编不需要额外的编译器和联接器,十分的方便。

在 VC++ 中,嵌入汇编主要用于如下场合:

  • 使用汇编语言写函数;
  • 实现那些对速度要求非常高的代码;
  • 设备驱动程序中用于直接访问硬件;
  • “Naked” 函数的 prolog 和 epilog。

所谓 Naked 的函数,VC++ 不帮着做栈处理,需要用户自己取输入参数、保护寄存器、甚至自己写 ret 等等,因此称为 “纯粹”的函数(asm 函数)。有关 Naked 函数可以参考: Rules and Limitations for Naked Functions

嵌入汇编代码的缺点也很明显:代码不易于移植,如果你的程序打算在不同类型的机器(比如 x86 和 ARM)上运行,应当尽量避免使用嵌入汇编。特别要注意,微软在 64 位 VC++ 上已经不再支持嵌入汇编。这时候你应该使用 MASM,因为 MASM 支持更方便的的宏指令和数据指示符。

嵌入汇编关键字
在 Visual C++ 中加入嵌入汇编是只需要使用 __asm 关键字,这点和 C++ 标准不同,VC 不支持 C++ 中的 asm 关键字,所以你需要使用 __asm(两个下划线)关键字来编写你的嵌入汇编代码。这个关键字有两种使用方法:

>>> 阅读全文

 

, , , ,

Libffi的使用及其范例

文章评分:

LibFF I与调用约定

“FFI” 的全名是 Foreign Function Interface,通常指的是允许以一种语言编写的代码调用另一种语言的代码。而 “Libffi” 库只提供了最底层的、与架构相关的、完整的”FFI”,因此在它之上必须有一层来负责管理两种语言之间参数的格式转换。

高级语言编译器产生代码时都会依据一系列的规则,这些规则十分必要,特别是对独立编译来说。其中之一是“调用约定” (Calling Convention),它包含了编译器关于函数入口处的函数参数、函数返回值的一系列假设。它有时也被称作“ABI”(Application Binary Interface)。调用约定(Calling Conventions)定义了程序中调用函数的方式,它决定了在函数调用的时候数据(比如说参数)在堆栈中的组织方式。

通常来说函数调用要用到的两条基本的指令:”CALL”指令和”RET”指令。”CALL”指令将当前的指令指针(这个指针指向紧接在CALL指令后面的那条指令)压入堆栈,然后执行一条无条件转移指令转移到新的代码地址。”RET”是与”CALL”指令配合使用的指令,在绝大多数函数中它是最后一条指令。”RET”指令弹出返回地址(就是早些时候”CALL”指令压入堆栈的地址)并将其加载到”EIP”寄存器中,然后从这个地址开始继续执行。

>>> 阅读全文

 

, , , , ,

将嵌入汇编迁移到Windows 64位平台

文章评价:
在 X86 平台上的 Visual C++ 有一个很有用的特性就是,你可以在 C++ 代码中,直接编写汇编语句,而无需外部的汇编工具(如 MASM )。不幸的是,在 X64 和 IA64 平台上,这项特性为微软所抛弃。如果你需要将手上的 32位代码迁移到 X64 或者 IA64 上来,你不得不寻找一些替代的方法,这些方法包括:

  • 将既有的汇编代码修改并保存到独立的文件中,然后用 MASM64 来编译
  • 使用微软的 Compiler intrinsics,它提供了一些类似汇编的功能。
  • 用 C++ 和 Windows API 重写

将汇编代码提出到独立的文件中。
这里有个很简单的嵌入汇编的例子,我们在 C 函数中调用 FPU 指令来完成开方函数的功能。在 C 函数出口处,VC++ 编译器会自动添加复栈指令,所以不必自己编写。那反而会使系统混乱。

int quick_sqrt(__int64 params)
{
        int result=-1;
        __asm
        {
                fild params
                fsqrt
                fistp result
        }
        return result;
}

这个函数等同于下面这个 Naked 函数

__declspec(naked) int quick_sqrt(__int64 params)
{
        int result;
        __asm
        {
                push ebp
                mov ebp, esp
                sub esp, __LOCAL_SIZE

                fild params
                fsqrt
                fistp result
                mov eax, result

>>> 阅读全文

 

, , ,

托管C++ (一) C++/CLI的诞生

Managed C++

和通常的VC++不同,托管(Managed) 是.NET的一个专门概念,它是融于通用语言运行时(CLR)中的一种新的编程理念,通用语言运行时是.NET 框架应用程序的执行引挚。它提供了许多服务,其中包括:代码管理(装入和执行)、类型安全性验证、元数据(高级类型信息)访问、为管理对象管理内存、管理代码,COM对象和预生成的DLLs(非管理代码和数据)的交互操作性、对开发人员服务的支持等等。 也就是说,托管C++意味着,我们的代码可以被CLR所管理,并能开发出具有最新特性如垃圾自动收集、程序间相互访问等的.NET框架应用程序。

  可见,Managed C++(下称MC++)是有别于原来的C++(下称VC++)的一种新的而且十分特殊的.NET语言。

MC++ 的特殊之处在于 Micorsoft 为 MC++ 提供了一种称之为“it just works”的Interop机制,后称“Interop技术”。即对程序中的每一个本地方法,MC++ 编译器同时生成一个托管和非托管进入点,它们中只有一个是真正方法的实现,另一个则是转发器,可进行相应的转换和必要的调度。托管进入点通常是真正方法的实现,除非代码不能解释为MSIL或开发者使用“#pragma unmanaged”强制指定进入点的实现为本地机器码。当一个 IJW 转发器起作用时——例如转发到本地代码中,编译器提供转换的实现,并且通过偏移或 IAT(Import Address Table)调用实际的实现代码;因此用 MC++ 编写的程序同时拥有托管代码和非托管代码。

我们先来看一下这个例子。在这个例子中,我们混合使用了托管代码和非托管代码。在托管代码中,我们不能使用类似嵌入汇编等等许多特性,这就是为什么同时使用托管和非托管代码有时很重要。

>>> 阅读全文

 

, ,

用VC++ 10.0 编译Boost类库

为什么需要 Boost 类库?
原因是 C++ 提供的标准类库功能十分有限,功能大概只相当于 Java 里的 io 和 util 两个包。对于开发现代的应用程序,一个没有提供正则表达式,XML 等等类库的语言是不可想象的。Boost 就是鉴于 C++ 类库功能有限,而提供的一套免费的 C++ 函数库 ,吸收了广大高质量的 C++ 函数库,提供更强大的功能,以方便写出写出更高质量的 C++ 程序,加快程序的开发周期。

对于大部分 Boost 应用来说,它是不用编译的,直接包含头文件就可使用,如:

any
array
asio
conversion
crc
bind/mem_fn
enable_if
function
lambda
mpl
smart_pt

只有少部分需要编译成库文件,需要编译的库如下:

date_time
filesystem
function_types
graph
iostreams
math
mpi
program_options
python
regex
serialization
signals
system
test
thread
wave

要完全编译 Boost 库需要先准备一些第三方库,这里在 C: 盘建立了一个 USR 目录,在下面建立三个子目录 bin,include 和 lib,我们需要将 C:\usr\bin 加入到路径中。编译 Boost 需要三个类库,如下:

  • 1. Regex 所需 Unicode 支持类库 — ICU, 这里选用版本是4.4.6.
    编译 ICU 较为简单,解开 ICU 包之后,在其目录下可以找到一个叫”allinone”的目录,用 Visual studio 2010 打开里面的allinone.sln,分别用debug和release编译,编译之后,将 include 目录和 release 目录下的库文件拷贝出来,dll 拷贝到 C:\usr\bin, 头文件拷贝到 C:\USR\include,库文件则放到 C:\USR\lib.
  • 2. Graph 所需的 Xml 处理类库 — Expat, 最新的版本是2.0.1。expat 提供了vc 6.0的项目文件:expat.dsw,用 Visual studio 2010 打开它,并转化成 2010 的项目文件即可编译。如果想要64位的库文件,请注意在配置管理中,把 platform 改成 x64。
  • 3. 编译 Boost.MPI 所需 MPI 类库 — Boost 的官方站点说需要 MPICH 或 OpenMPI 之类的支持,但是在 Windows 编译环境中总是会提示需要 MPIC++,其实你需要的是Microsoft Cluster Pack SDK,下载并默认安装在 c:\Program Files\Microsoft Compute Cluster Pack 下。当然你也可以用 Microsoft HPC Pack 2008 SDK,效果是一样的。

不过,如果下载的是 Microsoft HPC Pack 2008 SDK,或者 Microsoft Compute ClusterPack 不是装到默认的路径下,那 bjam 就会提示找不到 mpic++ 的路径,这时候需要修改一下 mpi.jam(在tools\build\v2\tools),用 notepad 打开它,找到 “local cluster_pack_path_native” 这行,改成:

>>> 阅读全文

 

, , , , , , ,

利用MKMF编写Ruby扩展

文章评价:
MKMF 可以帮你自动成 Ruby 扩展库的 Makefile,这样生成的.so或.dll链接库,可以直接在 ruby 程序中被引用,鉴于 Ruby 的 Win32API 写得极其糟糕,强烈建议在需要与 Windows API 打交道的时候,直接放弃 Ruby 的 Win32API,而采用 MKMF 来扩展 ruby。

这里,我会着重谈 Windows 平台,因为 Windows 平台提供了大量的 SDK 和详细的文档,可以很快地为 Ruby 扩展出丰富的功能。

MKMF 通常以一个名为extconf.rb的ruby脚本开始,运行该脚本会自动生成编译所需的 Makefile. 简单的看一下extconf.rb的内容,十分简单明了

require ‘mkmf’
# Give it a name
extension_name = ‘bootextract’
# The destination
dir_config(extension_name)
dir_config(extension_name,‘C:/work/Include’,‘C:/work/lib’)
# Do the work
create_makefile(extension_name)

这里着重说一下

dir_config(extension_name,‘C:/work/Include’,‘C:/work/lib’)

第二个参数用来添加编译所需include路径,第三个则是 library 的路径。MKMF 编写的 makefile,不能接受路径中有空格,建议和例子一样,将所需的SDK的头文件和库文件拷贝到不带空格的路径中。

>>> 阅读全文

 

, ,

用Visual Studio 2008开发IE浏览器帮助对象 之三

接下来,我们要为IE增加一个按钮(注意不是toolbar,toolbar要复杂得多),基本这是一个注册表的魔术.打开RayBHO.rgs, 添加

HKLM
{
        NoRemove Software
        {
                NoRemove Microsoft
                {
                        NoRemove ‘Internet Explorer’
                        {
                                NoRemove Extensions
                                {
                                        ForceRemove ‘{1AC31710-6759-484f-A129-A70C55485DA1}’
                                        {
                                                val ButtonText = s ‘Hello,World’
                                                val Icon = s ‘%MODULE%,201’
                                                val HotIcon = s ‘%MODULE%,202’
                                                val CLSID = s ‘{1FBA04EE-3024-11d2-8F1F-0000F87ABD16}’
                                                val ClsidExtension = s ‘{057F3E68-6C2E-40A5-A641-E8CF9D6766F3}’
                                                val ‘Default Visible’ = s ‘yes’
                                        }
                                }
                        }
                }
        }
}

当然,你也可以把这一项放在HKCU(Current User)下,这样的话,该Button只对当前用户起作用。这些注册表项说明如下:

ForceRemove ‘{1AC31710-6759-484f-A129-A70C55485DA1}’ 该extersion的CLSID,请自己用GUID这个程序生成.
val ButtonText = s ‘Hello,World’ // 按钮上的文字说明
val Icon = s ‘%MODULE%,201’ // 按钮的图标,可以是icon的绝对路径,也可以和我的例子一样从资源文件里加载.
val HotIcon = s ‘%MODULE%,202’// 鼠标悬停时按钮的图标,与Icon类似.
val CLSID = s ‘{1FBA04EE-3024-11d2-8F1F-0000F87ABD16}’ //该CLSID意思为可执行,此值有特定含义,请小心修改.
val ClsidExtension = s ‘{057F3E68-6C2E-40A5-A641-E8CF9D6766F3}’ // 这个是RayBHO的CLSID,即表示该按钮的动作连接到RayBHO这个com上,具体值有所不同,必须查询你自己的rgs文件得到.
val ‘Default Visible’ = s ‘yes’//按钮可见.

当然你也可以不使用COM来响应按钮的动作,另外两个键Exec和Script,可以设置响应的程序或者脚本..这个不是重点.现在编译,然后从IE的自定义工具栏将这个按钮拖出来…如图所示:

点点看…..结果呢? 当然是不起作用!

>>> 阅读全文

 

, , ,

用Visual Studio 2008开发IE浏览器帮助对象 之二

上一篇文章开发的RayBHO只是BHO的一个框架,根本不具备任何功能. 在这篇文章里,我们将使继续扩展这个BHO,让它具备更强的功能.首先我们学习如何让BHO接收IE的事件通知,接者学习为ie添加一个按钮,并让BHO对按钮做出响应. 要让BHO能接收事件通知, 它必须让处理函数与浏览器事件建立连接点. 为响应这些事件,它必须实现IDispEventImpl, ATL提供了一个默认实现,可以帮助简化这个事件处理逻辑。 在RayBHO.h添加: #include "exdispid.h" #include "shlguid.h" 我们的CRayBHO必须派生自IDispEventImpl,修改后的代码如下: class ATL_NO_VTABLE CRayBHO :  public CComObjectRootEx,  public CComCoClass,  public IObjectWithSiteImpl,  public IDispatchImpl,  public IDispEventImpl&lt;1,CRayBHO,&DIID_DWebBrowserEvents2,&LIBID_SHDocVw,1,1>; DispEventImpl为处理事件提供了一种简单安全的方法。 IDispEventImpl与事件路由表配合工作,可以将事件路由到相应的处理程序函数。在例子中,我们将”DocumentComplete”的事件交由OnDocumentComplete函数进行处理. 在public段添加路由表: BEGIN_SINK_MAP(CHelloWorldBHO) SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete) END_SINK_MAP() 上述声明中SINK_ENTRY_EX(1,…)中的”1″与接口声明中的IDispEventImpl<1,….>是对应的,在必要时可以用于区分来自不同接口的事件.

 

, , ,

用Visual Studio 2008开发IE浏览器帮助对象 之一

这篇文章是应同学们的要求写的,以前都是用 VC++ 6.0 和 Platform SDK 完成的. 迁移到 VS2008之后,原来 Visual Studio 6.0里的 BHO 向导不复存在,因此特此不厌其烦,详细说明,本文也适用于 VS2005.

BHO 简介和开发工具
首先谈 BHO 的开发工具,我偏向使用 VC++(unmanaged C++) 作为开发工具,因为 Java JVM 或 .Net CLR 的虚拟机是个很笨重的东西,也是内存杀手, 并不具备写 plugin 的快捷轻巧的特点.个人并不喜欢将其作为 Plug-in 的开发平台,不过我会有另文说明用 C# 开发 BHO 的全过程, 作为那些偏重开发效率的同学的参考.
其次是类库的选择,我倾向利用“活动模板库”(ATL) 来开发使用 C++ 的 BHO。之所以使用 ATL,是因为它方便地实现了我们可以按需进行扩展的基本样板。尽管使用“Microsoft 基础类”(MFC) 或 Win32 API 和 COM)也可以创建BHO,但 ATL 是为我们提供了自动处理许多细节的轻型库,包括建立含有 BHO 类标识符 (CLSID) 的注册表。

ATL 的另一个优势在于它的 COM 智能感知指针类(例如,CComPtr 和 CComBSTR),这些类可管理 COM 对象的生命周期。例如,CComPtr 在赋值时会调用 AddRef,而在对象被销毁或超出范围时会调用 Release。智能指针简化了代码并且有助于避免内存泄漏。当在单个方法范围内使用时,它们的稳定性和可靠性尤为有用。

介绍完ATL, 我们也简单介绍一下 BHO. BHO 是将自定义功能添加到 Internet Explorer 的轻型 DLL 扩展,除了 IE, BHO 还可以将功能添加到 Windows 资源管理器外壳程序.

 BHO 通常并不提供其自身的任何用户界面 (UI)。它们而是通过在后台响应浏览器事件和用户输入数据来发挥作用。例如,BHO 可以拦截弹出窗口、自动填充窗体或为鼠标手势添加支持。

>>> 阅读全文

 

, , , , ,