筆記:深度探索C++物件模型 第四章

Function
語意學 ( The Semantics of Function )

摘要:

nonstatic member function

virtual member function

static member function

virtual member function(單一繼承)

多重繼承

函式的效能

Point-to-member functions

inline function

 

nonstatic member function

nonstatic member function 實際上會被內化為 nonmember 的形式,步驟如下:

  1. 改寫函式的 signature 以安插一個額外的參數,用以提供一個存取管道,使 class object 得以將此函式喚起.額外參數就是 this.
  2. 將對 "nonstatic data member 的存取動作" 改經由 this 指標存取.
  3. 改寫成一個外部函式,將函式名稱經過 "mangling" 處理,以成為一個獨一無二的語彙.

名稱的特殊處理 (Name mangling)

一般而言, member 的名稱後面會被加上 class 名稱. 若你宣告 extern "C", 就會壓抑 nonmember
functions 的 "mangling" 效果. 此 mangling 的動作, 各家 compiler 實作方式不同.

 

virtual member functions

若 ptr->normalize(); 

則會被轉化為

(*ptr->vptr[1])(ptr);

其中:

  • vptr 表示由 compiler 產生的指標,指向 virtual table, 其名稱也會被 "mangled", 因為在一個複雜的衍生體系中,可能存在多個
    vptrs.
  • 1 是 virtual tabe slot 的索引值.
  • 第二個 ptr 表示 this 指標.

若宣告為 inline, 則會被 compiler 當作一般 nonstatic member function 一樣地決議,提供極大的效率利益.

 

static member function

一般建議把 static data member 宣告為 nonpublic, 並提供一個或多個 member functions 來存取之.

主要特性: 沒有 this 指標.

次要特性: 他不能直接存取其 class 中的 nonstatic members; 他不能被宣告為 const, volatile, 或 virtual;
他不需要經由 class object 才被喚起.

如果取一個 static member function 的位址, 將獲得其在記憶體中的位置,其位址的型別並不是一個 "指向 class member
function 的指標", 而是一個 "nonmember 函式指標".即

&Point3D::object_count();

會得到

 unsigned int (*)();

而非

 unsigned int (Point3D:*)();

差不多等同於 nonmember function.

p.s. object_count 原型宣告為

unsigned int
Point3D::
object_count() {
return _object_count;
}

 

virtual member function(單一繼承)

單一繼承一般是在每個多型的 class object 身上增加 2 個 member:

  1. 一個字串或數字,表示 class 型別.
  2. 一個指標指向某表格,表格中持有程式的 virtual functions 的執行時期位址.為了找到函式位址,每個 virtual function
    被指派一個表格索引值.

這些工作都由 compiler 完成. 執行時期要做的只是在特定的 virtual table slot 中啟動 virtual function.

圖解. 若 Point3D 繼承 Point2D 繼承 Point, 那麼個別的 virtual table 就可能是

於是當

Point *ptr;
ptr=new Point3D();
ptr->z();

compiler 可以把該呼叫轉化為

(*ptr->vptr[4])(ptr);

 

多重繼承

在多重繼承中支援 virtual function,其複雜度圍繞在第二個及後繼的 base class 上,以及"必須在執行時期調整 this
指標"上.

即後繼的 class 會有多個 virtual table.

將後繼的物件位址指定給一個 base1 指標或 base2 指標時, virtual table 就要視指標的型態作切換,以免呼叫到錯誤的函數.

效率若依照原始 c++ 模型,會變的不好,但這方面各家 compiler 會利用 thunk 或 address points 策略來改善.

虛擬繼承下的 virtual functions

實作上,同樣要調整 this 指標,很複雜,效率也不一定較好,建議不要在一個 virtual base class 中宣告 nonstatic data
members.

 

函式的效能

inline > (nonmember friend=static member=nonstatic member) > virtual
member > virtual member(多重繼承) > virtual member(虛擬繼承)

virtual member 在層數越多的狀況下,其執行時間也成正比增加.

 

Point-to-Member functions

double (Point::*pmf)();
double (Point::*coord)()=&Point::x; //初始
coord=&Point::y; //或是這樣初始

於是可以

(origin.*coord)();

(ptr->*coord)();

這樣用.

實際上則會轉化為

(coord)(&origin);

(coord)(ptr);

支援"指向 virtual member functions"的指標

考慮如下片段(假設 z 為 virtual function)

float (Point::*pmf)()=&Point::z;
Point *ptr=new Point3D;
ptr->z(); //ok
(ptr->*pmf)(); //仍然 ok

compiler 實作上,必須定義 pmf, 使他能持有兩種數值,並且其數值能區分其意義.

多重繼承的狀況:

stroustrup 利用 union 來處理

struct __mptr {
int delta;
int index; //處理 virtual table 索引,不指時為 -1
union {
ptrfunc faddr;
int v_offset; //處理 nonvirtual member function
};
};

於是

(ptr->*pmf)();

會變成

(pmf.index<0)?
(*pmf.faddr)(ptr):
(*ptr->vptr[pmf.index](ptr));

Microsoft 以 vcall thunk 來作檢查,避免浪費檢查的時間,但副作用是,當傳遞一個不變值的指標給 member function 時,需要產生暫時性的物件.

效率:同樣地,不牽涉到"虛擬"+"多重"情況的,效率較佳.

 

inline function

一般處理時,有兩個階段:

  1. 分析函式,若因某些問題(複雜度過高,建構問題…等)被判斷不可 inline, 則會轉為 static 函式,並在被編譯模組中產生對應的函式定義.
  2. 真正的 inline function 擴展動作,是在呼叫的那一點上,這會帶來參數的求值動作及暫時性物件的管理.

通常需進入 assembler 中,才能得知是否真實現了 inline

形式參數擴展的情況大致如下:

inline int min(int i, int j) {
return i<j?i:j;
}
int
main() {
int minval;
int val1=1024,val2=2048;
minval=min(val1,val2);		//minval=val1<val2?val1:val2;
minval=min(1024,2048);		//minval=1024;
minval=min(foo(),bar()+1);	//int t1,t2;
//minval=(t1=foo()),(t2=bar()+1),t1<t2?t1:t2;

 

區域變數的情況

inline int
min(int i, int j) {
int minval=i<j?i:j;
return minval;
}
int local_var;
int minval;
...
minval=min(val1,val2);

則可能會代換為

int local_var;
int minval;
int __min_lv_minval;
minval=(__min_lv_minval=val1<val2?val1:val2),__min_ln_minval;

inline 函式中的區域變數再加上有副作用的參數,可能會導致大量暫時性物件的產生.並且使得程式大小暴增.避免過於複雜的 inline 函式,以免 compiler
無法擴展開來.

老鼠掛鈴鐺

原本不是很喜歡看政治新聞的
但最近的政治新聞實在是很有趣,當作每日一笑也是不錯. ^_^
不知道各位知不知道”老鼠掛鈴鐺”的故事??
有群老鼠,因為長年受到貓的侵擾而死傷慘重,覺得一定要想個方法來對付他,於是他們就開會討論.討論了許久,終於得到了一個方案-那就是幫貓掛鈴鐺.在幫貓掛上鈴鐺後,貓如果靠近他們的話,就會聽到鈴鐺聲,於是便能提早逃掉,減少喪生的機會.但是,誰去掛呢??
這似乎跟目前國民黨李前主席與國民黨的情況相同啊~~

筆記:深度探索C++物件模型 第三章

The Semantics of Data

摘要

 

static data member

static data member 永遠在 global data segment, 不影響 class object 的大小.

 

物件的大小

class object 的大小有可能因為 compiler 實作物件模型的方式不同而有不同,原因:

  1. compiler自動加上額外 data member, 以支援某些語言特性.
  2. alignment 邊界調整需要(比如: char 為配合機器特性而以 long 存放)

雖然目前 C++ compiler 已經非常進步,但早期會有兩種程式寫作防禦方格,以防止資料繫結錯誤.

  1. 把 data member 寫在 class 的開頭.
  2. inline function 移到 class 宣告之後,而不在宣告區裡面實作, 如:
extern int x;
class Point3D {
private:
float x,y,z;
public:
float X() const {return x;} 	//到底傳回哪個 x 呢?
float getX() const; 			//早期若不這樣寫,會造成資料繫結到上面那個 x
};
inline float
Point3D::
getX() const {
return x;
}

 

data member 的佈局

data member 實際的存放(佈局)

注意:各 data member 再實際存放時不一定連續.

如:

class Point3D {
float x;
float y;
float z;
};

x,y,z 不一定是連續的,有可能為了要補 alignment 或因compiler 的調整為 y,x,z, 主要原因是 C++ Standard 對此採放任態度.一般而言,仍是連續的.

 

data member 的存取

當 member 被宣告為 static 時,實際上和一般變數存取一樣,因為 static member 存放在 class 之外,不需要再透過 class
去存取. 若不是宣告為 static, 事實上,都會透過一個隱含的 class object (this) 完成,也就是類似這樣

Point3D
Point3D:translate( Point3D* this, const Point3D &pt) {
this->x+=pt.x;
this->y+=pt.y;
this->z+=pt.z;
}

若在程式中對 data member 做存取,如:

origin.y=0.0;

那麼實際上將等於

&origin+(&Point3D::y-1)

指向 data member 的指標,其 offset 值總是被加上 1, 這樣子 compiler就能需分出"一個指向 data member
的指標,用以指出 class 的第一個 member"和"一個指向 data member 的指標,未指向任何 member"的情況.(那就是上面為什麼要
-1的原因).

 

繼承與 data member

在沒有 virtual function 的狀況下,和 struct 相同.原本是獨立不相干的 class 湊成 type / subtype, 並有繼承關係(如
Point2D -> Point3D), 經驗不足的人可能會重複設計一些相同動作的函式,以 constructor 和 operator += 為例,
可以做成 inline. 另外把一個 class 分解為二層或更多層,有可能會為了"表現 class 體系之抽象化"而膨脹所需空間.如:
parent 為兩個 int, child 為一個 char, grandchild 為一個 char, compiler 會為了 alignment
而填補空間.

加上 virtual function 後,將需要導入 vptr, 並在每個函式做 vptr 的處理, 解構時也要把 vptr 抹消,一般而言, vptr
放在 class 的最後面,但 visual c++ 放在最前面,主要是為了繼承的效率問題.

class 若內含一個或多個 virtual base class subobjects, 將會分割為二部分:一個不變區域和一個共享區域,不變區域的資料,不管後繼如何衍化,總擁有固定的
offset(自 object 的起頭算起), 所以可以直接存取,至於共享區域,表現的是 virtual base class subobject, 其位置會因為每次衍生動作而有變化,只能被間接存取,
compiler 會在子類別中安插一些指標,每個指標指向一個 virtual base class.這樣的做法有兩個缺點:

  1. 每個物件必須針對其每個 virtual base class 背負一個額外指標(空間增加).
  2. 由於虛擬繼承串鏈的加長,導致間接存取層次增加.(時間增加)

大部分編譯器到今天仍使用"經由拷貝動作取得所有的 nested virtual base class 指標,放到 derived class
object 之中"來解決第二個問題.

第一個問題一般有兩個解法. Microsoft compiler 引入 virtual base class table, 每一個 class object
如果有一個或多個 virtual base class, 就會由 compiler 安插一個 pointer, 指向 virtual base class
table. 第二個方法是在 virtual function table 中放置 virtual base class 的 offset.

 

指向 data members 的指標

可用以決定 vptr 放在 class 的 begin 或 end, 另一個用途可用來決定 class 中的 access sections.

usage:

	printf("&Point3d::x=%p\n",&Point3d::x);

如果 vptr 在物件尾巴,則 offset 為 0

如果 vptr 在物件起頭,則 offset 為 4

但為何在尾巴時,傳回值總是多 1? 意即 1 主要是用以區分"沒有指向任何 data member" 的指標和 "指向第一個
data member" 的指標.

注意,此種額外的間接性會降低"把所有處理都搬移到暫存器中執行"最佳能力.

筆記:深度探索C++物件模型 第二章

摘要

 

簡介

編譯器可能會有隱含的動作,如對 overload operator 的誤判. 因為 compiler 會自動去尋找最符合其意義的來解釋你的意圖.

 

constructor

以下是各種 default constructor 的建構情形. 一般若 class 沒有宣告 constructor, 那麼 compiler 會在需要時為他建立一個簡單的
constructor, 有四種狀況:

1. class 沒有 constructor, 但成員之中有 member object, 而該 member object 有 default constructor
時, compiler 必定會為前者合成一個 default constructor.

假設

class Dopey {...};
class Sneezy {...};
class Bashful {...};
class Snow_White {
public:
Dopey dopey;
Sneezy sneezy;
Bashful bashful;
private:
int mumble;
};

Snow_White 沒有 default constructor, 那麼 compiler 會為他合成一個 default constructor,
依序喚起 Dopey, Sneezy, Bashful 的 default constructor. 若 Snow_White 定義了 constructor
如下:

Snow_White::Snow_White:sneezy(1024) {
mumble=2048;
}

則 compiler 會擴充為

Snow_White::Snow_White();sneezy(1024) {
dopey.Dopey::Dopey();
sneezy.Sneezy::Sneezy(1024);
bashful.Bashful::Bashful();
mumble=2048;
}

2. 若 class 繼承一個帶有 default constructor 的 class, 則 compiler 會為這個 class 合成一個 constructor,
這個 constructor 只呼叫 parent class 的 constructor.

又若有多個 constructor, 但卻沒有 default constructor 時, compiler 會擴張各 constructor, 以便去呼叫必要的
default constructor. 但卻不會去合成一個新的 default constructor.

3. 若 class 有 virtual function 的時候, compiler 會在 default constructor 中把 vtable
初始化,以便讓子類別能正確呼叫到 virtual function 所對應的 function.

4. virtual base class, 各 compiler 的實作方法不同,但共通點都是要使 virtual base class 在每個 derived
class object 中的位置,能夠在 runtime 時準備妥當.例如:

class X { public: int i; };
class A: public virtual X { public: int j; };
class B: public virtual X { public: double d; };
class C:public virtual X {public: int k;};
//無法在編譯時決議(resolve)出 pa->X::i
void foo( const A* pa) { pa->i=1024; }
int
main() {
foo(new A);
foo(new C);
//...
}

所以, compiler 必須改變"執行存取動作"的程式碼,使得 X::i 能延遲到 runtime 時才決定下來.

以上四種情況都會使 compiler 必須為未宣告 constructor 的 class 合成 default constructor, 這些被 compiler
合成出來的東西, 在 C++ Standard 中稱為 implict nontrival default constructor.

除了這四種情況之外, compiler 不會合成任何 constructor.

另外在合成的 default constructor 中, 只有 base class object 和 member class object 會被初始化,
其他 nonstatic data member,如: int, int*, int array 等都不會.

新手誤解:

1.任何 class 若沒有 default constructor, 就會自動合成一個.

2. compiler合成的 default constructor 會明白設定"class內每個 data member 的預設值".

 

copy constructor

有三種情況會以一 object 內容作為另一 class object 的初值.

  1. string bb; string aa=bb;
  2. object 被當作參數交給函式時.
  3. 函數傳回值是 object 時.

當沒有提供 copy constructor 時,會直接把 member 一個個地複製到要複製的 object 上, 如:

class string {
char *str;
int len;
};

string noun("book");
string verb=noun;

實際上是

verb.str=noun.str;
verb.len=noun.len;

此處的 copy constructor 不等於 copy assignment (operator=)!!

default constructor 和 copy constructor 在必要的時候才用 compiler 產生出來.

請注意 member 若有 pointer 時,那麼預設會把指標指過去,就會有潛在的指標問題!

此種情況應宣告 explicit copy constructor 來解決此問題.如:

class string {
public:
string(const char *);
string(const string&);
};

一般 copy 會有四種情況, class 不展現出 "bitwise copy semantics"(即上述狀況):

  1. 1.class 內含一個 member object 而後者有 copy constructor 時.
  2. 2.class衍生自一個 base class, 而 base class 有 copy constructor 時.
  3. 3.class宣告一個或多個 virtual functions.
  4. 4.當 class 衍生自一個繼承串鏈,其中有一個或多個 virtual base classes.

第三種情況需考慮到之前第一章所提到的 virtual table.

因此若父類別有 animate() 和 draw()這兩個 virtual function,而子類別增加 dance(), 則不能

childclass cc1;
parentclass parent1=cc1;

會造成 virtual table 被切掉,因為 parent1 的 virtual table 根本就沒有 dance.若 parent1宣告為參考或指標時,被
compiler 合成出來的 copy constructor 會把隱含的 vptr 指向 childclass 的 virtual table, 而非
bitwise copy.這個也叫做 upcasting.

第四種情況則請回想一下 virtual base classes, 因為只會有一個實體存在,因此,compiler 會特別審慎考量,不會 bitwise
copy.

NRV最佳化,如下情況會被 compiler

x bar() {
X xx;
//....
return xx;
}

轉為如下的情況

void bar(X& __result) {
__result.X::X();
//....
return;
}

 

NRV

NRV -> Named Return Value 提供重要效率改善.

 

使用 member initialization list.

時機:

  1. 當初始化一個 reference member
  2. 當初始化一個 const member 時
  3. 當喚起一個 base class 的 constructor, 而他擁有一個參數時.
  4. 當喚起一個 member class 的 constructor, 而他擁有一個參數時.

Why:

一般這樣寫, compiler 會先產生一暫存物件, 再 assign 給 member object, 所以效率不彰.

class Word {
String _name;
int _cnt;
public:
Word(){
_name=0;
_cnt=0;
}
};

所以要用 Word::Word:_name(0) {_cnt=0;}; 效率較佳.

缺點及注意事項:

注意 initialization list 的初始順序. 若 member object 依賴性太高,最好不要放到 initialization list
中,而應移至 constructor 中.

What is an Interface

What is an iterface?(什麼是介面)?
在英文裡面,一個介面(Interface)是一組用來與不相干實體互動的設備或系統.根據這個定義,一個遙控器是一組介於你和電視的介面,英文是一組介於兩個人之間的介面,在軍隊中的行為協定是一組在不同階級的人之間的介面.在Java語言中,一組介面是一個與不相干物件互動的設備.一組介面大致可以比擬為一個協定(在行為上的允許).實際上,其他物件導向語言也有介面的功能,但他們稱他們的介面為協定.
腳踏車類別和他的後繼類別定義了一部腳踏車在騎的時候可以做什麼和不可以做什麼,但不包括腳踏車在其他期間與世界的互動.舉例來說,在店裡的一部腳踏車可以被一個庫存程式來管理.一個庫存程式不管物品是什麼類別的,他只要每個物品提供確切的資訊,像是價錢和追蹤號碼,就能管理.取代在其他不相關聯的物品上作強制類別關聯的做法是,庫存程式初始化了一個通訊的協定.這個協定引入常數和函式的集合,包含在一個介面之中.庫存介面將會被定義,但不是實作,裡面的方法將會被用來設定和取得零售價錢,給予一個追蹤號碼等等.
在庫存程式裡作業時,腳踏車類別必須藉著實作這個介面以同意這個協定.當一個類別實作一個介面,這個類別就需要實作在介面中定義的所有方法.例如,腳踏車類別將提供設定和取得零售價格,給予一個追蹤號碼等方法的實作.
使用一個介面去定義一個行為的協定,以用來被任何類別來實作.介面在以下情況是非常有用的:
.在沒有關聯的類別中找到相似點,而不需要強制給予類別的關係.
.定義一個或多個類別預計要實作的方法.
.不需要顯露出物件的類別,就能顯露他的程式化介面.

Windows Script Component 與 NT/2000 的安全

摘要:說明 Windows Script Component 與 NT/2000 檔案安全的關係

內容:

說明
參考

下載本文章的範例程式(下載後以滑鼠右鍵點選,然後選擇註冊即可)

說明

前二日,我在使用WSC(Windows Script Component)的時候,發現了很詭異的事情.

這個元件不管是利用VB或是利用Windows Script Host呼叫,都是正常而且可以運作的.

但是在 ASP 中,卻始終無法使用, 我為了這個問題,搞了兩個多小時,才終於搞懂,這個檔案的安全權限必須要加入IUSR_XXXX(IIS訪客)這個使用者才行!

唉唉唉~~

真是難搞的要命,如果各位有遇到難解的問題,不妨檢查一下資料夾以及檔案的安全權限,也許就能迎刃而解了.

呼~~

上面有提到 WSC, 我在這裡也順便說明一下, WSC 是 Microsoft 提出的一個與 Script 相關的技術,它讓我們可以利用 Script
來撰寫COM的元件,你可以參考http://www.microsoft.com/taiwan/products/develop/scripting/,這裡有許多與
Script 相關的技術文件.

本篇文章所附的範例則是來自 Active Server Pages 3.0 深度探索一書中的範例,主要的用處是結合Recordset來產生一個表格,使用方法很簡單:

dim objtbl
set objtbl = server.createobject("asptable.wsc")
objtbl.addcolumn "員工編號","emp_no",""
objtbl.addcolumn "員工姓名","emp_name", ""
obj.gettext()

不過我改寫過了,增加了兩個方法,一個方法是getpagetext(pageno),可以得到第幾頁的Table;另一個則是obj.getstrtext(),取得整個table的html字串.

當然這裡寫的還不是清楚,以後再來補吧 ^_^

參考

Active Server Pages 3.0 深度探索(國外出版社Wrox,國內則是由碁峰翻譯後發行)

Build Library How-to

程式碼
======
假設 main() 呼叫 iloveso() 這個函式,而且程式分開的話,程式碼就會像這樣子:
callso.c
#include <stdio.h>
void iloveso(void);
int
main(void)
{
printf(“\nmain() begin\n”);
iloveso();
printf(“main() end\n”);
}
iloveso.c
#include <stdio.h>
extern void iloveso(void);
void
iloveso(void)
{
printf(“here is so!!\n”);
}
一般做法
========
一般的情況下,我們會這樣去編譯程式
gcc callso.c iloveso.c –o callso
編譯出來後,直接執行 ./callso 就可以執行程式了。
靜態連結
========
因為 iloveso() 這個函式,其他程式也可能會使用到,我們希望把他獨立出來,這樣子,別人就可以直接連結他,而不需要重新編譯一次。
我們可以先編譯為 obj 檔之後,再利用 ar 指令,將 obj 檔變為 .a (靜態library) 檔。
再執行Ranlib 以確保 .a 檔能跟 unix 相容。
gcc -c iloveso.c -o iloveso.o
ar rcv libiloveso.a iloveso.o
ranlib libiloveso.a
為了驗證這樣子是可行的,請先在 /etc/ld.so.conf 最後加上 /usr/local/lib。
接著執行 ldconfig。然後將 libiloveso.a 放到 /usr/local/lib
執行
gcc –o callso callso.c –liloveso
沒有錯誤訊息的話表示成功,請執行 ./callso 試試。
如果有錯誤的話,通常是因為 ld.so.conf 檔案未設定,或設定後未執行 ldconfig 的緣故。
動態連結
========
當有很多程式都使用同一個靜態Library時,每個程式都會將該靜態連結檔給含括進來,所以會造成空間浪費,我們可以利用動態連結避免掉這個缺點。
同樣的,我們可以利用下列的程式來編譯動態連結檔
gcc -shared -Wl,-soname,libiloveso.so -o libiloveso.so iloveso.c -lc
這樣子就直接產出 libiloveso.so 了。
接下來同樣安裝到 /usr/local/lib,確定ld.so.conf有增加/usr/local/lib後,執行 ldconfig,以完成設定。
然後利用如下指令編譯 callso.c
gcc -o callso callso.c –liloveso
這樣就 ok 了,你可以試著用 ldd callso 去驗證一下,是不是真的有連結到。
結論及注意事項
==============
發現了嗎??不管是靜態或動態,程式碼和程式指令都不曾變更過,只有編譯Library 檔時,有些許差別而已。這對程式設計師來說,真是方便很多。
要特別注意的是Library檔必須以 lib 為開頭;另外,當 .a 和.so 同時存在時,gcc 會自動以 .so 作為優先連結對象,若你要強迫gcc作靜態連結,就要在編譯時加上 –static 。

VFP7 對 XML 的支援

XML是一種在網路上用來描述與傳遞的標準語言,就跟 HTML 是用來建立並顯示網頁的標準語言一樣.
XML使用標籤和屬性來界定資料,並且完整的保留資料原有的解釋讓應用程式去閱讀.
為了盡可能的要讓你更容易的利用XML來實作資料交換,VFP提供如下的新函數和功能:
CURSORTOXML( ) 將一個Cursor轉換成XML.
XMLUPDATEGRAM( ) 從一個有被變更過的工作區或cursor建立一個XML更新語法.
XMLTOCURSOR( ) 將XML資料轉換成Cursor或資料表格.
以上轉譯自 VFP7 提供的輔助說明.
以下就簡單介紹用法,但詳細用法,仍請參照輔助說明.
CursorToXML()
直接把工作區的資料轉換為一個xml檔案
舉例來說
CursorToXML(“LABELS”, “myXMLFile.xml”, 1, 512, 0, ;
“mySchema.xsd”, “http://www.microsoft.com/mySchema.xsd”)
這一行就是說把 LABELS 這個工作區的資料,轉換為 myXMLFile.xml
1 的意思就是Element-centric XML ,轉換為以元素為主的XML.
512 是表示第二個參數是一個檔案
0 則是設定編碼原則
“mySchema.xsd” 則是轉出後的規則檔
“http://www.microsoft.com/mySchema.xsd” 則是表示規則要參照 www.microsoft.com 上的 mySchema.xsd
XMLTOCursor()
就是把xml檔案轉換為一個工作區,注意:目前工作區若有開啟檔案,將會被關閉!
XMLToCursor(“myXMLFile.xml”,”LABELS”)
XMLUpdateGram()
這是一個很有用的函數,使用前,你必須先把目前的buffering開啟
它主要是把目前工作區有被變更過的資料轉變為 xml
只有變更過的才會喔~
於是可以聯想到,資料在Client被處理完,可以只把有被變更過的傳回Server
大大減低網路的負荷,並且提昇速度喔
之後就可以利用 xmltocursor() 函數來處理

初試啼聲 – 利用 Visual FoxPro 建立 Web Services

本文轉譯自VFP7光碟片 \technical articles\vfpws.doc

McAlister Merchant
Microsoft Corporation
Created: March 2001
Revised: March 2001
Applies To: Microsoft Visual Foxpro

總覽

Web Services 可以在任何地方被使用.你可以自己建立他們,或者你可以在他們被公佈的地方存取他們.
你可以使用VFP去建立 Web Services, 而且如果你在IntelliSense中註冊他們,VFP使得你能近乎自動化地存取他們變的可能.
你也可以使用VFP存取利用其他語言建立的 Web Services.

內容

介紹
Web Services
Establishing a Virtual Directory (建立一個虛擬目錄)
Creating a Project (建立專案)
Creating a COM Server (建立一個COM伺服器)
Creating and Publishing a Web Service (建立並發布一個 Web Service)
Registering a Web Service (註冊一個 Web Service)
Using a Web Service (使用一個 Web Service)
更多資訊
譯註

 

介紹

使用VFP建立 Web Services 是一個複雜的過程,因為牽涉到使用IIS, VFP COM伺服器和SOAP.
你使用IIS去建立一個虛擬目錄來參照到你的Service擺放在硬碟上的位置.
IIS可以利用MS NT4 Option Pack, MS W2k Professional或MS W2K Server來安裝.(在Option Pack中,IIS是其中的一部份,在W2K
Pro中可以利用新增/移除來安裝,W2K Server則是安裝時會自動一併安裝).譯註1.
然後你可以使用專案管理員去建立或編譯COM物件.當COM物件被建立了,你可以使用VFP Web Services精靈把他們公佈到你的虛擬目錄中.
你可以利用VFP的IntelliSense管理員去註冊 Web Services,所以Web Service的佈置和程式碼中必要呼叫的常式能迅速被執行.
這篇文章引領你為現存的COM伺服器建立Web Serveice並且準備Web Service以使用的這個複雜過程.
你能從一個COM伺服器建立一個簡單的Web Service,讓任何人都能從HTTP協定上存取.
這個 Web Service 提供一個關於目前目錄和有多少檔案在目錄中的簡單報告.

Web Services

Web Services延展了你自己的COM伺服器的可存取能力,能夠使得其他開發者能透過瀏覽器來存取.
Web Services提供機能並且能跨平台和在異質連接中存取資料.
你可以從Web Service存在的地方建立並使用Web Services去提供或者獲得功能.(譯註2)
大部分Web Service重要的觀點是Web Services使用HTTP協定和SOAP代理人,因此功能可以在跨越防火牆之後,仍然有效.
因為HTTP和SOAP是不管平台的,所以Web Services也是不管平台的.在某些案件,這仍能在COM失效的時候提供功能.
要發布一個 Web Service, 你必須在IIS中建立一個虛擬目錄,然後建立或複製一個COM伺服器,從而產生一個Web Service到你的虛擬目錄中.

 

Establishing a Virtual Directory(建立一個虛擬目錄)

你必須建立或識別一個目錄以存放你的 Web Services.
儘管你可以使用一個已存在的目錄,你仍可能想要在你硬碟上建立一個不一樣的地方存放這些檔案以方便管理你的Web Service檔案.
去建立一個虛擬目錄,譯註3
1.在VFP主要目錄下(\Program Files\Microsofft Visual FoxPro 7)建立一個資料夾,並命名為 Web Services.
2.在控制台裡的系統管理工具資料夾中,雙擊Internet服務管理員.
3.Internet服務管理員視窗打開,在左邊會顯示一個樹狀目錄,他顯示了你的硬碟.展開節點,以便能看到子資料夾.
4.在樹狀目錄中選擇預設的Web站台.
5.從右擊選單中,選擇"新增",然後點選"虛擬目錄".這會開啟"虛擬目錄新增精靈".
6.遵循精靈的指示進行,選擇虛擬目錄的時候,指定到步驟一所建立的 Web Services 資料夾.
你已經建立了一個虛擬目錄,參照到你的Web Services資料夾,之後你將會在這裡存放你建立的所有Web Services檔案.

 

Creating a Project (建立一個專案)

專案管理員在VFP中是十分基礎的應用程式建立工具.
在VFP專案中,你可以組合各式各項的文件,資料,類別,程式碼和其他類型的檔案等.
一個專案提供了你可能想要含括到一個伺服器中的所有元件,並且容納,以便編輯.
在這個範例中,你使用專案去編譯你的 Web Services
去建立一個專案,譯註4
1.在File(檔案)選單中,選擇 New(開新檔案)
2.在New(開新檔案)的對話盒中選擇 Project(專案),並點選 New File(開新檔案).
3.在 Create(建立)對話盒中,選擇你之前建立的 Web Services 資料夾,並且取名為 myWServ1.
4.點選 Save(存檔)
現在你已經在 Web Services 資料夾中建立了你的專案.這使得當你完成加入元件後可以在正確的資料夾中自動的儲存並編譯.

 

Creating a COM Server(建立一個COM伺服器)

因為,一個Web Service是一個.dll或.exe的檔案,你必須使用一個COM伺服器來當作你Web Service的基礎.
一個COM伺服器在VFP來說是一個有.dll或.exe副檔名,裡面包含從一個利用OLEPUBLIC識別字所建立的類別.
當一個物件被指定為OLEPUBLIC時,表示可以被Automation客戶端存取.當你從.dll檔案發布Web Service時,一個新的.wsdl檔案將會被產生.
.wsdl檔案有必要的XML源碼,他描述在.dll檔中的類別和參照實際在Web Service背後出力的.dll檔案.
從這個COM伺服器範例中,你將建立一個custom類別,他簡單回報了一些非常簡單的訊息.
在這個過程中,你將要寫一個程式來建立一個包含兩個方法的類別,並且編譯這個專案成一個COM伺服器和一個已發布的Web Service.
去建立一個簡單的類別,譯註5
1.在File(檔案)選單中,選擇New(新增).
2.在New(開新檔案)對話盒中,選擇Project(專案),並點選New file(開新檔案).這會開啟Project Manager(專案管理員).
3.在Project Manager(專案管理員)的All(所有)頁籤中,將Code(程式)展開,選擇Programs(程式碼),然後選擇New(開新檔案).
4.將下列的程式碼複製到程式碼編輯視窗中.
*– returns curdir() and number of JPEG files

DEFINE CLASS showem AS session OLEPUBLIC

Name = "showem"

PROCEDURE justshow
howmany=ADIR(afilArry,"*.jpg")
IF howmany <1
fileNm="no"
ELSE
fileNm=ALLTRIM(STR(howmany))
ENDIF

X = "about " + CURDIR() + " … "
Y = "There are " + fileNm + " JPEG files here."
RETURN x + y
ENDPROC
ENDDEFINE

這個程式定義了一個類別 showem,他有一個方法 justshow
5.關閉編輯視窗,並在Save As(另存新檔)對話盒中,將程式命名為myWServ1,並點選存檔.
6.確定 myServ1 設定為主程式.(如果他被設定為主程式,他將會以粗體呈現).如果沒有,在Project Manager(專案管理員)中,以滑鼠右鍵點選myWServ1並在快捷選單中選擇Set
Main(設為主程式).

 

去編譯這個COM伺服器

1.在Project Manager(專案管理員)中,選擇 Build (建立)
2.選擇 Single-threaded COM Server
3.選擇 Ok, 並接受名稱為 myWServ1
VFP將會編譯這個myWServ1檔案為一個COM伺服器,之後你就可以將這個COM伺服器轉為Web Service.

 

Creating and Publishing a Web Service(建立並發布一個Web Service)

必要地加入一個元件並且編譯COM伺服器,才能將你的COM伺服器轉為Web Service, 當成一個 Web Service發布.譯註6
在你指定完這些設定,VFP會建立必要的XML和SOAP檔案以將你的伺服器轉為一個 Web Service,註冊你的新 Web Service,並發布到你的虛擬目錄中.
去編譯和發佈一個Web Service
1.在你完成前一個步驟,並且編譯好COM伺服器myWServ1.dll,以滑鼠右鍵點選Project Manager(專案管理員),並從快捷選單中選擇Builder.
2.在Wizard Selection(精靈選擇)對話盒中,選擇Web Services Publisher,然後按下 Ok.
3.在VFP Web Services Publisher對話盒中,使用 Ellipsis 按鈕去找到 myWServ1, 然後選擇你想要從伺服器中註冊的類別.在這個你建立的COM伺服器中,myWServ1,你只有一個類別
showem
Note 點選 Advance(進階)按鈕以確定Web Services Publisher對話盒參照到正確的檔案是一個不錯的主意,特別是如果你建立了相似名稱的伺服器或者服務在不同資料夾的時候.如果正確的檔案沒有被顯示在下拉清單中,使用
Ellipsis 按鈕(…)去找到正確的檔案.
IntelliSense scripts 欄位下方顯示了你(和SOAP toolkit)將找到的名稱.在Advance Web Services Publisher對話盒中確認了資訊之後,點選Ok
譯註7.
4.點選Generate(產生)按鈕
在VFP產生Web Service檔案後, Web Services Publishing Results對話盒會出現,顯示包含COM伺服器,.wsdl檔案,ISAPI或ASP
listener和IntelliSense路徑名稱和編輯結果的清單.
到了這裡,你已經建立和發布你的Web Service.任何存取到你早先所建立的這個虛擬目錄,就能使用HTTP去存取你剛剛建立的Web Service.
要使用這個Web Service,你必須撰寫SOAP用戶端,一個listener(傾聽者)和瀏覽器的程式碼並且必須利用好幾個協定的層級和過程.
在VFP裡面,只要藉著在IntelliSense管理者中註冊Web Service就能簡化許多.

 

Registering a Web Service(註冊一個Web Service)

你可以在IntelliSense管理者中註冊Web Services(你所建立的或被其他人建立的).
當你做完,IntelliSense在你存取一個已註冊的Web Service時會提供了支援的程式碼.

去註冊一個Web Service
1.在 Tools(工具)選單,選擇IntelliSense Manager.
2.在IntelliSense Manager的Types頁籤,按下Web Services按鈕以註冊你新的Web Services(你的新類別也將會出現在Types清單中)
現在,當你加入一個強制型別的變數到你的VFP程式碼中時,IntelliSense將會提供你的Web Services如同一個非必要的選擇.
譯註8

 

Using a Web Service(使用一個Web Service)

使用Web Service就跟你想要一個應用程式的任何COM伺服器把功能公開出來給另一個應用程式或程式一樣.
舉例來說,你可以建立一個呼叫範例Web Service, myWServ1,的VFP程式.
在一個VFP程式中,宣告一個強制型別的變數,如同下列的程式碼一樣,藉著從IntelliSense下拉選單中可以參照到你的Web Service, myServ1.
LOCAL x AS myWServ1
VFP IntelliSense使用這個Web Service已註冊的資訊,去完成下列的程式碼:
x = CREATEOBJECT("MSSOAP.SoapClient")
x.MSSoapInit("http://TheVirtualDisk/web services/myWServ1.WSDL", ,
" myWServ1SoapPort")
然後,你可以像以前呼叫COM物件或類別的方法一樣去呼叫,就像下列的程式碼一樣:
? x.justshow()
譯註9
你可以加入相似的程式碼到VFP程式或應用程式中,去存取你自己的或其他Web Services.

 

更多資訊

要取得更多關於Web Services的資訊,請看VFP輔助說明中的 "Web Services Overview".
要取得更多關於COM伺服器的資訊,請看VFP輔助說明中的 "Creating Automation Servers".

Text for description meta tag:VFP使得從COM伺服器建立Web Services變的可能.

KeyWords(關鍵字):VFP Web Services, creating; VFP, creating Web Services;VFP, Web
Services.

 

譯註

譯註1:此句翻譯的不好,本來只有一句,但我切成兩句.
譯註2:此句也翻譯的不好.意思有到了.
譯註3:這邊的步驟,我都照做一次,並將原有的英文W2k上的字眼換成中文w2k上的字眼.
譯註4:因為我的VFP7是英文版的,所以我使用英文的字,不用中文的
譯註5:如果你前面有建立專案的話,就不需要再建立了,直接從步驟三開始吧.
譯註6:這句翻的非常不好,附上原文: To add the components necessary to turn your COM server into
a Web Service, you must compile the COM server as a Web Service.
譯註7:此處務必要自己玩過一遍,盡量多按一按,因為這邊寫的不是很清楚.
譯註8:如果你之前為虛擬目錄取名為 "Web Services" 時,會出現錯誤,因為他不允許名稱中間有空白!!
譯註9:這段程式碼讓我花了一些時間去測試,不過錯誤原因在於我自己在建立並發布Web Services的時候,選擇了以ASP的方式產生,後來改為ISAPI的方式產生就ok了.

VFP7-Behavior Changes since the Previous Version

本章翻譯自 foxhelp.chm Behavior Changes since the Previous Version.
很多可能翻譯的與原文不同,但是大意是一樣的.
從前一版到現在的行為改變
這個章節描述了這一版和前版VFP行為較為不同的地方,這些改變可能會影響到已經存在的程式碼.
._Screen 和 _VFP 這兩個物件的屬性已經經過調整,使得這兩個物件的屬性比較容易區分._VFP就是協調整個VFP應用程式的視窗(包含選單和狀態列)._Screen就是負責協調目前整個桌面區域(比如使用?指令時,會有輸出結果的視窗)
.為了效率,CreateObject()函數不再為一個動態已被建立的Name屬性的值加入連續號碼.這會應用到在prg檔案中的沒有明確定義Name屬性或基本類別的類別定義.(似乎影響不大)
.因為支援 IntelliSense 和 COM Server 型態(舉例來說 Local cName as string), 利用空白字元隔開的變數宣告(如 local x y z)將不再被允許.宣告變數的時候必須以 , 隔開(如 local x,y,z ).
.Session類別現在在為一個OLEPUBLIC的子類別產生Type Library的時候隱藏了固有的屬性,方法和事件. 另外,當利用session物件使用一個私有的data session時,SET TALK,SET EXCLUSIVE,SET SAFETY預設設定為 off.
.Version函數傳回的格式改變了. Build number現在移到最後面去了.如果你有使用AGETFILEVERSION函數的話,這也可能影響到.
.某些 Home([n]) 傳回的數值被改變了.這些變化只會影響到目前安裝的 Visual Studio. Samples和Graphics資料夾現在移到VFP的根目錄下.
.儲存檔案的新預設位置為了要和Windows 2000 Logo Guide相容的關係,已經被改變了.你可以從選項對話方盒(檔案位置頁籤)查看(或改變)位置. 譯註1.
.選單檔案(MNX)的格式已經被改變,以便能容納新的圖形支援.
.資料庫檔案(DBC)的格式在”DBC事件”啟動的時候將會被改變.
.命令列視窗現在會自動把內容保存起來.內容存放在 _command.prg 中.
.資源檔(如 FOXUSER )現在是開啟為分享模式,所以你可以執行好幾個VFP的個體,且使用同一個資源檔.在前一個版本,你可以在選項對話方盒中去設定.
.VFP執行時期函式庫(舉例,VFP7T.DLL)不在安裝在 Windows\system 目錄下.此外,其他語言的資源檔現在將和英文的產品一同安裝.譯註2.
.當屬性視窗設定為Dockable的時候,將會一直開啟著,即使目前的form/class designer(表單/類別設計)已經關閉.
.舊版FoxBASE+所支援的FOXGRAPH,FOXVIEW,FOXGEN,FOXCODE,CENTRAL和ASSIST已經不再被支援.
.Windows檔案總管在開啟已經的VFP檔案格式的時候,現在會喚起一個新的VFP個體來開啟檔案.此外,程式檔(PRG)和查詢檔(QRY)同時支援”Open”和”Run”選項.程式檔現在的預設動作為”Open”取代了前版本的”Run”.
.Class/Form Designer(類別/表單設計)畫面顯示的格子(如果有設定要顯示格線)現在以實際的像素來顯示,取代前一版本使用Fox2x的設定.譯註3.
.Grid欄位標頭現在允許顯示鍵盤快捷鍵.這僅僅只有顯示而已,所以開發者如果要處理快捷鍵的動作,必須要寫程式來處理.
.#includes 的搜尋路徑已經延展為PRGs(程式檔), SCXs(畫面檔)和VCXs(類別檔).
.使用SET EVENTTRACKING指令產生出來的事件追蹤歷史檔的格式現在包含了一個Timestamp的欄位.
._DBLCLICK系統變數不再為控制項控制incremental搜尋,如listbox. 現在這被一個新的系統變數_INCSEEK來處理.
.一個起始的應用程式(舉例,vfp7strt.app)不再和VFP發布出去.
.ASCAN()函數的 nStartPos 參數如果傳入為0的數值的話,將只產生一個錯誤. 如果傳入一個比陣列個數要多的數值,也將傳回0.
.在前版本,當呼叫Create Table … name LongTable Name 時,我們可以利用 _ 來取代空白.當在Table Designer(表單設計)時將不會發生.在這個版本,這個功能被保留起來了,以與Table Designe的行為相同.
從前版本至今具特色的改變
接下來這些前一版本VFP的特點已經被移除了:
.文法檢查不再包含在此產品中.
.安裝精靈不再包含在此產品中. 一個以Microsoft Installer為基礎的發布工具將被提供. 細節部分請參考 Destributing Applications 這章.
.Graph不再包含,因為他本來是跟著之前的安裝精靈一併發布的.
.執行 ActiveDoc 的選單項目不再被包含在此產品中,但是仍然有效並且可以利用Do來呼叫.
.好幾個MSDN的選單項目已經從輔助說明選單中移除.此產品的線上文件已經改變為一個獨立的輔助說明檔,他和MSDN Library一樣好.此外,”Microsoft on the Web”子選單已經被一個單獨的”Visual FoxPro on the Web”項目所取代.
.Calendar, Outline 和 FoxHWnd ActiveX控制項不再和此產品一起銷售.
.VFP ODBC 驅動程式不再和此產品一起銷售.你仍然可以從VFP網站來取得.但VFP OLEDB Provider是一個存取遠端VFP資料更好的解決方案.
譯註1:可是我並沒有發現有任何改變,我使用的環境是 Windows 2000+VFP7.我想他指的是不是 set default 呢??
譯註2:的確,現在多安裝了VFP7CHT.DLL,VFP7KOR.DLL等這些資源檔.
譯註3:簡單的說,單位已經改變了.