在控制台程序中隐藏控制台窗口

大家都知道,当编写一个win32 console application时,当运行此类程序的时候
默认情况下会有一个类似DOS窗口的console窗口,但是有的时候我们只想在程序
中运行一段功能代码,不希望显示这个console窗口,让代码执行完毕之后程序自
动退出.

下面就介绍一下,如何隐藏win32 console application的console窗口

因为此种方法是通过设置编译器的链接开关来实现,所以让我们来看一下编译
器的链接开关选项(也就是linker选项).

首先我们来看一下linker的 /subsystem 选项

该选项的语法形式如下:
/SUBSYSTEM:{CONSOLE|EFI_APPLICATION|EFI_BOOT_SERVICE_DRIVER|
            EFI_ROM|EFI_RUNTIME_DRIVER|NATIVE|POSIX|WINDOWS|WINDOWSCE}
            [,major[.minor]]

这个链接选项告诉操作系统如何运行可执行文件

CONSOLE:
win32 字符模式应用程序,此种类型的应用程序在运行的时候会产生一个类似DOS
窗口的控制台窗口,如果在应用程序的主函数为main()或者wmain(),在默认情况下
该应用程序就是一个控制台应用程序

Extensible Firmware Interface
和CPU具体架构相关的一个参数选项,并不常用,在这里暂不详细介绍.
如果对此有兴趣的可以访问intel主页来查看相关内容

NATIVE;
设备驱动器选项,如果/DRIVER:WDM选项被设定的话,该链接选项(NATIVE)就为默认选项

POSIX:
在windows NT 种运行在POSIX子系统上的应用程序

WINDOWS:
该类型的应用程序不产生console窗口,该类型的应用程序的窗口由用户自己创建,简而言之
就是一个标准的Win32 application,其入口地址为WinMain()函数或者wWinMain()函数的地址
如果你在应用程序种定义的主函数为WinMain或者wWinMain,在默认情况下该应用程序就是一个
Win32 Application !

WINDOWSCE:
运行在windows CE上的应用程序

major and minor (optional):
主版本号和次版本号,该选项为可选,该选项为0~65535之间的十进制整数

从上面可以看出如果我们建立一个win32 console application的话,linker的/subsystem选项应该为
CONSOLE,可以在VC开发环境的project->setting->link->project option中看到!

接下来我们再看看应用程序是如何运行的!
我们知道用VC编写的程序,运行的时候是需要 C\C++运行库支持的.当我们运行一个C/C++程序的时候
链接器会首先寻找应用程序的启动函数,例如:
如果你建立了一个console程序的话,编译器得链接开关会是以下这种形式
/subsystem:"console" /entry:"mainCRTStartup"   (ANSI)
/subsystem:"console" /entry:"wmainCRTStartuup" (UNICODE)

如果你建立了一个win32 application,编译器得链接开关则会是一下形式
/subsystem:"windows" /entry:"WinMain"   (ANSI)
/sbusystem:"windows" /entry:"wWinMain"  (UINCODE)

上面的两种形式可以再project->setting->link->project option中看到
上面的subsystem和entry并不需要都设置,如果你只设置了/subsystem:"console"
的话,那么默认的entry开关在默认情况下应为/entry:"mainCRTStartup"
反之,如果你在应用程序中定义了main函数的话,默认情况下,你的/subsystem开关
应该为/system:"console"


在默认情况下/subsystem 和/entry开关是匹配的,也就是
console对应mainCRTStartup或者wmainCRTStartup
windows对应WinMain或者wWinMain

但是我们也可以通过手动改动的方式使他们不匹配


例如我们可以这样改动

#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" ) // 设置入口地址


int main(int argc, char* argv[])
{
 MessageBox(NULL, "hello", "Notice", MB_OK);
 return 0;
}

在默认情况下链接器看到/subsystem下是windows选项的时候,它会自动寻找WinMain或者wWinMain

但我们强制指定入口地址,这样运行程序的时候默认的console窗口就会隐藏!


上面是在代码中使用#pragma指令来设置,还有一种就是直接在开发环境的
project->setting->link->project option中手工改动!

posted @ 2008-04-09 09:49 Randy 阅读(2589) | 评论 (0)编辑 收藏

richedit2

 

1 使用richedit 控件首先要加载 richedit 的dll.
LoadLibrary(_T("riched20.dll"));
2 加入对 richedit.h 头文件的引用后,创建窗口
hwndBuffer    =   CreateWindow(RICHEDIT_CLASS,NULL,WS_CHILD|WS_VISIBLE|ES_MULTILINE|WS_VSCROLL|ES_SUNKEN
                            |ES_AUTOVSCROLL|ES_READONLY|ES_SAVESEL,6,30,526,354,hWnd,(HMENU)EDIT_ID,hInstance,NULL); 
这里创建了一个只读的窗口,这样只能通过函数输入文字。
3 设置颜色:
//设置背景色
SendMessage( hwndBuffer, EM_SETBKGNDCOLOR, 0, RGB(0,0,0) ); 
//设置文字颜色
CHARFORMAT2 charFmt;
ZeroMemory(&charFmt,sizeof(charFmt));
charFmt.cbSize = sizeof(charFmt);
charFmt.dwMask = CFM_COLOR;      //只修改文字颜色标志
charFmt.crTextColor = RGB(0,179,0);//设置字体颜色  
SendMessage(hwndBuffer,EM_SETCHARFORMAT,0,(LPARAM)&charFmt);
//修改部分文字颜色
SendMessage(hwndBuffer,EM_SETSEL,0,10);    //选中0-10的文字
charFmt.crTextColor = it->clr;
//修改文字颜色
SendMessage(hwndBuffer,EM_SETCHARFORMAT,SCF_SELECTION,(LPARAM)&charFmt);           
注意对于richedit2.0  汉字仅作为一个字符。对于unicode 工程无所谓。但对于多字节来说一个汉字为2个字符。所以你通过strlen之类计算的字符串长度会比richedit 认为的要长如:
"大家好"。计算为6长度。但对于richedit来说为3个.发送EM_SEL消息,设置选中消息时必须以richedit为准.
SendMessage(hwndBuffer,EM_SETSEL,0,3);    如果指定为6将超出范围
4 滚动滚动条:
SendMessage(hwndBuffer,WM_VSCROLL, SB_BOTTOM, 0);    //滚动到文档的最后面
5 游标
int temp1,temp2;  //获取起始和结束位置
SendMessage(hwndBuffer,EM_GETSEL,(WPARAM)&temp1,(LPARAM)&temp2);
//在文档末尾输入字符:
SendMessage(hwndBuffer, EM_SETSEL,-1,-1);
SendMessage(hwndBuffer, EM_REPLACESEL, TRUE, (LPARAM) buffer );

posted @ 2008-03-25 20:01 Randy 阅读(1376) | 评论 (1)编辑 收藏

可变参数函数设计

#include "stdafx.h"
#include 
<stdio.h>
#include 
<stdarg.h>

int mul(int num,int data1,)
{
    
int total = data1;
    
int arg,i;
    va_list ap;
    va_start(ap,data1);
    
for(i=1;i<num;i++)
    {
        arg 
= va_arg(ap,int);
        total
*=arg;
    }
    va_end(ap);
    
return total;
}

long mul2(int i,)
{
    
int *p,j;
    p 
= &i+1;//p指向参数列表下一个位置
    long s = *p;
    
for (j=1;j<i;j++)
        s 
*= p[j];
    
return s;
}

int main()
{
    printf(
"%d\n",mul(3,2,3,5));
    printf(
"%d\n",mul2(3,2,3,5));
    
return 0;
}



printf的设计
#include "stdio.h"
#include 
"stdlib.h"
#include 
<stdarg.h>

void myprintf(char* fmt, )        //一个简单的类似于printf的实现,//参数必须都是int 类型
{
    
//char* pArg=NULL;               //等价于原来的va_list
    va_list pArg;
    
char c;
   
   
// pArg = (char*) &fmt;          //注意不要写成p = fmt !!因为这里要对参数取址,而不是取值
  
// pArg += sizeof(fmt);         //等价于原来的va_start         
    va_start(pArg,fmt);

    
do
    {
        c 
=*fmt;
        
if (c != '%')
        {
            putchar(c);            
//照原样输出字符
        }
        
else
        {
//按格式字符输出数据
            switch(*++fmt)
            {
            
case 'd':
                printf(
"%d",*((int*)pArg));           
                
break;
            
case 'x':
                printf(
"%#x",*((int*)pArg));
                
break;
            
case 'f':
                printf(
"%f",*((float*)pArg));
            
default:
                
break;
            }
            
//pArg += sizeof(int);               //等价于原来的va_arg
            va_arg(pArg,int);
        }
        
++fmt;
    }
while (*fmt != '\0');
    
//pArg = NULL;                               //等价于va_end
    va_end(pArg);
    
return;
}
int main(int argc, char* argv[])
{
    
int i = 1234;
    
int j = 5678;
   
    myprintf(
"the first test:i=%d",i,j);
    myprintf(
"the secend test:i=%f; %x;j=%d;",i,0xabcd,j);
    system(
"pause");
    
return 0;
}


可变参数在编译器中的处理 

    我们知道va_start,va_arg,va_end是在stdarg.h中被定义成宏的, 由于1)硬件平台的不同 2)编译器的不同,所以定义的宏也有所不同,下面以VC++stdarg.hx86平台的宏定义摘录如下(’"’号表示折行):
typedef char * va_list; 
#define _INTSIZEOF(n) \ 
((
sizeof(n)+sizeof(int)-1)&~(sizeof(int- 1) ) 
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) 
#define va_arg(ap,t) \ 
*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) 
#define va_end(ap) ( ap = (va_list)0 ) 

    定义_INTSIZEOF(n)主要是为了某些需要内存的对齐的系统.C语言的函数是从右向左压入堆栈的,(1)是函数的参数在堆栈中的分布位置.我们看到va_list被定义成char*,有一些平台或操作系统定义为void*.再看va_start的定义,定义为&v+_INTSIZEOF(v),&v是固定参数在堆栈的地址,所以我们运行va_start(ap, v)以后,ap指向第一个可变参数在堆栈的地址,如图:

高地址|-----------------------------| 
|函数返回地址 | 
|-----------------------------| 
|| 
|-----------------------------| 
|第n个参数(第一个可变参数) | 
|-----------------------------|<--va_start后ap指向 
|第n-1个参数(最后一个固定参数)| 
低地址
|-----------------------------|<-- &
图( 
1 ) 

    然后,我们用va_arg()取得类型t的可变参数值,以上例为int型为例,我们看一下va_argint型的返回值: j= ( *(int*)((ap += _INTSIZEOF(int))-_INTSIZEOF(int)) );
首先ap+=sizeof(int),已经指向下一个参数的地址了.然后返回ap-sizeof(int)int*指针,这正是第一个可变参数在堆栈里的地址(2).然后用*取得这个地址的内容(参数值)赋给j.

高地址|-----------------------------| 
|函数返回地址 | 
|-----------------------------| 
|| 
|-----------------------------|<--va_arg后ap指向 
|第n个参数(第一个可变参数) | 
|-----------------------------|<--va_start后ap指向 
|第n-1个参数(最后一个固定参数)| 
低地址
|-----------------------------|<-- &
图( 
2 ) 

    最后要说的是va_end宏的意思,x86平台定义为ap=(char*)0;使ap不再指向堆栈,而是跟NULL一样.有些直接定义为((void*)0),这样编译器不会为va_end产生代码,例如gcclinuxx86平台就是这样定义的.在这里大家要注意一个问题:由于参数的地址用于va_start,所以参数不能声明为寄存器变量或作为函数或数组类型.关于va_start, va_arg, va_end的描述就是这些了,我们要注意的是不同的操作系统和硬件平台的定义有些不同,但原理却是相似的

posted @ 2008-03-05 13:55 Randy 阅读(173) | 评论 (0)编辑 收藏

assert

让assert嵌入更多的信息

assert(src!=0 && "VectorNormalize: src vector pointer is Null");
assert(dst!=0 && "VectorNormalize: dst vector pointer is Null");

有了这个直观的字符串提示,就可以告诉当前的函数名,错误原因。

posted @ 2008-03-05 13:53 Randy 阅读(150) | 评论 (0)编辑 收藏

typeid Operator

// compile with: /GR /EHsc
#include <iostream>
#include <typeinfo.h>

template < typename T > T max( T arg1, T arg2 ) {
   cout << typeid( T ).name() << "s compared." << endl;
   return ( arg1 > arg2 ? arg1 : arg2 );
}

返回类型的名字

posted @ 2008-02-15 10:27 Randy 阅读(169) | 评论 (0)编辑 收藏

#pragma data_seg——新的尝试

 

                 

#pragma data_seg之前从来没有用过,今天找出了它的一个妙用。

持续整理中......

#pragma data_seg介绍

 

应用一:单应用程序。

    有的时候我们可能想让一个应用程序只启动一次,就像单件模式(singleton)一样,实现的方法可能有多种,这里说说用#pragma data_seg来实现的方法,很是简洁便利。

应用程序的入口文件前面加上

#pragma data_seg("flag_data")
int app_count = 0;
#pragma data_seg()
#pragma comment(linker,"/SECTION:flag_data,RWS")

然后程序启动的地方加上

 if(app_count>0)    // 如果计数大于0,则退出应用程序。
 {
  //MessageBox(NULL, "已经启动一个应用程序", "Warning", MB_OK);

  //printf("no%d application", app_count);

  return FALSE;
 }
 app_count++;

posted @ 2008-02-14 15:17 Randy 阅读(869) | 评论 (0)编辑 收藏

Loki库研究-Factory,AbstractFactory

上个星期, ric和我提出了这样一个问题,就是一个人物技能体系,如何设定,比较原始的方法是,设立一个纯虚基类,然后一层一层的派生,但是这样有一个问题..就是如何出现两个不同的派生体系对同一功能需求.这样...就只能在两个派生体系中都设定一份..造成代码的无复用和庞大.虽然这点可以在前期避免,但是没有人是神......前期构架无法得到最后的结构,构架总是在调整,我第一次提出的方法是..将每有功能以组件形式提供,在相应的功能需求者里组装,这个思路是可以..但是实现起来有点郁闷,因为我不知道是用单纯的类new...还是使用单件来做更好,相反,,我到是觉得这两种都不好.
带着这样的需求和思路,去研究了一下Loki的Factory,AbstractFactory  类厂与抽象类厂.
Factoy可以理解为一个可以保存类的表,通过Register方法,将类以数字或字符的形式,保存起来,当然需要提供一个可以在内部new的函数.在使用用通过
在注册时的类标示,加上CreateObject方法来返回相应的类指针,其实就相当于一个类表.我们可以通过我们自定义的标示来创建相对应的类.
AbstractFactory 可以理解为抽象的类表,就是在前期不需要提供相应的可以在内部new的函数,只需要提供类就可以注册为一个类厂,在使用时.通过类来进行创建,最大的区别是...AbstractFactory 本身就提供组装的概念.可以在设定基本工厂之后,根据基本工厂,设定具体工厂模型.在使用时,可以通过具体工厂模型得到在具体工厂模型中注册的类,最大的区别时,,可以通过注册类的基类.也就是基本工厂中注册类来得到.这样很爽..但是.还没有想清楚在真正的人物技能体系中,如何去使用这个东东.偶滴神啊......让偶想清楚吧...下面给些具体的例子

factory:
Factory< AbstractProduct, int > aFactory;
  aFactory.Register( 1, createProductNull );<br>
  aFactory.CreateObject( 1 );

AbstractFactory:
typedef ::Loki::AbstractFactory< LOKI_TYPELIST_3(Soldier, Monster, SuperMonster)>  \
AbstractEnemyFactory;


typedef ::Loki::ConcreteFactory<AbstractEnemyFactory, ::Loki::OpNewFactoryUnit,     \
LOKI_TYPELIST_3(SillySoldier, SillyMonster, SillySuperMonster)>
EasyLevelEnemyFactory;

typedef ::Loki::ConcreteFactory<AbstractEnemyFactory, ::Loki::OpNewFactoryUnit,     \
LOKI_TYPELIST_3(BadSoldier, BadMonster, BadSuperMonster)>
DieHardLevelEnemyFactory;

switch (id)
  {
  case 0:
   {
    pFactory = new EasyLevelEnemyFactory;
   }
   break;
  case 1:
   {
    pFactory  = new DieHardLevelEnemyFactory;
   }
   break;
  }

the.Select(0);
SmartPtr<Soldier>::Ptr pMonster = the.pFactory->Create<Soldier>();

posted @ 2007-11-05 08:06 Randy 阅读(758) | 评论 (0)编辑 收藏

Loki库研究-消息回调机制实现

最早以前.就说要好好研究一下Loki库..一直都忘记..要么就有事.刚好这几天要写一个事件ID与事件处理函数的绑订机制.就想起来Loki的functor来了...花了两天时间仔细研究了下loki中的几个实现.全部明白不敢说..不过确实明白了..这种思路..在这里也要多谢权哥...呵呵
我是从具体的现实思路出发.来研究loki的..我的实现目地.是可以便捷的绑订事件ID与事件处理函数,在回调事件处理函数时.不会有以前使用函数地址注册时所产生的问题,比如.回调的函数中无法正常使用函数所归属类的信息.无法绑订同一类不同对象的实现方法

首先先来研究..实现思路应该是一样的..同样是绑订函数地址.这点.loki的functor以前帮我做好了.剩下的.只是如何能更好的使用
loki 的一个functor只能绑订一个函数,
思路是,预先申请一个足够大的functor数组,将每个申请的functor存入这个数组中,以数组的下标做为处理函数的ID;

解决了回调函数的问题,然后就是事件ID了.这个有点郁闷.实现思路有点绕.

首先事件ID肯定是声明了的.然后利用声明的事件ID自动生成相应的事件结构体,则于事件回调不需要变动参数,所以
所有结构体都派生自同一基体,然后就通过事件结构体中的事件ID来进行绑订,这样可以确订声明的处理方法必须隶属于
某一个对应的事件ID,这点在回调注册时也可以保证,默认要注册两个ID.用于判定当前ID是否超过以声明的范围 ,EventBegin  EventEnd;

解决了事件ID之后..就是它们之前的绑订了.这个简单.在声明进.先传入事件结构体,再传入用同样事件结构体声明的回调函数,然后将回调函数
放入相对应以事件ID做为下标的数组中.搞定

最后还有一个消息分派的问题,这个地方有很多做法,,,暂时我先用最简单
由外界传入一个触发的事件ID,然后判定 这个ID是否合法,然后回调对应下标的functor...这样..就回调了相应的处理函数了,参数这里
做的最简单化处理,只是声明了一个结构体,然后.将事件ID赋值,然后把结构体传入回调函数,以后有需要的话..此结构体可由外界传入
相应的值由外界传入,不过..一个事件回调....貌似没有这种要求

呵呵..总之对于模板编程,有了一个比较清晰的了解..最大的收获就是,看着满篇的模板,四处乱飞的typename typedef class..不再头晕了...呵呵

在这个过程中..还研究了..单件模式,智能指针,抽象化工厂,呵呵..收获非常大..也感觉自己以前的实现太不优雅了...以后要往优雅方向努力啦

Loki的单件模式非常Happy.....以后决定..彻底忘记自己 写的单件...看都不看....专心用loki的.....还有智能指针..不过智能指针有个不爽的地方
..也不是不爽..只是我感觉失落....为啥呢...因为智能指针无法控制它什么时候释放.......它自己会释放..这样虽然..安全都有保证,用着也开心....
不过....忽然不用我释放了...总是觉得心里少点啥.........想delete......哈哈

变高手~~~~~变高手~~~~~变高手~~~~~变高手~~~~~变高手~~~~~变高手~~~~~变高手~~~~~变高手~~~~~

posted @ 2007-11-02 18:19 Randy 阅读(1409) | 评论 (0)编辑 收藏

MaxUserPort

HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters

Description

Determines the highest port number TCP can assign when an application requests an available user port from the system. Typically, ephemeral ports (those used briefly) are allocated to port numbers 1024 through 5000.


Windows 2000 does not add this entry to the registry. You can add it by editing the registry or by using a program that edits the registry.

posted @ 2007-10-29 12:53 Randy 阅读(231) | 评论 (0)编辑 收藏

粒子编辑器

这两天的工作。就是重做粒子编辑器。。。原有的粒子编辑器。是使用自制的UI。极大的妨碍了扩展。和使用的便捷性。经过这两天的重制。将原有的功能重新以MFC控件重现。接下来就是扩展原有的粒子效果了。。。。发张图来秀一下

posted @ 2007-10-26 20:27 Randy 阅读(510) | 评论 (1)编辑 收藏

仅列出标题
共4页: 1 2 3 4 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(3)

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜