在家目录下建立.inputrc文件,内容是:
"\e[1~": beginning-of-line "\e[2~": overwrite-mode "\e[3~": delete-char "\e[4~": end-of-line重新登陆以后就可以使用DELETE HOME END INSERT这些按键了
如何在make install的时候将boot sector安装到软盘的引导区呢?这里要用一个dos的小技巧。首先,我们建立一个叫做installa.script文件内容为:
w 100 0 0 1
q
之后在makefile的install section里加入
@debug boot144.bin > installa.script
这样一来installa.script的内容会自动在debug进入的时候执行了。另外也不用使用额外的partcopy之类的工具了。
为什么您不能通过随机光盘重新安装您的系统 – PORTEGE存在一个极端弱智的问题
Portege系列笔记本电脑的恢复盘有一个极端弱智的问题(至少R150和R100都是)。恢复CD的引导部分(BOOTIMG.BIN)实际上是一个Windows 98系统盘。利用这个Windows 98系统启动后,系统盘的AUTOEXEC.BAT自动执行GHOST进行还原。由于GHOST镜像在光盘上因此TOSHIBA在引导部分中提供了OAKCDROM.SYS类的一个万能CDROM驱动程序。据我自己了解这个驱动程序只能驱动部分USB CDROM和大多数IDE CDROM,而不能驱动PCMCIA接口的CDROM。请注意,PORTEGE系列的笔记本电脑是不能通过USB CDROM引导系统的,笔记本电脑更不可能直接连接IDE设备。因此,只有通过PCMCIA接口的CDROM才能用恢复CD引导。现在问题出现了,恢复CD引导后使用的OAKCDROM.SYS类驱动程序,不能驱动PCMCIA光驱,也就不能读取光盘的GHOST文件。至此,恢复失败。
如何重新安装您的系统
首先确认您有以下设备:
- PCMCIA CD-ROM以及其DOS版本驱动程序
- USB 软盘驱动器
- TOSHIBA Portege系列笔记本电脑以及电脑的恢复光盘
安装步骤如下:
- 下载您所使用的PCMCIA CDROM的驱动程序。(例如我使用的是Argosy DR768, 就可以去http://www.argosy.com.tw下载)
- 使用WinImage打开BOOTIMG.BIN,并且将驱动程序*.SYS拷贝到BOOTIMG.BIN镜像中。修改CONFIG.SYS加入对应的驱动程序例如(DEVICE=A:\DOS\CDROM.SYS /D:MSCD0001)。这里请注意CDROM的标号必须为MSCD001,因为TOSHIBA在AUTOEXEC.BAT中引用了这个标号。
- 将这个BOOTIMG.BIN写入一张软盘中,用软盘引导,即可完成安装
使用.NET CF的API
Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase)+@"\"
使用P/Invoke Core API:
Q: How can I get current directory of my application on .net cf
A: Unfortunately I don’t know the .NET CF answer for this. I know in native code you can use GetModuleFileName to get the full path to the current .exe, and chop off the .exe name to get the path. In general Windows CE does not expose the concept of a “current directory,” but the .NET CF may.
DLL的基本结构
在Visual C++ .NET 2003中File|New|Projects, Project Types对话框中选择Visual C++ Project | Win32 | Win32 Project.在Application Wizard对话框中选择Application Settings选项页, Application Type选择DLL。
建立工程后开始书写你的函数。如果函数需要导出,请建立一个.h文件。添加下面的内容。
dll.h:
#ifndef _DLL_H #define _DLL_H #ifdef DLL #define EXTERN extern "C" __declspec(dllexport) #else #define EXTERN extern __declspec(dllimport) #endif EXTERN int add(int, int); #endif // _DLL_H
同时要修改你的.cpp文件
dll.cpp:
#define DLL
#include "FaceRec.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
EXTERN int add(int a, int b) {
// Something to do
return a + b;
}
这样就声明导出了一个add(int, int)函数。
这里需要说明几点,要特别注意:
- 由于是使用C++语言,C++支持函数重载。因此导出函数的函数明会因为重载有所变化。所以需要加上 extern “C” 强制使用C语言进行导出。当然这样一来,导出函数也就不能出现重载版本了。
- 另外,由于很多DLL的使用者是使用.lib文件+.h文件的方法(静态方法)访问DLL的,而非LoadLibrary + GetProcAddress的方法(动态方法)。因此我们书写.h文件的时候做一个宏判断如果是在DLL编译中,就定义EXTERN为 __declspec(dllexport)。如果是用户使用就定义EXTERN为extern __declspec(dllimport)。
- 对于C++导出给其他语言(Delphi, C++ Builder)的DLL.要根据其他语言函数的调用方式(Calling Convention)加入适当的修饰符。比如给delphi要用pascal修饰,给Windows API回调要使用stdcall等等。
DLL输出正确与否的检测
进入在Visual Studio .NET 2003的Visual .NET Tools里Visual .NET Command Prompt命令行模式。输入命令
dumpbin /exports dll.dll
来显示dll的导出表,可以看看导出的函数数量和名称是否正确。再对自己的程序进行调整
之前我创建的CompatibleBitmap是使用memDC来创建的(memDC = CreateCompatibleDC)
仔细看了MSDN关于CreateCompatibleBitmap的remark章节才清楚。原来memDC默认识单色的。新的bitmap必须使用hSrcDC来创建,才能出现彩色图。
The following is copied from MSDN
Note: When a memory device context is created, it initially has a 1-by-1 monochrome bitmap selected into it. If this memory device
context is used in CreateCompatibleBitmap, the bitmap that is created is a monochrome bitmap. To create a color bitmap, use the hDC that was used to create the memory device context, as shown in the following code:
HDC memDC = CreateCompatibleDC ( hDC );
HBITMAP memBM = CreateCompatibleBitmap ( hDC, nWidth, nHeight );
SelectObject ( memDC, memBM );
前几天给我的R150重装系统,差点气背过去。用自带光驱Argosy DR768 + TOSHIBA的原场恢复盘恢复。居然提示我找不到光驱!!先晕之后,狂汗!最后还是自己用软盘做了一个恢复引导盘,还配置半天GHOST引导,才把本本装好。回想起前段时间,一次把本本放到桌子上稍微重了一点点,就一点点哦。硬盘居然坏了!!还有,很久很久以前,这个R150的电池,居然在我小心的呵护下起了一个大包!有要爆炸的趋势!好帕帕!回想起和这个R150度过的时光,感觉TOSHIBA的本,烂就一个字啊。
有几个公式,不过我的眼睛是感觉不到什么区别咯。
- Y = 0.3R + 0.59B + 0.11G
- Y=0.212671*R + 0.715160*G + 0.072169*B
- Y=(max(R,G,B) + min(R,G,B)) / 2
这是一篇介绍C语言中的函数调用是如何用实现的文章。写给那些对C语言各种行为的底层实现感兴趣人的入门级文章。如果你是C语言或者汇编、底层技术的老鸟或是对这个问题不感兴趣,那么这篇文章只会耽误您的时间,您大可不必阅读他。当然如果前辈们愿意为我指出不足,我将十分感谢您的指导,并对耽误您宝贵的时间致歉。 好了,废话少说!要研究这个问题,让我们先打开VC++吧。最好是6.0的,:-P。(什么你没有VC++,倒!….赶快装一个!@#$,要快!) 首先,让我们在VC++里建立一个Win32 Console Application项目,并建立主文件fun.c。并输入以下内容。
int fun(int a, int b) {
a = 0x4455;
b = 0x6677;
return a + b;
}
int main() {
fun(0x8899,0x1100);
return 0;
}
之后,最关键的是在项目设置里关闭优化功能。也就是把Project->Setting->C/C++->Optimizations选为Disabled。编译器的优化在分析底层实现时大多数情况不太受欢迎。 按键盘上的F10键,进入单步调试模式(Step Over)。看到你的main函数左侧有个黄色的小箭头了吗?那个就是程序即将执行的语句。按Alt + 8。打开反编译窗口,看到汇编语句了吗?是不是想这个样子
==> 00401078 push 1100h
0040107D push 8899h
00401082 call @ILT+5(fun) (0040100a)
00401087 add esp,8
看到两个PUSH指令了吗?再看看后面的数字,不正是我们要传递的参数吗。奇怪阿?我们明明是先传递的0×8899怎么反倒先push 1100h呢?呵呵,这个现象就叫Calling conversion。究竟是何方神圣,我在后面会详细的给你解释的。先别着急。随后的Call指令的作用就是开始调用函数了。 接下来关掉反汇编窗口,在源代码窗口按F11进入函数体。当看到那个黄色的小箭头指向函数名的时候再调出反汇编窗口(Alt+8)。你会看到类似下面的代码:
1: int fun(int a, int b) {
00401000 push ebp
00401001 mov ebp,esp
00401003 sub esp,40h
00401006 push ebx
00401007 push esi
00401008 push edi
00401009 lea edi,[ebp-40h]
0040100C mov ecx,10h
00401011 mov eax,0CCCCCCCCh
00401016 rep stos dword ptr [edi]
2: a = 0x4455;
00401018 mov dword ptr [ebp+8],4455h
3: b = 0x6677;
0040101F mov dword ptr [ebp+0Ch],6677h
4: return a + b;
00401026 mov eax,dword ptr [ebp+8]
00401029 add eax,dword ptr [ebp+0Ch]
5: }
0040102C pop edi
0040102D pop esi
0040102E pop ebx
0040102F mov esp,ebp
00401031 pop ebp
00401032 ret
VC++就是好,还在难懂的汇编语句前加入了C语言的源代码。不过同时也有不少我们不需要的代码。因此,你只需要关心红色的部分就可以了。 奇怪阿?不是参数都用push传递了吗?怎么没看到被pop出来?问题其实是这样,当你调用Call进入函数的时候Call背着你做了一件事。call把它下一条语句的地址push进了堆栈。(旁人: 什么!这是为什么?)原因很简单,因为函数调用完了,要用ret返回。而ret怎么知道返回哪里呢?对了, ret指令pop了call指令push给他的地址(搞清楚这个关系哦),然后返回到了这个地址。call和ret配合的如此绝妙,一个PUSH一个POP肯定不会让堆栈不平衡的(老外叫no stack unwinding)。现在明白了,如果你来个pop eax,那eax里面是什么?当然是ret要用的返回地址了。好啦,你要是pop eax就等于抢了ret要用的东西了。不论曾程序流程和道德标准上你做的都不对 :-P。 可是怎么在函数体里使用参数呢?问题其实并不难,既然参数在堆栈里我们就可以使用esp(堆栈指针)来访问了。不过,我相信你也想到了。esp是个经常变化的值。一旦,函数里出现pop或push他就会变化。这样很不容易定位参数的于内存中的位置。因此,我们需要一个不会变化的东西作为访问参数的基准。看看函数体的开头部分:
00401000 push ebp
00401001 mov ebp,esp
先用push ebp保存了原来ebp的值再把esp的值给ebp。原来ebp就是用来做基准的。也难怪他被称为ebp(Base Pointer)。很自然ret返回前的pop ebp就是恢复原来ebp的数值喽。当然一定要恢复,因为函数里也可以调用函数嘛。每个函数都用ebp,自然要保证使用完后完璧归赵了。现在当函数执行到 mov ebp, esp后堆栈应该变成这个样子了。
/-------------------\ Higher Address
| 参数2: 0x1100h |
+-----------------+
| 参数1: 0x8899h |
+-----------------+
| 函数返回地址 |
| 0x00401087 |
+-----------------+
| ebp |
\-------------------/ Lower Address <== stack pointer
& ebp all point to here, now
由于我们在VC++上使用的int类型是一个32位类型,ebp和函数返回值也是32位的。因此每个量要占去4个字节。另外还需要注意堆栈的扩展方向是高地址到低地址。有了这些指示。我们就可以分析出,第一个参数的地址是ebp + 08h,第二个参数就是ebp + 0ch。看看反汇编的代码:
2: a = 0x4455;
00401018 mov dword ptr [ebp+8],4455h
3: b = 0x6677;
0040101F mov dword ptr [ebp+0Ch],6677h
与我们的计算吻合。之后呢:
00401031 pop ebp
00401032 ret
将ebp原来的数值完璧归赵,调用ret指令,ret指令pop出返回地址,之后返回到调用函数的call指令的下一条语句。ret之后,堆栈应该变成这个样子了
/-------------------\ Higher Address
| 参数2: 0x1100h |
+-----------------+
| 参数1: 0x8899h |
\-------------------/ Lower Address <== stack pointer
哈哈,问题出现了,再函数返回后堆栈出现了不平衡的情况(Stack Unwinding)。怎么办呢?好办啊,直接 pop cx pop cx 把堆栈平衡过来就好了。幸好我们只有两个参数,要是有20个的话,那就要有20个pop cx。不说影响美观,程序效率也会很低。所以VC++使用了这个办法解决问题:
00401082 call @ILT+5(fun) (0040100a)
00401087 add esp,8
看红色的语句,直接将esp的值加8,让堆栈变成
/-------------------\ Higher Address <== stack pointer
| 参数2: 0x1100h |
+-----------------+
| 参数1: 0x8899h |
\-------------------/ Lower Address
通过改变esp从根本上解决了Stack unwinding。(push,pop指令本质上不就是通过改变esp来实现堆栈平衡的吗) 现在,明白了函数如何传递参数,如何调用,如何返回。下一个问题就是看看函数如何传递返回值了。相信你早就注意到了
4: return a + b;
00401026 mov eax,dword ptr [ebp+8]
00401029 add eax,dword ptr [ebp+0Ch]
可见,函数正式用eax寄存器来保存返回值的。如果你想使用函数的返回值,那么一定要在函数一返回就把eax寄存器的值读出来。至于为什么不用ebx,ecx…,这个虽然没有规定,但是习惯上大家都是用eax的。而且windows程序中也明确指出了,函数的返回值必须放入eax内。 OK,现在来解决什么是calling conversion这个历史遗留问题。如果认真思考过,你一定想函数的参数为什么偏用堆栈转递呢,寄存器不也可以传递吗?而且很快阿。参数的传递顺序不一定要是由后到前的,从前到后传递也不会出现任何问题啊?再有为什么一定要等到函数返回了再处理堆栈平衡的问题呢,能否在函数返回前就让堆栈平衡呢? 所有上述提议都是绝对可行的,而他们之间不同的组合就造就了函数不同的调用方法。也就是你常看到或听到的stdcall,pascal,fastcall,WINAPI,cdecl等等。这些不同的处理函数调用方式就叫做calling convention。 默认情况下C语言使用的是cdecl方式,也就是上面提到的。参数由右到左进栈,调用函数者处理堆栈平衡。如果你在我们刚才的程序中fun函数前加入__stdcall,再来用上面的方法分析一下。
8: fun(0x8899,0x1100); 00401058 push 1100h ; <== 参数仍然是由右到左传递的 0040105D push 8899h 00401062 call fun (00401000) ;<== 这里没有了 add esp, 08h1: int __stdcall fun(int a, int b) { 00401000 push ebp 00401001 mov ebp,esp 00401003 sub esp,40h 00401006 push ebx 00401007 push esi 00401008 push edi 00401009 lea edi,[ebp-40h] 0040100C mov ecx,10h 00401011 mov eax,0CCCCCCCCh 00401016 rep stos dword ptr [edi] 2: a = 0x4455; 00401018 mov dword ptr [ebp+8],4455h 3: b = 0x6677; 0040101F mov dword ptr [ebp+0Ch],6677h 4: return a + b; 00401026 mov eax,dword ptr [ebp+8] 00401029 add eax,dword ptr [ebp+0Ch] 5: } 0040102C pop edi 0040102D pop esi 0040102E pop ebx 0040102F mov esp,ebp 00401031 pop ebp 00401032 ret 8; <== ret 取出返回地址后, ; 给esp加上 8。看!堆栈平衡在函数内完成了。 ; ret指令这个语法设计就是专门用来实现函数 ; 内完成堆栈平衡的
于是得出结论,stdcall是由右到左传递参数,被调用函数恢复堆栈的calling convention. 其他几种calling convention的修饰关键词分别是__pascal,__fastcall, WINAPI。现在,你可以用上面说的方法自己分析一下他们各自的特点了。
.NET Compact Framework中,把原有的Form.Handle属性去掉了。表面上看似乎无法获得一个窗口的句柄了。实际上我们可以变通一下来解决这个问题。首先,通过Form.Capture让窗口获得焦点,接下来使用GetCapture API来取得焦点窗口的句柄。类似的方法还可以获得其他一些有焦点控件的句柄
[DllImport("coredll.dll")]
private static extern IntPtr SetCapture(IntPtr hWnd);
[DllImport("coredll.dll")]
private static extern IntPtr GetCapture();
public static IntPtr GetHandle()
{
IntPtr hOldWnd = GetCapture();
this.Capture = true;
IntPtr ret = GetCapture();
this.Capture = false;
SetCapture(hOldWnd);
return ret;
}
最新评论
1 周 6 天之前
6 周 4 天之前
10 周 4 天之前