大家都知道,當編寫一個win32 console application時,當運行此類程序的時候
默認情況下會有一個類似DOS窗口的console窗口,但是有的時候我們只想在程序
中運行一段功能代碼,不希望顯示這個console窗口,讓代碼執(zhí)行完畢之后程序自
動退出.
下面就介紹一下,如何隱藏win32 console application的console窗口
因為此種方法是通過設置編譯器的鏈接開關來實現(xiàn),所以讓我們來看一下編譯
器的鏈接開關選項(也就是linker選項).
首先我們來看一下linker的 /subsystem 選項
該選項的語法形式如下:
/SUBSYSTEM:{CONSOLE|EFI_APPLICATION|EFI_BOOT_SERVICE_DRIVER|
EFI_ROM|EFI_RUNTIME_DRIVER|NATIVE|POSIX|WINDOWS|WINDOWSCE}
[,major[.minor]]
這個鏈接選項告訴操作系統(tǒng)如何運行可執(zhí)行文件
CONSOLE:
win32 字符模式應用程序,此種類型的應用程序在運行的時候會產生一個類似DOS
窗口的控制臺窗口,如果在應用程序的主函數為main()或者wmain(),在默認情況下
該應用程序就是一個控制臺應用程序
Extensible Firmware Interface
和CPU具體架構相關的一個參數選項,并不常用,在這里暫不詳細介紹.
如果對此有興趣的可以訪問intel主頁來查看相關內容
NATIVE;
設備驅動器選項,如果/DRIVER:WDM選項被設定的話,該鏈接選項(NATIVE)就為默認選項
POSIX:
在windows NT 種運行在POSIX子系統(tǒng)上的應用程序
WINDOWS:
該類型的應用程序不產生console窗口,該類型的應用程序的窗口由用戶自己創(chuàng)建,簡而言之
就是一個標準的Win32 application,其入口地址為WinMain()函數或者wWinMain()函數的地址
如果你在應用程序種定義的主函數為WinMain或者wWinMain,在默認情況下該應用程序就是一個
Win32 Application !
WINDOWSCE:
運行在windows CE上的應用程序
major and minor (optional):
主版本號和次版本號,該選項為可選,該選項為0~65535之間的十進制整數
從上面可以看出如果我們建立一個win32 console application的話,linker的/subsystem選項應該為
CONSOLE,可以在VC開發(fā)環(huán)境的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指令來設置,還有一種就是直接在開發(fā)環(huán)境的
project->setting->link->project option中手工改動!
1 使用richedit 控件首先要加載 richedit 的dll.
LoadLibrary(_T("riched20.dll"));
2 加入對 richedit.h 頭文件的引用后,創(chuàng)建窗口
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);
這里創(chuàng)建了一個只讀的窗口,這樣只能通過函數輸入文字。
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 工程無所謂。但對于多字節(jié)來說一個漢字為2個字符。所以你通過strlen之類計算的字符串長度會比richedit 認為的要長如:
"大家好"。計算為6長度。但對于richedit來說為3個.發(fā)送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 );
#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的實現(xiàn),//參數必須都是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.h里x86平臺的宏定義摘錄如下(’"’號表示折行):
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)主要是為了某些需要內存的對齊的系統(tǒng).C語言的函數是從右向左壓入堆棧的,圖(1)是函數的參數在堆棧中的分布位置.我們看到va_list被定義成char*,有一些平臺或操作系統(tǒng)定義為void*.再看va_start的定義,定義為&v+_INTSIZEOF(v),而&v是固定參數在堆棧的地址,所以我們運行va_start(ap, v)以后,ap指向第一個可變參數在堆棧的地址,如圖:
高地址|-----------------------------|
|函數返回地址 |
|-----------------------------|
|
. |
|-----------------------------|
|第n個參數(第一個可變參數) |
|-----------------------------|<--va_start后ap指向
|第n-1個參數(最后一個固定參數)|
低地址|-----------------------------|<-- &v
圖( 1 )

然后,我們用va_arg()取得類型t的可變參數值,以上例為int型為例,我們看一下va_arg取int型的返回值: 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個參數(最后一個固定參數)|
低地址|-----------------------------|<-- &v
圖( 2 )


最后要說的是va_end宏的意思,x86平臺定義為ap=(char*)0;使ap不再指向堆棧,而是跟NULL一樣.有些直接定義為((void*)0),這樣編譯器不會為va_end產生代碼,例如gcc在linux的x86平臺就是這樣定義的.在這里大家要注意一個問題:由于參數的地址用于va_start宏,所以參數不能聲明為寄存器變量或作為函數或數組類型.關于va_start, va_arg, va_end的描述就是這些了,我們要注意的是不同的操作系統(tǒng)和硬件平臺的定義有些不同,但原理卻是相似的
讓assert嵌入更多的信息
assert(src!=0 && "VectorNormalize: src vector pointer is Null");
assert(dst!=0 && "VectorNormalize: dst vector pointer is Null");
有了這個直觀的字符串提示,就可以告訴當前的函數名,錯誤原因。
// 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 );
}
返回類型的名字
#pragma data_seg之前從來沒有用過,今天找出了它的一個妙用。
持續(xù)整理中......
#pragma data_seg介紹
應用一:單應用程序。
有的時候我們可能想讓一個應用程序只啟動一次,就像單件模式(singleton)一樣,實現(xiàn)的方法可能有多種,這里說說用#pragma data_seg來實現(xiàn)的方法,很是簡潔便利。
應用程序的入口文件前面加上
#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++;
上個星期, ric和我提出了這樣一個問題,就是一個人物技能體系,如何設定,比較原始的方法是,設立一個純虛基類,然后一層一層的派生,但是這樣有一個問題..就是如何出現(xiàn)兩個不同的派生體系對同一功能需求.這樣...就只能在兩個派生體系中都設定一份..造成代碼的無復用和龐大.雖然這點可以在前期避免,但是沒有人是神......前期構架無法得到最后的結構,構架總是在調整,我第一次提出的方法是..將每有功能以組件形式提供,在相應的功能需求者里組裝,這個思路是可以..但是實現(xiàn)起來有點郁悶,因為我不知道是用單純的類new...還是使用單件來做更好,相反,,我到是覺得這兩種都不好.
帶著這樣的需求和思路,去研究了一下Loki的Factory,AbstractFactory 類廠與抽象類廠.
Factoy可以理解為一個可以保存類的表,通過Register方法,將類以數字或字符的形式,保存起來,當然需要提供一個可以在內部new的函數.在使用用通過
在注冊時的類標示,加上CreateObject方法來返回相應的類指針,其實就相當于一個類表.我們可以通過我們自定義的標示來創(chuàng)建相對應的類.
AbstractFactory 可以理解為抽象的類表,就是在前期不需要提供相應的可以在內部new的函數,只需要提供類就可以注冊為一個類廠,在使用時.通過類來進行創(chuàng)建,最大的區(qū)別是...AbstractFactory 本身就提供組裝的概念.可以在設定基本工廠之后,根據基本工廠,設定具體工廠模型.在使用時,可以通過具體工廠模型得到在具體工廠模型中注冊的類,最大的區(qū)別時,,可以通過注冊類的基類.也就是基本工廠中注冊類來得到.這樣很爽..但是.還沒有想清楚在真正的人物技能體系中,如何去使用這個東東.偶滴神啊......讓偶想清楚吧...下面給些具體的例子
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>();
最早以前.就說要好好研究一下Loki庫..一直都忘記..要么就有事.剛好這幾天要寫一個事件ID與事件處理函數的綁訂機制.就想起來Loki的functor來了...花了兩天時間仔細研究了下loki中的幾個實現(xiàn).全部明白不敢說..不過確實明白了..這種思路..在這里也要多謝權哥...呵呵
我是從具體的現(xiàn)實思路出發(fā).來研究loki的..我的實現(xiàn)目地.是可以便捷的綁訂事件ID與事件處理函數,在回調事件處理函數時.不會有以前使用函數地址注冊時所產生的問題,比如.回調的函數中無法正常使用函數所歸屬類的信息.無法綁訂同一類不同對象的實現(xiàn)方法
首先先來研究..實現(xiàn)思路應該是一樣的..同樣是綁訂函數地址.這點.loki的functor以前幫我做好了.剩下的.只是如何能更好的使用
loki 的一個functor只能綁訂一個函數,
思路是,預先申請一個足夠大的functor數組,將每個申請的functor存入這個數組中,以數組的下標做為處理函數的ID;
解決了回調函數的問題,然后就是事件ID了.這個有點郁悶.實現(xiàn)思路有點繞.
首先事件ID肯定是聲明了的.然后利用聲明的事件ID自動生成相應的事件結構體,則于事件回調不需要變動參數,所以
所有結構體都派生自同一基體,然后就通過事件結構體中的事件ID來進行綁訂,這樣可以確訂聲明的處理方法必須隸屬于
某一個對應的事件ID,這點在回調注冊時也可以保證,默認要注冊兩個ID.用于判定當前ID是否超過以聲明的范圍 ,EventBegin EventEnd;
解決了事件ID之后..就是它們之前的綁訂了.這個簡單.在聲明進.先傳入事件結構體,再傳入用同樣事件結構體聲明的回調函數,然后將回調函數
放入相對應以事件ID做為下標的數組中.搞定
最后還有一個消息分派的問題,這個地方有很多做法,,,暫時我先用最簡單
由外界傳入一個觸發(fā)的事件ID,然后判定 這個ID是否合法,然后回調對應下標的functor...這樣..就回調了相應的處理函數了,參數這里
做的最簡單化處理,只是聲明了一個結構體,然后.將事件ID賦值,然后把結構體傳入回調函數,以后有需要的話..此結構體可由外界傳入
相應的值由外界傳入,不過..一個事件回調....貌似沒有這種要求
呵呵..總之對于模板編程,有了一個比較清晰的了解..最大的收獲就是,看著滿篇的模板,四處亂飛的typename typedef class..不再頭暈了...呵呵
在這個過程中..還研究了..單件模式,智能指針,抽象化工廠,呵呵..收獲非常大..也感覺自己以前的實現(xiàn)太不優(yōu)雅了...以后要往優(yōu)雅方向努力啦
Loki的單件模式非常Happy.....以后決定..徹底忘記自己 寫的單件...看都不看....專心用loki的.....還有智能指針..不過智能指針有個不爽的地方
..也不是不爽..只是我感覺失落....為啥呢...因為智能指針無法控制它什么時候釋放.......它自己會釋放..這樣雖然..安全都有保證,用著也開心....
不過....忽然不用我釋放了...總是覺得心里少點啥.........想delete......哈哈
變高手~~~~~變高手~~~~~變高手~~~~~變高手~~~~~變高手~~~~~變高手~~~~~變高手~~~~~變高手~~~~~
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.
這兩天的工作。就是重做粒子編輯器。。。原有的粒子編輯器。是使用自制的UI。極大的妨礙了擴展。和使用的便捷性。經過這兩天的重制。將原有的功能重新以MFC控件重現(xiàn)。接下來就是擴展原有的粒子效果了。。。。發(fā)張圖來秀一下