2011年1月26日星期三

趣题:两步猜出多项式的各项系数

来源: Matrix67


有一个黑匣子,黑匣子里有一个关于 x 的多项式 p(x) 。我们不知道它有多少项,但已知所有的系数都是正整数。每一次,你可以给黑匣子输入一个整数,黑匣子将返回把这个整数代入多项式后的值。有一个不可思议的结论:你可以在两步之内还原出整个多项式!这是如何做到的呢?

首先,输入 1 ,于是便得到整个多项式的所有系数之和。不妨把这个系数和记作 S 。下一步,输入 S + 1 ,于是黑匣子返回

    an * (S + 1)n + an-1 * (S + 1)n-1 + … + a1 * (S + 1) + a0

把这个值转换成 S + 1 进制,依次读出每一位上的数,它们就是多项式的各项系数了。

来源:http://rjlipton.wordpress.com/2010/12/20/some-mathematical-gifts/
这个有趣的问题曾经以另一形式出现在了这个 Blog 里,见 这里 的 35 题。

2011年1月10日星期一

VC++ 2005 Express + SDK

You can use Visual C++ Express to build powerful .NET Framework applications immediately after installation. In order to use Visual C++ Express to build Win32 applications, you'll need to take just a few more steps. I'll list the steps necessary for building Win32 applications using Visual C++ Express.

Step 1: Install Visual C++ Express.


If you haven't done so already, install Visual C++ Express.


Step 2: Install the Microsoft Platform SDK.

Install the Platform SDK over the Web from the Download Center. Follow the instructions and install the SDK for the x86 platform.


Step 3: Update the Visual C++ directories in the Projects and Solutions section in the Options dialog box.

From the Tools menu in Visual Studio, select Options. The Options dialog box appears.

From the Options dialog box, expand the Projects and Solutions node and select VC++ Directories. In that section, add the following paths to the appropriate subsection:

    * Executable files: C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Bin
    * Include files: C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include
    * Library files: C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Lib

Note: Alternatively, you can update the Visual C++ Directories by modifying the VCProjectEngine.dll.express.config file located in the \vc\vcpackages subdirectory of the Visual C++ Express install location. Please make sure that you also delete the file "vccomponents.dat" located in the "%USERPROFILE%\Local Settings\Application Data\Microsoft\VCExpress\8.0" if it exists before restarting Visual C++ Express Edition.


Step 4: Update the corewin_express.vsprops file.

One more step is needed to make the Win32 template work in Visual C++ Express. You need to edit the corewin_express.vsprops file (found in C:\Program Files\Microsoft Visual Studio 8\VC\VCProjectDefaults) and

Change the string that reads:

AdditionalDependencies="kernel32.lib"

to

AdditionalDependencies="kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib"


Step 5: Generate and build a Win32 application to test your paths.

In Visual C++ Express, the Win32 Windows Application type is disabled in the Win32 Application Wizard. To enable that type, you need to edit the file AppSettings.htm file located in the folder %ProgramFiles%\Microsoft Visual Studio 8\VC\VCWizards\AppWiz\Generic\Application\html\1033\".

In a text editor comment out lines 441 - 444 by putting a // in front of them as shown here:

// WIN_APP.disabled = true;
// WIN_APP_LABEL.disabled = true;
// DLL_APP.disabled = true;
// DLL_APP_LABEL.disabled = true;

Save and close the file and open Visual C++ Express.

From the File menu, click New Project. In the New Project dialog box, expand the Visual C++ node in the Product Types tree and then click Win32. Click on the Win32 Console Application template and then give your project a name and click OK. In the Win32 Application Wizard dialog box, make sure that Windows application is selected as the Application type and the ATL is not selected. Click the Finish button to generate the project.

As a final step, test your project by clicking the Start button in the IDE or by pressing F5. Your Win32 application should build and run.

参考译文:

安装之后,您可以立即使用Visual C++ 2005速成版来生成功能强大的.NET Framework 应用程序。若要使用 Visual C++ 速成版生成Win32应用程序,只需采取几个步骤,下面对此进行了详细介绍。 
   
1. 安装 Platform SDK 以便与 Visual C++ 速成版结合使用从 Platform SDK 更新站点 的 Platform SDK Update 站点通过 Web 安装 Microsoft Platform SDK。在该页上,单击“Download”(下载),然后确保从列表中依次选择“Windows SDK”和“Core SDK”。 

2. 从 Visual Studio 中的“工具”菜单上,选择“选项”。出现“选项”对话框。 
从“选项”对话框中,展开“项目和解决方案”节点并选择“VC++ 目录”。在该部分,将以下路径添加到相应的子节: 
可执行文件:C:\Program Files\Microsoft SDK\Bin 
包含文件:C:\Program Files\Microsoft SDK\include 
库文件:C:\Program Files\Microsoft SDK\lib 
    注意 在您的系统上,Platform SDK 的位置可能有所不同。 

3. 更新 corewin_express.vsprops 文件 
(位于C:\Program Files\Microsoft Visual Studio 8\VC\VCProjectDefaults 中) 

并将以下字符串: 

AdditionalDependencies="kernel32.lib" 

更改为: 

              AdditionalDependencies="kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib" 

注意 在您的系统上,Visual C++ 速成版 可能安装到了不同的位置。 

4. 在Visual C++ 速成版,Win32应用程序向导上的Windows 应用程序选项是不可选的,要使用这个选项,你需要修改AppSettings.htm这个文件,它位于"%ProgramFiles%\ Microsoft Visual Studio 8\VC\VCWizards\AppWiz\Generic\Application\html\1033\". 

在文本编辑器用"// "符号把文件里的441 - 444这几行注释掉,像下面这样: 

// WIN_APP.disabled = true; 

// WIN_APP_LABEL.disabled = true; 

// DLL_APP.disabled = true; 

// DLL_APP_LABEL.disabled = true; 

保存和关闭文件,然后打开Visual C++ 速成版.

2011年1月8日星期六

msvcprt.lib(MSVCP90.dll) : error LNK2005:已经在libcpmtd.lib(xmutex.obj) 中定义

来源:未知

这篇文章要来谈谈程式库 (Library) 连结,以及关于 MSVC 与 CRT 之间的种种恩怨情仇。

如果你使用的作业系统是 Linux、Mac 或其他非 Windows 平台,你可以忽略这篇文章;如果你使用的作业系统是 Windows 平台,但没有用 Microsoft Visual Studio C++(以下简称为 MSVC)软体撰写 C++ 程式的话,这篇文章对你的帮助可能很有限;但如果你的作业系统是 Windows,而且你使用的程式整合开发环境是 MSVC 软体撰写 C++ 程式的话,这篇文章应该能够帮助你釐清一些重要的基础观念。

身为程式设计者,在学习程式设计的过程中,你是否曾经遇过某些看起来不知所云的错误讯息,却不知该如何解决?例如当你快快乐乐地写完程式,并且确认所有的程式码都能成功通过编译之后,接着执行「建置方案」(Build Solution) 的步骤,结果却跑出一堆莫名其妙的错误:

LIBCMTD.lib(mlock.obj) : error LNK2005: __lock 已在 MSVCRTD.lib(MSVCR80D.dll) 中定义过了

LIBCMTD.lib(mlock.obj) : error LNK2005: __unlock 已在 MSVCRTD.lib(MSVCR80D.dll) 中定义过了

LIBCMTD.lib(crt0.obj) : error LNK2005: _mainCRTStartup 已在 MSVCRTD.lib(crtexe.obj) 中定义过了

…………

LINK : warning LNK4098: 预设的程式库 ‘MSVCRTD’ 与其他使用的程式库冲突,请使用 /NODEFAULTLIB:library

LINK : warning LNK4098: 预设的程式库 ‘LIBCMTD’ 与其他使用的程式库冲突,请使用 /NODEFAULTLIB:library

D:\Workspace\CrtLibTest\Debug\CrtLibTest.exe : fatal error LNK1169: 找到有一或多个已定义的符号

以一般的情况来说,如果在你的程式专案中有使用某些由他人所撰写的第三方程式库或是开源专案的程式库,比较容易会发生上述的错误状况。从上述这些看似离奇而令人摸不着头绪的错误讯息中,我们大概可以猜测问题点应该在于 LIBCMTD.lib 与 MSVCRTD.lib 这两个程式库身上。但到底什么是 LIBCMTD.lib 和 MSVCRTD.lib?在我们的程式码中有使用这些程式库吗?

 

答案是肯定的。

熟悉 C 语言的程式设计者都知道,如果要使用 printf()、scanf() 或者 fopen() 等等 C 语言的基本 I/O 操作函式时,首先必须用 #include 语法将 stdio.h 这个标头档纳入我们的程式码中。藉由 stdio.h 中对这些 I/O 操作函式所做出的函式宣告 (function declaration),编译器 (Compiler) 才得以确认 printf、scanf 以及 fopen 等等都是合法可用的函式。

而当我们撰写的程式码经过编译器产出 OBJ 形式的档案之后,需要再经由连结器 (Linker) 的处理程序,将程式码中全部有使用到的函式定义 (function definition) 连结建置起来,才能够产生出最后的程式执行档。问题来了,我们知道 printf、scanf 以及 fopen 的函式宣告存在于 stdio.h 当中,但是这些傢伙的函式定义,也就是真正的实做程式码,究竟存放在什么地方呢?

在 C 语言的标准程式库中。

由 C 语言所制订的标准程式库,称之为「执行阶段程式库」,也就是 C Run-Time Library,通常可简称为 CRT。在 C 语言的标准程式库中,包含了一组常用的基础函式,例如 I/O 处理与字串操作程序等等,所以只要我们使用 C 语言撰写程式码,就一定要将编译完成后的程式码 OBJ 档,连结至 C 语言的执行阶段程式库,才能够产生出合法的 C 语言程式执行档。

而 CRT 并非只有单一一种版本存在。事实上,除了可以依「除错」与「释出」用途分成两个版本之外,两者又可分别衍生分出「静态连结」与「动态连结」两种形式:

静态连结:

LIBCMTD.lib(除错版本)

LIBCMT.lib

动态连结:

MSVCRTD.lib(除错版本)

MSVCRT.lib

虽然这四个 CRT 版本的用途与使用方式各不相同,但却有个共通的特点,就是它们都是满足执行绪安全需求,可在多执行绪程式码中安全使用的程式库版本。事实上,在过去 MSVC 6 的版本中,本来还有另外两个 LIBCD.lib(除错版本)与 LIBC.lib 程式库,是专门给单执行绪程式使用的 CRT 版本,但是这两个选项自 MSVC 2005 开始就从设定选项中被删除掉了,所以现在大多数程式设计者使用的都是多执行绪的 CRT 版本。

在程式库连结 (library linking) 的行为中,静态连结和动态连结的分别,在于使用静态连结时,会直接将程式库的函式定义嵌入执行档之中,而使用动态连结时,程式库的函式定义则存在于另外的独立档案,通常是 DLL 格式的档案中,然后与程式执行档一同发佈给使用者。因此在档案的尺寸上,使用动态连结的执行档档案,通常会比使用静态连结的执行档档案来得更小一些。

使用动态连结 CRT 版本的好处,是能够将经常使用到的标准程式库们独立出来,放在 Windows 的系统资料夹中,以减少我们建置出来的执行档档案尺寸。但反过来说,使用动态连结 CRT 版本的缺点也在于这些与执行档相依为命的 DLL 档案上。举例来说,如果程式以 MSVC 2005 建置出 Debug 组态的执行档,则此执行档需要有 msvcr80d.dll 存在才能顺利执行;如果是 Release 组态,则相依于 msvcr80.dll。但是如果你把相同的程式码拿到 MSVC 2008 上建置,产生出来的执行档则相依于 msvcr90d.dll 与 msvcr90.dll 两个不同的 DLL 档案。不同版本的 MSVC,都会有各自不同的相依 DLL 档案。

在 MSVC 的程式专案中,如何指定程式码要使用静态连结或者动态连结的 CRT 版本?其实很容易,只要在专案属性的「C/C++」页面中,选择「程式码产生」(Code Generation) 子页面,其中有个「执行阶段程式库」(Runtime Library) 的项目,也就是专案中用来设定 CRT 连结版本的地方。其中总共有四个选项,正好对应于上述静态连结与动态连结的四个不同程式库版本。

多执行绪侦错 (/MTd):对应 LIBCMTD.lib

多执行绪 (/MT):对应 LIBCMT.lib

多执行绪侦错 DLL (/MDd):对应 MSVCRTD.lib

多执行绪 DLL (/MD):对应 MSVCRT.lib

如果你没有做任何设定就开始建置程式的话,MSVC 的预设选项则会使用动态连结的版本。


C Runtime Library

请注意,以上只是单纯 C 语言的程式库而没有包含 C++ 语言在内。如果你的程式系统中,有包含 C++ 语言的程式码的话,那又是另外一回事了。但是在专案属性的页面中,为什么找不到相关的设定选项呢?因为 MSVC 悄悄地帮程式设计者代劳处理掉了。只要在程式码中使用 #include 语法纳入任何一个 C++ 的标头档,例如 iostream 或 fstream,MSVC 就会在连结器的运作阶段中,自动帮我们连结 C++ 的执行阶段程式库。而 C++ 的执行阶段程式库,同样可分为四个版本:

静态连结:

LIBCPMTD.lib(除错版本)

LIBCPMT.lib

动态连结:

MSVCPRTD.lib(除错版本):执行档相依于 MSVCP90D.dll

MSVCPRT.lib:执行档相依于 MSVCP90.dll

至于程式执行档使用的是静态连结或者动态连结的版本,就仰赖于 C 语言的版本设定选项了。举个例子来说,如果你撰写了一个 Debug 组态的 C++ 程式,并且保留专案原先预设的建置选项(动态连结),那么最终建置出来的程式执行档将会相依于 MSVCR90D.dll 以及 MSVCP90D.dll 两个 DLL 档案。如果将相同的程式以 Release 组态建置完成,则会相依于 MSVCR90.dll 以及 MSVCP90.dll 二者。


Standard C++ Library

刚学习程式设计的入门者,经常会在满心欢喜地完成一件程式作品并且传给其他人使用时,却发现不能在别人的电脑上启动程式,其实就是陷入了使用者电脑缺少 DLL 档案而无法执行程式的窘境。有三种方法可以解决这个令人困扰的问题:

使用者的电脑,必须先安装「Visual C++ 可转发套件」(MSVC 2008 或 MSVC 2005 )。

将所需的 DLL 档案,例如 MSVCR90D.dll 与 MSVCP90D.dll,直接附在程式的下载包当中。

以静态连结方式建置程式执行档。

当你无法确定自己的程式或别人的程式,是否相依于某些特定的 DLL 档案时,有一个非常好用的免费工具程式 Dependency Walker,可以开启 EXE 格式的执行档或者 DLL 格式的动态程式库,然后详细地条列出它们所相依的 DLL 档案。

瞭解了几种不同的 CRT 版本选项之后,回到最前面的错误讯息问题,相信各位现在应该能够很清楚地理解,原来会发生这些奇怪的错误状况,是因为程式同时连结了 LIBCMTD.lib 与 MSVCRTD.lib 所以造成函式定义版本冲突。也就是说,程式连结器已经在其中一个 CRT 的版本中找到所需的函式定义,但此时却又跳出另外一位 CRT,也给了一份相同函式的实作版本,所以连结器无法判断应该忽略谁并且选择谁。

而这个状况的发生原因,就是你的程式与程式所连结的外部程式库,使用了不同的 CRT 版本之故。例如,当你的程式使用了 Lua,自然必须连结至 Lua 的程式库 lua5.1.lib,但如果 lua5.1.lib 是以静态连结版本的 CRT 建置而成,而你的程式却是以预设选项,动态连结 CRT 来建置程式执行档的话,如此一来就会产生上述这些错误讯息了。至此,问题的答案已昭然若揭,解决方法有二种:其一是将 Lua 重新以动态连结 CRT 的方式建置出一个新的程式库,其二则是将自己的程式专案改成以静态连结 CRT 方式建置。

换个角度想,当你身为一位程式库的设计开发者,想要将自己写的东西分享给其他人,但又不想要完全开放自己撰写的程式源码时,至少可以同时提供以下四种版本的程式库,以妥善满足使用者的各种不同需求:

Debug:动态连结除错版本

Release:动态连结版本

Debug_Static:静态连结除错版本

Release_Static:静态连结版本

然而,有时候世界并不会运作得如此理想。在某些特殊的状况下,当我们使用他人所写的第三方程式库时,有时可能只拿得到其中某个特定的版本,例如 Release_Static 版本时,就很有可能会遇到程式库冲突的错误情形。此时就需要视专案的实际需求而定,可以在专案属性中指定「忽略特定程式库」(Ignore Specific Library) 这个选项,让程式码连结器忽略某些程式库,以此化解动静程式库或新旧程式库之间的恩怨冲突。

小测验:你所撰写的程式,必须连结某个以静态多执行绪 (/MT) CRT 建置而成的程式库。如果你的程式在 Debug 组态下以多执行绪侦错 (/MTd) 选项建置,是否会产生冲突?如果你的程式在 Release 组态下以多执行绪 (/MT) 选项建置,是否会产生冲突?是的话,应该如何解决?

 

 

上面的方法还是不行!会出现其他问题的。

以下是我摸索出的最新的解决方法:

首先,所有的lib文件,使用/MTd或/MT编译。Debug调试模式使用/MTd,Release模式使用/MT。

然后,在自己的程序中也使用/MTd或/MT编译。这样就不会出问题了。

三种编译链接库的方式:

(1)连接Windows库。针对Win32 API编写的应用程序,上面的方法可能带来新问题,可以忽略libcmt.lib库,即可。如果还有其他问题,再忽略相应的库。

(2)MFC静态链接。上面的方法就是针对这种链接方式的,所以没问题。

(3)MFC动态链接。没有试过,应该和(1)类似。

 

最后补充:如果还不行,直接加入/force:multiple编译参数吧。这次之所以没有使用它,也是为了严谨起见。



2011年1月7日星期五

输出从1到1000的数


本文来自: http://stackoverflow.com/q/4568645/89806
有这样一个面试题——请把从1到1000的数打印出来,但你不能使用任何的循环语句或是条件语句。更不能写1000个printf或是cout用C/C++语言
我相信,大多数人一开始你可能想到的是递归算法:
void f(int n){
printf("%d\n",n);
(1000-n) ? f(n+1) : exit(0) ;
}
int main(){
f(1);
}
当然,题目中说了不能使用条件语句,所以,上面那种解法的不符合题意的,因为还是变向地使用了条件表达式。不过,我们可以用别的方法来让这个递归终止,比如:
除以零,当程序crash,呵呵。
void f(int n){
printf("%d\n",n);
n/(1000-n);
f(n+1);
}
还有这样退出递归的:

void yesprint(int i);
void noprint(int i);

typedef void(*fnPtr)(int);
fnPtr dispatch[] = { yesprint, noprint };

void yesprint(int i) {
printf("%d\n", i);
dispatch[i / 1000](i + 1);
}

void noprint(int i) { /* do nothing. */ }

int main() {
yesprint(1);
}
还有下面这些各种各样的解法:
#include

/* prints number i */
void print1(int i) {
printf("%d\n",i);
}

/* prints 10 numbers starting from i */
void print10(int i) {
print1(i);
print1(i+1);
print1(i+2);
print1(i+3);
print1(i+4);
print1(i+5);
print1(i+6);
print1(i+7);
print1(i+8);
print1(i+9);
}

/* prints 100 numbers starting from i */
void print100(int i) {
print10(i);
print10(i+10);
print10(i+20);
print10(i+30);
print10(i+40);
print10(i+50);
print10(i+60);
print10(i+70);
print10(i+80);
print10(i+90);
}

/* prints 1000 numbers starting from i */
void print1000(int i) {
print100(i);
print100(i+100);
print100(i+200);
print100(i+300);
print100(i+400);
print100(i+500);
print100(i+600);
print100(i+700);
print100(i+800);
print100(i+900);
}

int main() {
print1000(1);
return 0;
}
不过,print用得多了一些。我们可以用宏嘛。
#include
#define Out(i) printf("%d\n", i++);
#define REP(N) N N N N N N N N N N
#define Out1000(i) REP(REP(REP(Out(i))));
void main()
{
int i = 1;
Out1000(i);
}
不过,我们应该使用C++的一些特性,比如:
使用构造函数
class Printer
{
public:
Printer() { static unsigned i=1; cout << i++ << endl;; }

};

int main()
{
Printer p[1000];
}
或是更为NB的Template:
template
struct NumberGeneration{
static void out(std::ostream& os)
{
NumberGeneration::out(os);
os << N << std::endl;
}
};

template<>
struct NumberGeneration<1>{
static void out(std::ostream& os)
{
os << 1 << std::endl;
}
};

int main(){
NumberGeneration<1000>::out(std::cout);
}
最后来个BT一点的:
void main(int j) {
printf("%d\n", j);
(main + (exit - main)*(j/1000))(j+1);
}

2010年12月13日星期一

标准输出重定向

标准输入输出指的是控制台的输入输出,涉及的函数和对象包括C运行库中的printf,scanf,fprintf……,C++标准库中的cin,cout,cerr,……
这里只讨论标准输出的重定向。重定向包括定向到文件、屏幕(Console窗口)、GUI中。
(1)重定向到文件中
利用freopen函数实现。当将C运行库的stdout重定向到文件中后,C++标准库中的cout未做任何改变即已重定向了。
//将stdout重定向到文件中
FILE *stream;
if( (stream = freopen("file.txt", "w", stdout ) ) == NULL )
    exit(-1);
printf("this is stdout output");

//重新将stdout 重定向到console中
stream = freopen("CON","w",stdout);
pintf("And now back to the console once again");

(2)重定向到屏幕
参考资料:http://dslweb.nwnexus.com/~ast/dload/guicon.htm
对于console程序而言,谈论重定向到屏幕可能没有意义。所以这里谈论的是,对于GUI程序,也就是普通的Windows程序中,如何使用printf输出到console。
主要思路是,AllocConsole分配一个控制台窗口,GetStdHandle获得相应句柄再调用_open_osfhandle关联到C运行库的句柄,_fdopen打开句柄获得文件流指针,与stdout关联。
/*****************************************************
*guicon.cpp
*
*****************************************************/

#include <windows.h>
#include <io.h>
#include <stdio.h>
#include <fcntl.h>

#include "guicon.h"

static const WORD MAX_CONSOLE_LINES = 500;

void RedirectIOToConsole()
{
int hConHandle;
//HANDLE lStdHandle;
FILE *fp;
CONSOLE_SCREEN_BUFFER_INFO coninfo;

// allocate a console for this app
AllocConsole();

// set the screen buffer to be big enough to let us scroll text
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),
&coninfo);
coninfo.dwSize.Y = MAX_CONSOLE_LINES;
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),
coninfo.dwSize);

// redirect unbuffered STDOUT to the console
hConHandle = _open_osfhandle((intptr_t)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT);
fp = _fdopen( hConHandle, "w" );
*stdout = *fp;
setvbuf( stdout, NULL, _IONBF, 0 );

// redirect unbuffered STDIN to the console
hConHandle = _open_osfhandle((intptr_t)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT);
fp = _fdopen( hConHandle, "r" );
*stdin = *fp;
setvbuf( stdin, NULL, _IONBF, 0 );

// redirect unbuffered STDERR to the console
hConHandle = _open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT);
fp = _fdopen( hConHandle, "w" );
*stderr = *fp;
setvbuf( stderr, NULL, _IONBF, 0 );

}

(3)重定向到GUI中
参考资料:http://blog.vckbase.com/michael/archive/2005/06/06/6163.aspx

将stdout重定向某个MFC程序View中显示出来,实现稍有点复杂。总体思路是
1)创建一个匿名管道(pipe)
2)将stdout重定向到这个pipe上
3)创建一个线程,读取pipe中的内容,然后将这些内容输出到你希望输出的地方,譬如一个view里。
启动进程时,先调用StdoutRedirect(),那么这个进程中所有的printf都可以输出到View里。

#include <windows.h>
#include <io.h>
#include <stdio.h>
#include <fcntl.h>

HANDLE hOutputReadTmp = 0;
HANDLE hOutputWrite = 0;
void DisplayError(char* szMsg)
{
 TRACE(szMsg);
 TRACE(" ");
}

DWORD WINAPI ReadStdout(LPVOID lpvThreadParam)
{
 CHAR lpBuffer[256];
 DWORD nBytesRead;

 while(TRUE)
 {
 if (!ReadFile(hOutputReadTmp, lpBuffer,sizeof(lpBuffer),
 &nBytesRead,NULL) || !nBytesRead)
 {
 if (GetLastError() == ERROR_BROKEN_PIPE)
 break; // pipe done - normal exit path.
 else
 DisplayError("ReadFile"); // Something bad happened.
 }

 if (nBytesRead >0 )
 {
 //获取到printf的输出
 lpBuffer[nBytesRead] = 0;

 //replace your code here. Do it yourself.
 DisplayInView(lpBuffer);
 }
 }

 return 0;
}

void StdoutRedirect()
{
 if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,0,0))
 DisplayError("CreatePipe");

 int hCrt;
 FILE *hf;
 //AllocConsole();
 hCrt = _open_osfhandle(
 (long)hOutputWrite,
 _O_TEXT
 );
 hf = _fdopen( hCrt, "w" );
 *stdout = *hf;
 int i = setvbuf( stdout, NULL, _IONBF, 0 );

 // Launch the thread that gets the input and sends it to the child.
 DWORD ThreadID;
 HANDLE hThread = ::CreateThread(NULL,0,ReadStdout,
 0,0,&ThreadID);
}


后记:
想实现类似此帖中的接口,它把重定向到GUI的操作用一个回调函数来实现,可以很好地扩展,封装了匿名管道的操作,而不是像(3)中那样,用户需要在新创建的线程中修改。但尝试失败,可能是线程的操作不熟悉。

2010年8月2日星期一

一个小函数,ShowErrMsg

void ShowErrMsg()
{
TCHAR szBuf[80];
LPVOID lpMsgBuf;
DWORD dw = GetLastError();

FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );

MessageBox(NULL, lpMsgBuf, "系统错误", MB_OK|MB_ICONSTOP);

LocalFree(lpMsgBuf);
}

[转]关于Windows平台下内存的分配和释放

Windows平台下主要的内存管理途径:
new / delete
malloc / free
CoTaskMemAlloc / CoTaskMemFree
IMalloc::alloc / IMalloc/free
GlobalAlloc / GlobalFree
LocalAlloc / LocalFree
HeapAlloc / HeapFree
VirtualAlloc / VirtualFree

VirtualAlloc以页面为单位(4K)进行分配,是操纵虚拟内存的底层函数。

HeapAlloc在指定堆上分配内存。
Windows为每个进程设置了一个缺省堆,应用程序也可以用HeapCreate创建更多的堆。

GlobalAlloc和LocalAlloc原本为16位环境下的API,Win32为了保持兼容性而保留了这两个函数,但在Win32下已不存在全局堆(所有的堆都是进程私有),所以 GlobalAlloc 和 LocalAlloc 在Win32下意义完全相同,它们都在进程缺省堆中进行内存分配。需注意的是,在Win32下,GlobalAlloc的字面意思已失效,它并不能在进程间共享数据。微软强调,GlobalAlloc/LocalAlloc 比 HeapAlloc 要慢,已不再推荐使用,但由于GlobalAlloc/LocalAlloc 具有简单的使用接口,所以即使在微软所提供的源码中,它们仍被大量使用。

malloc是CRT函数,实现方式取决于具体的CRT版本。VC++的malloc系对 HeapAlloc 作简单的包装,而Borland C++则选择自己实现malloc。在应用程序启动时,CRT创建自己的私有堆,驻留在Win32堆的顶部。

C++中的new先调用 operator new,再调用构造函数生成类对象。而 operator new 在缺省情况下调用 malloc 进行内存分配。应用程序可以重载 operator new,选择其他的内存分配方式。

CoTaskMemAlloc用于COM对象,它在进程的缺省堆中分配内存。
IMalloc接口是对 CoTaskMemAlloc/CoTaskMemFree 的再次封装。

调用关系:
msvcrt.malloc => kernel32.HeapAlloc(ntdll.RtlAllocateHeap)
kernel32.LocalAlloc => ntdll.RtlAllocateHeap
kernel32.GlobleAlloc => ntdll.RtlAllocateHeap
kernel32.HeapAlloc == ntdll.RtlAllocateHeap (映射)
kernel32.VirtualAlloc => kernel32.VirtualAllocEx
kernel32.VirtualAllocEx => ntdll.NtAllocateVirtualMemory
ntdll.RtlAllocateHeap => ntdll.NtAllocateVirtualMemory
ntdll.NtAllocateVirtualMemory => ntdll.KiFastSystemCall
ntdll.KiFastSystemCall => sysenter指令 (0F34)

即:
new -> malloc -> HeapAlloc -> VirtualAlloc -> 驱动程序的_PageAlloc