<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" 
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Thinking more...-Visual FoxPro</title>
<link>http://blog.roodo.com/thinkingmore/archives/cat_82808.html</link>
<description>Just thinking more...&amp;nbsp;訂閱 RSS













  google.load(&quot;jquery&quot;, &quot;1.2&quot;);
  google.setOnLoadCallback( function() {
    _uacct = &quot;UA-97150-7&quot;;
    urchinTracker();
    if( dp!=&#039;undefined&#039; ) {
      $(&quot;pre &gt; br&quot;).each( function() { $(this).replaceWith( &quot;\n&quot; ); } );
      $(&quot;textarea &gt; br&quot;).each( function() { $(this).replaceWith( &quot;\n&quot; ); } );
      dp.SyntaxHighlighter.ClipboardSwf = &#039;http://syntaxhighlighter.googlecode.com/svn/tags/1.5.1/Scripts/clipboard.swf&#039;;
      dp.SyntaxHighlighter.HighlightAll(&#039;code&#039;);
    }
  } );

</description>
<language>zh-tw</language>
<generator>Roodo Blog System</generator>
<copyright>All Rights Reserved</copyright>
<atom:link href="http://blog.roodo.com/thinkingmore/archives/cat_82808.xml" rel="self" type="application/rss+xml" />
<item>
	<title>VFP 與 Regular Expression</title>
	<description><![CDATA[
	VFP 本身並不支援 Regular Expression，幸好，有人非常熱心，以 C/C++ 有名的 Boost library 為基礎，製作了給 VFP 用的 Regular Expression Library。
License 基本上是遵循 boost library 的 LGPL license，所以你可以直接使用 binary code (也就是 .fll)在商業用途上。

為甚麼要 Regular Expression？因為他可以很方便地以簡單的語法表示出一段文字的規則。
以文章裡面的範例來看，你會發現他真的好用～
*!* 驗證 email 信箱格式
lcExpression = "^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$"
?RegExp("john@isp.com",lcExpression)
?RegExp("john@.isp.com",lcExpression)

*!* 驗證金額
lcExpression = "^(\$)?(([1-9]\d{0,2}(\,\d{3})*)|([1-9]\d*)|(0))(\.\d{2})?$"
?RegExp("$1,244,311.81",lcExpression) && Match
?RegExp("$1,24,4311.81",lcExpression) && No Match

*!* 驗證電話號碼
lcExpression = "^[2-9]\d{2}-\d{3}-\d{4}$"
?RegExp("507-562-0020",lcExpression) && Match
?RegExp("507-56-0020",lcExpression) && No Match


基本規則說明可以直接參考洪朝貴先生發佈的文章。

參考資料：A Regular Expressions library for Visual FoxProReg Exp - Visual FoxPro Wiki一輩子受用的 Regular Expressions -- 兼談另類的電腦學習態度Programming with GNU Regex LibraryJserv's blog: Regex Programming 資源Boost.Regex: Perl Regular Expression Syntax

	]]>
	</description>
	<content:encoded><![CDATA[
	VFP 本身並不支援 Regular Expression，幸好，<a href="http://www.sweetpotatosoftware.com/SPSBlog/PermaLink,guid,91241006-595a-487d-ac06-d0fc1fc71632.aspx" title="A Regular Expressions library for Visual FoxPro">有人非常熱心，以 C/C++ 有名的 Boost library 為基礎，製作了給 VFP 用的 Regular Expression Library。</a><br />
License 基本上是遵循 boost library 的 LGPL license，所以你可以直接使用 binary code (也就是 .fll)在商業用途上。<br />
<br />
為甚麼要 Regular Expression？因為他可以很方便地以簡單的語法表示出一段文字的規則。<br />
以<a href="http://www.sweetpotatosoftware.com/SPSBlog/PermaLink,guid,91241006-595a-487d-ac06-d0fc1fc71632.aspx" title="A Regular Expressions library for Visual FoxPro">文章</a>裡面的範例來看，你會發現他真的好用～<br />
<blockquote>*!* 驗證 email 信箱格式<br />
lcExpression = "^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$"<br />
?RegExp("john@isp.com",lcExpression)<br />
?RegExp("john@.isp.com",lcExpression)<br />
<br />
*!* 驗證金額<br />
lcExpression = "^(\$)?(([1-9]\d{0,2}(\,\d{3})*)|([1-9]\d*)|(0))(\.\d{2})?$"<br />
?RegExp("$1,244,311.81",lcExpression) && Match<br />
?RegExp("$1,24,4311.81",lcExpression) && No Match<br />
<br />
*!* 驗證電話號碼<br />
lcExpression = "^[2-9]\d{2}-\d{3}-\d{4}$"<br />
?RegExp("507-562-0020",lcExpression) && Match<br />
?RegExp("507-56-0020",lcExpression) && No Match<br />
</blockquote><br />
<br />
基本規則說明可以直接參考<a href="http://www.cyut.edu.tw/~ckhung/b/gnu/regexp.php" title="一輩子受用的 Regular Expressions -- 兼談另類的電腦學習態度">洪朝貴先生發佈的文章</a>。<br />
<br />
參考資料：<ul><li><a href="http://www.sweetpotatosoftware.com/SPSBlog/PermaLink,guid,91241006-595a-487d-ac06-d0fc1fc71632.aspx" title="A Regular Expressions library for Visual FoxPro">A Regular Expressions library for Visual FoxPro</a></li><li><a href="http://wikis.com/wc.dll?Wiki~RegExp" title="Reg Exp - Visual FoxPro Wiki">Reg Exp - Visual FoxPro Wiki</a></li><li><a href="http://www.cyut.edu.tw/~ckhung/b/gnu/regexp.php" title="一輩子受用的 Regular Expressions -- 兼談另類的電腦學習態度">一輩子受用的 Regular Expressions -- 兼談另類的電腦學習態度</a></li><li><a href="http://phi.sinica.edu.tw/aspac/reports/96/96004/" title="Programming with GNU Regex Library">Programming with GNU Regex Library</a></li><li><a href="http://blog.linux.org.tw/~jserv/archives/001476.html" title="Jserv's blog: Regex Programming 資源">Jserv's blog: Regex Programming 資源</a></li><li><a href="http://www.boost.org/libs/regex/doc/syntax_perl.html" title="Boost.Regex: Perl Regular Expression Syntax">Boost.Regex: Perl Regular Expression Syntax</a></li></ul><br />
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/2231406.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/2231406.html</guid>
	<category>Visual FoxPro</category>
	<pubDate>Wed, 04 Oct 2006 12:25:09 +0800</pubDate>
</item>
<item>
	<title>How to detect device arrival?</title>
	<description><![CDATA[
	幫朋友找資料找到的,順道貼上來
我沒試過
不過我想應該是可以運行吧~~

以 VFP 或 VB 來說,是利用 sysinfo 這個 Active X control .
主要是攔截 DeviceArrival 這個 event

詳細的範例可以參考DeviceArrival Event Example

雖然他是 VB 的範例,不過看起來應該是很好改成 VFP 才是...

Private Sub SysInfo1_DeviceArrival(ByVal DeviceType As Long, ByVal DeviceID As Long, ByVal DeviceName As String, ByVal DeviceData As Long)
   Debug.Print "DeviceArrival"
   Debug.Print "devicetype " & DeviceType
   Debug.Print "DeviceID " & GetDrive(DeviceType, DeviceID)
   Debug.Print "DeviceName " & DeviceName
   Debug.Print "DeviceData " & DeviceData
End Sub

Private Function GetDrive(devType As Long, devID As Long) As String
   Select Case devType
   Case 0 To 1 ' returns null
      GetDrive = devID
      Exit Function
   Case 3 To 4 ' returns null
      GetDrive = devID
      Exit Function
   Case 2 ' logical drive.
      ' Create an array for the possible drive numbers returned by 
      ' deviceID
      Dim drives(25) ' A-Z
      Dim dvNum As Long ' Possible bit values.
      Dim i As Integer
      dvNum = 1
      drives(0) = 1
      ' Populate with bit values.
      For i = 1 To 25
         dvNum = dvNum * 2
         drives(i) = dvNum
      Next i
      For i = 0 To 25
         If drives(i) = devID Then
            GetDrive = "Drive: " & Chr(i + 65)
            Exit Function
         End If
      Next I
   Case Else   ' Other unexpected returns
      Debug.Print devType, devID
   End Select
 End Function

	]]>
	</description>
	<content:encoded><![CDATA[
	幫朋友找資料找到的,順道貼上來<br />
我沒試過<br />
不過我想應該是可以運行吧~~<br />
<br />
以 VFP 或 VB 來說,是利用 sysinfo 這個 Active X control .<br />
主要是攔截 DeviceArrival 這個 event<br />
<br />
詳細的範例可以參考<a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinf98/html/vbevtdevicearrivaleventexamplex.asp" title="DeviceArrival Event Example">DeviceArrival Event Example</a><br />
<br />
雖然他是 VB 的範例,不過看起來應該是很好改成 VFP 才是...<br />
<br />
<blockquote>Private Sub SysInfo1_DeviceArrival(ByVal DeviceType As Long, ByVal DeviceID As Long, ByVal DeviceName As String, ByVal DeviceData As Long)<br />
   Debug.Print "DeviceArrival"<br />
   Debug.Print "devicetype " & DeviceType<br />
   Debug.Print "DeviceID " & GetDrive(DeviceType, DeviceID)<br />
   Debug.Print "DeviceName " & DeviceName<br />
   Debug.Print "DeviceData " & DeviceData<br />
End Sub<br />
<br />
Private Function GetDrive(devType As Long, devID As Long) As String<br />
   Select Case devType<br />
   Case 0 To 1 ' returns null<br />
      GetDrive = devID<br />
      Exit Function<br />
   Case 3 To 4 ' returns null<br />
      GetDrive = devID<br />
      Exit Function<br />
   Case 2 ' logical drive.<br />
      ' Create an array for the possible drive numbers returned by <br />
      ' deviceID<br />
      Dim drives(25) ' A-Z<br />
      Dim dvNum As Long ' Possible bit values.<br />
      Dim i As Integer<br />
      dvNum = 1<br />
      drives(0) = 1<br />
      ' Populate with bit values.<br />
      For i = 1 To 25<br />
         dvNum = dvNum * 2<br />
         drives(i) = dvNum<br />
      Next i<br />
      For i = 0 To 25<br />
         If drives(i) = devID Then<br />
            GetDrive = "Drive: " & Chr(i + 65)<br />
            Exit Function<br />
         End If<br />
      Next I<br />
   Case Else   ' Other unexpected returns<br />
      Debug.Print devType, devID<br />
   End Select<br />
 End Function<br />
</blockquote>		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/554278.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/554278.html</guid>
	<category>Visual FoxPro</category>
	<pubDate>Tue, 26 Jul 2005 21:25:01 +0800</pubDate>
</item>
<item>
	<title>Hook operation / Hooks and anchor</title>
	<description><![CDATA[
	以前逛到的兩個 Pattern, 比較特別的是:
1.Pattern 名字沒聽過,
2.用 VFP 實作的~~

挺有趣的,網址在這裡:
Hook operation
Hooks and anchor

Hook operation 講的是一種包裝別人物件的方法,一般來說,都是利用繼承的方法來將對方的 control 包進去,然後再去作客製化的動作;這個 pattern 則是繼承以後,另外利用了一個 hook manager 去專門處理這個客製化的動作.所以才稱作 Hook(掛勾).

Hooks and anchar 則是前面 hook 的延伸,據我個人的理解,這就有點像是 c# 可以把很多 event 或 delegate 串起來一樣~不過我不是很確定啦~~

另外,上面文章所屬的網站還蠻多文章的,大部分是講如何用VFP去搞 Design Pattern 的,各位可以去看看~~

與各位分享~
	]]>
	</description>
	<content:encoded><![CDATA[
	以前逛到的兩個 Pattern, 比較特別的是:<br />
1.Pattern 名字沒聽過,<br />
2.用 VFP 實作的~~<br />
<br />
挺有趣的,網址在這裡:<br />
<a href="http://www.stevenblack.com/PTN-Hook%20Operations.asp">Hook operation</a><br />
<a href="http://stevenblack.com/HooksAndAnchorsDesignPattern.ASP">Hooks and anchor</a><br />
<br />
Hook operation 講的是一種包裝別人物件的方法,一般來說,都是利用繼承的方法來將對方的 control 包進去,然後再去作客製化的動作;這個 pattern 則是繼承以後,另外利用了一個 hook manager 去專門處理這個客製化的動作.所以才稱作 Hook(掛勾).<br />
<br />
Hooks and anchar 則是前面 hook 的延伸,據我個人的理解,這就有點像是 c# 可以把很多 event 或 delegate 串起來一樣~不過我不是很確定啦~~<br />
<br />
另外,上面文章所屬的<a href="http://www.stevenblack.com/SBC%20Publications.asp">網站</a>還蠻多文章的,大部分是講如何用VFP去搞 Design Pattern 的,各位可以去看看~~<br />
<br />
與各位分享~		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/554006.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/554006.html</guid>
	<category>Visual FoxPro</category>
	<pubDate>Sat, 27 Mar 2004 20:31:06 +0800</pubDate>
</item>
<item>
	<title>Builder pattern on VFP</title>
	<description><![CDATA[
	給予適當的建構資料,就可以得到我們所需要的物件.

詳細的說明可以參考後面所附的參考資料,那些資料就已經寫的很好了,我想我沒必要再畫蛇添足.

最初製作 SQL Builder class 的時候是想說,我只要給予它一些欄位和條件,他就會自動幫我產生 SQL 敘述,而不必再去考量 SQL 敘述是否正確啊...等等的問題.剛好想到 Builder pattern 其實很適合,所以就實做看看.

目前這個 class 已經蠻齊全了,不過有些地方可以再加強.
   1. Join 部分
   2. 資料型態轉換,作的不好~應該可以再更精簡,更可靠.
   3. 傳入 select - sql 敘述,就能產生 delete-sql, update-sql.
   4. ...

參考資料:
http://www.dotspace.idv.tw/Patterns/Jdon_Builder.htm
http://140.109.17.201/Jyemii/patternscolumn/articles/DesignPatternPart(2).htm
Design Pattern 中譯本 - 葉秉哲譯

範例程式:

* 主程式
LOCAL lc_sql
LOCAL lo_builder
lo_builder=CREATEOBJECT("csqlbuilder")
lo_builder.ADDCOLUMN("u_id")
lo_builder.ADDCOLUMN("u_name")
lo_builder.setTable("users")
lo_builder.addWhere("", "u_id", "=", "drury")
lo_builder.addOrderby( "u_id" )
lo_builder.addGroupby( "u_id" )
? "====="
? "select sql::"
?? lo_builder.getSelectSQL()
lo_builder.reconf()
lo_builder.addPair("u_id", "ellery")
lo_builder.addPair("u_name", "ellery")
lo_builder.setTable("users")
lo_builder.addWhere("", "u_id", "=", "drury")
? "====="
? "insert sql::" + lo_builder.getInsertSQL()
? "update sql::" + lo_builder.getUpdateSQL()
? "delete sql::" + lo_builder.getDeleteSQL()

*
* Builder pattern 類別實作
* 此類別適用環境: VFP 8.0
* 類別定義開始
*
DEFINE CLASS CSQLBuilder AS CUSTOM
&nbsp;&nbsp;ADD OBJECT PROTECTED m_pairs AS COLLECTION
&nbsp;&nbsp;ADD OBJECT PROTECTED m_where AS COLLECTION
&nbsp;&nbsp;ADD OBJECT PROTECTED m_groupby AS COLLECTION
&nbsp;&nbsp;ADD OBJECT PROTECTED m_orderby AS COLLECTION
&nbsp;&nbsp;ADD OBJECT PROTECTED m_keys as collection 
&nbsp;&nbsp;m_table = ""		&& the table
&nbsp;&nbsp;m_targettype=""	&& cursor, table
&nbsp;&nbsp;m_target=""		&& name

&nbsp;&nbsp;* Init
&nbsp;&nbsp;PROCEDURE INIT
&nbsp;&nbsp;ENDPROC

&nbsp;&nbsp;* Destroy
&nbsp;&nbsp;PROCEDURE DESTROY
&nbsp;&nbsp;ENDPROC

&nbsp;&nbsp;* Reconfigure
&nbsp;&nbsp;PROCEDURE reconf
&nbsp;&nbsp;&nbsp;&nbsp;* MSDN said: pass -1 will clear all items
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_pairs.REMOVE( -1 )
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_where.REMOVE( -1 )
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_groupby.REMOVE( -1 )
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_orderby.REMOVE( -1 )
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_table = ""
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_targettype=""
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_target=""
&nbsp;&nbsp;ENDPROC

&nbsp;&nbsp;* addColumns
&nbsp;&nbsp;PROCEDURE ADDCOLUMN( c_field AS STRING )
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_pairs.ADD( .NULL., c_field )
&nbsp;&nbsp;ENDPROC

&nbsp;&nbsp;PROCEDURE addWhere( c_logical AS STRING, c_field AS STRING, c_operator AS STRING, o_value AS OBJECT )
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_where.ADD( o_value, c_logical + c_field + c_operator )
&nbsp;&nbsp;ENDPROC

&nbsp;&nbsp;PROCEDURE addPair( c_field AS STRING, o_value AS OBJECT )
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_pairs.ADD( o_value, c_field )
&nbsp;&nbsp;ENDPROC

&nbsp;&nbsp;PROCEDURE addGroupby( c_field AS STRING )
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_groupby.ADD( .NULL., c_field )
&nbsp;&nbsp;ENDPROC

&nbsp;&nbsp;PROCEDURE addOrderby( c_field AS STRING )
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_orderby.ADD( .NULL., c_field )
&nbsp;&nbsp;ENDPROC

&nbsp;&nbsp;PROCEDURE addKey( c_field as String )
&nbsp;&nbsp;&nbsp;&nbsp;this.m_keys.add( .null., c_field )
&nbsp;&nbsp;ENDPROC
	
&nbsp;&nbsp;PROCEDURE addJoin
&nbsp;&nbsp;&nbsp;&nbsp;* todo: this is the most hard part.
&nbsp;&nbsp;ENDPROC

&nbsp;&nbsp;PROCEDURE setTable( c_table AS STRING )
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_table=c_table
&nbsp;&nbsp;ENDPROC

&nbsp;&nbsp;PROCEDURE setTarget( c_type AS STRING, c_target AS STRING )
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_targettype=c_type
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_target=c_string
&nbsp;&nbsp;ENDPROC

&nbsp;&nbsp;PROCEDURE getInsertSQL
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL i
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL lc_sql
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL lc_fields, lc_values, lc_type

&nbsp;&nbsp;&nbsp;&nbsp;lc_fields=""
&nbsp;&nbsp;&nbsp;&nbsp;lc_values=""
&nbsp;&nbsp;&nbsp;&nbsp;lc_sql="insert into " + THIS.m_table + " "
&nbsp;&nbsp;&nbsp;&nbsp;FOR i=1 TO THIS.m_pairs.COUNT
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_fields=lc_fields+THIS.m_pairs.GETKEY(i)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IF( i+1 
	]]>
	</description>
	<content:encoded><![CDATA[
	給予適當的建構資料,就可以得到我們所需要的物件.<br />
<br />
詳細的說明可以參考後面所附的參考資料,那些資料就已經寫的很好了,我想我沒必要再畫蛇添足.<br />
<br />
最初製作 SQL Builder class 的時候是想說,我只要給予它一些欄位和條件,他就會自動幫我產生 SQL 敘述,而不必再去考量 SQL 敘述是否正確啊...等等的問題.剛好想到 Builder pattern 其實很適合,所以就實做看看.<br />
<br />
目前這個 class 已經蠻齊全了,不過有些地方可以再加強.<br />
   1. Join 部分<br />
   2. 資料型態轉換,作的不好~應該可以再更精簡,更可靠.<br />
   3. 傳入 select - sql 敘述,就能產生 delete-sql, update-sql.<br />
   4. ...<br />
<br />
參考資料:<br />
<a href="http://www.dotspace.idv.tw/Patterns/Jdon_Builder.htm">http://www.dotspace.idv.tw/Patterns/Jdon_Builder.htm</a><br />
<a href="http://140.109.17.201/Jyemii/patternscolumn/articles/DesignPatternPart(2).htm">http://140.109.17.201/Jyemii/patternscolumn/articles/DesignPatternPart(2).htm</a><br />
Design Pattern 中譯本 - 葉秉哲譯<br />
<br />
範例程式:<br />
<blockquote><br />
* 主程式<br />
LOCAL lc_sql<br />
LOCAL lo_builder<br />
lo_builder=CREATEOBJECT("csqlbuilder")<br />
lo_builder.ADDCOLUMN("u_id")<br />
lo_builder.ADDCOLUMN("u_name")<br />
lo_builder.setTable("users")<br />
lo_builder.addWhere("", "u_id", "=", "drury")<br />
lo_builder.addOrderby( "u_id" )<br />
lo_builder.addGroupby( "u_id" )<br />
? "====="<br />
? "select sql::"<br />
?? lo_builder.getSelectSQL()<br />
lo_builder.reconf()<br />
lo_builder.addPair("u_id", "ellery")<br />
lo_builder.addPair("u_name", "ellery")<br />
lo_builder.setTable("users")<br />
lo_builder.addWhere("", "u_id", "=", "drury")<br />
? "====="<br />
? "insert sql::" + lo_builder.getInsertSQL()<br />
? "update sql::" + lo_builder.getUpdateSQL()<br />
? "delete sql::" + lo_builder.getDeleteSQL()<br />
<br />
*<br />
* Builder pattern 類別實作<br />
* 此類別適用環境: VFP 8.0<br />
* 類別定義開始<br />
*<br />
DEFINE CLASS CSQLBuilder AS CUSTOM<br />
&nbsp;&nbsp;ADD OBJECT PROTECTED m_pairs AS COLLECTION<br />
&nbsp;&nbsp;ADD OBJECT PROTECTED m_where AS COLLECTION<br />
&nbsp;&nbsp;ADD OBJECT PROTECTED m_groupby AS COLLECTION<br />
&nbsp;&nbsp;ADD OBJECT PROTECTED m_orderby AS COLLECTION<br />
&nbsp;&nbsp;ADD OBJECT PROTECTED m_keys as collection <br />
&nbsp;&nbsp;m_table = ""		&& the table<br />
&nbsp;&nbsp;m_targettype=""	&& cursor, table<br />
&nbsp;&nbsp;m_target=""		&& name<br />
<br />
&nbsp;&nbsp;* Init<br />
&nbsp;&nbsp;PROCEDURE INIT<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;* Destroy<br />
&nbsp;&nbsp;PROCEDURE DESTROY<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;* Reconfigure<br />
&nbsp;&nbsp;PROCEDURE reconf<br />
&nbsp;&nbsp;&nbsp;&nbsp;* MSDN said: pass -1 will clear all items<br />
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_pairs.REMOVE( -1 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_where.REMOVE( -1 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_groupby.REMOVE( -1 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_orderby.REMOVE( -1 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_table = ""<br />
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_targettype=""<br />
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_target=""<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;* addColumns<br />
&nbsp;&nbsp;PROCEDURE ADDCOLUMN( c_field AS STRING )<br />
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_pairs.ADD( .NULL., c_field )<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;PROCEDURE addWhere( c_logical AS STRING, c_field AS STRING, c_operator AS STRING, o_value AS OBJECT )<br />
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_where.ADD( o_value, c_logical + c_field + c_operator )<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;PROCEDURE addPair( c_field AS STRING, o_value AS OBJECT )<br />
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_pairs.ADD( o_value, c_field )<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;PROCEDURE addGroupby( c_field AS STRING )<br />
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_groupby.ADD( .NULL., c_field )<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;PROCEDURE addOrderby( c_field AS STRING )<br />
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_orderby.ADD( .NULL., c_field )<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;PROCEDURE addKey( c_field as String )<br />
&nbsp;&nbsp;&nbsp;&nbsp;this.m_keys.add( .null., c_field )<br />
&nbsp;&nbsp;ENDPROC<br />
	<br />
&nbsp;&nbsp;PROCEDURE addJoin<br />
&nbsp;&nbsp;&nbsp;&nbsp;* todo: this is the most hard part.<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;PROCEDURE setTable( c_table AS STRING )<br />
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_table=c_table<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;PROCEDURE setTarget( c_type AS STRING, c_target AS STRING )<br />
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_targettype=c_type<br />
&nbsp;&nbsp;&nbsp;&nbsp;THIS.m_target=c_string<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;PROCEDURE getInsertSQL<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL i<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL lc_sql<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL lc_fields, lc_values, lc_type<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_fields=""<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_values=""<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_sql="insert into " + THIS.m_table + " "<br />
&nbsp;&nbsp;&nbsp;&nbsp;FOR i=1 TO THIS.m_pairs.COUNT<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_fields=lc_fields+THIS.m_pairs.GETKEY(i)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IF( i+1 <= THIS.m_pairs.COUNT )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_fields=lc_fields+","<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_type=VARTYPE( THIS.m_pairs.ITEM(i) )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DO CASE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CASE lc_type="C"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_values=lc_values+ "'" + THIS.m_pairs.ITEM(i)+ "'"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CASE INLIST( lc_type, "N", "Y" )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_values=lc_values + ALLTRIM( STR(THIS.m_pairs.ITEM(i) ) )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CASE lc_type="D"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_values=lc_values + "{^" + DTOC( THIS.m_pairs.ITEM(i) ) + "}"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CASE lc_type="L"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_values=lc_values + IIF( THIS.m_pairs.ITEM(i), ".T.", ".F." )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CASE lc_type="X"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_values=lc_values + ".null."<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDCASE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IF( i+1 <= THIS.m_pairs.COUNT )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_values=lc_values+","<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br />
&nbsp;&nbsp;&nbsp;&nbsp;NEXT<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql + "(" + lc_fields + ") values (" + lc_values + ")"<br />
&nbsp;&nbsp;&nbsp;&nbsp;RETURN lc_sql<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;PROTECTED PROCEDURE getWhereSQL<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL lc_sql<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;IF( THIS.m_where.COUNT >=1 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=" where "<br />
&nbsp;&nbsp;&nbsp;&nbsp;ELSE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=""<br />
&nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br />
&nbsp;&nbsp;&nbsp;&nbsp;FOR i=1 TO THIS.m_where.COUNT<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_type=VARTYPE( THIS.m_where.ITEM(i) )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DO CASE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CASE lc_type="C"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_value= "'" + THIS.m_where.ITEM(i)+ "'"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CASE INLIST( lc_type, "N", "Y" )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_value= ALLTRIM( STR(THIS.m_where.ITEM(i) ) )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CASE lc_type="D"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_value="{" + DTOC( THIS.m_where.ITEM(i) ) + "}"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CASE lc_type="L"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_value=IIF( THIS.m_where.ITEM(i), ".T.", ".F." )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CASE lc_type="X"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_value=".null."<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDCASE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql + THIS.m_where.GETKEY(i) + lc_value<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IF( i+1 <= THIS.m_where.COUNT )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql+","<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br />
&nbsp;&nbsp;&nbsp;&nbsp;NEXT<br />
&nbsp;&nbsp;&nbsp;&nbsp;RETURN lc_sql<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;PROCEDURE getOnlyKey( o_collection AS COLLECTION )<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL i<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL lc_sql<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=""<br />
&nbsp;&nbsp;&nbsp;&nbsp;FOR i=1 TO o_collection.COUNT<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql+o_collection.GETKEY(i)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IF( i+1 <= o_collection.COUNT )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql+","<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br />
&nbsp;&nbsp;&nbsp;&nbsp;NEXT<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;RETURN lc_sql<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;PROCEDURE getOrderBySQL<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL lc_sql<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=THIS.getOnlyKey( THIS.m_orderby )<br />
&nbsp;&nbsp;&nbsp;&nbsp;IF( EMPTY(lc_sql) )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RETURN lc_sql<br />
&nbsp;&nbsp;&nbsp;&nbsp;ELSE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RETURN " order by " + lc_sql<br />
&nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;PROCEDURE getGroupbySQL<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL lc_sql<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=THIS.getOnlyKey( THIS.m_groupby )<br />
&nbsp;&nbsp;&nbsp;&nbsp;IF( EMPTY(lc_sql) )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RETURN lc_sql<br />
&nbsp;&nbsp;&nbsp;&nbsp;ELSE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RETURN " group by " + lc_sql<br />
&nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;PROCEDURE getSelectSQL<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL i<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL lc_sql, lc_where, lc_orderby, lc_groupby<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_sql="select "<br />
&nbsp;&nbsp;&nbsp;&nbsp;FOR i=1 TO THIS.m_pairs.COUNT<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql+THIS.m_pairs.GETKEY(i)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IF( i+1 <= THIS.m_pairs.COUNT )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql+","<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br />
&nbsp;&nbsp;&nbsp;&nbsp;NEXT<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql + " from " + THIS.m_table<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_where=THIS.getWhereSQL()<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql + lc_where<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_orderby=THIS.getOrderBySQL()<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql+lc_orderby<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_groupby=THIS.getGroupbySQL()<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql+lc_groupby<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;RETURN lc_sql<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;PROCEDURE getDeleteSQL<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL lc_sql<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL i<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL lc_value, lc_type, lc_where<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_sql="delete from " + THIS.m_table<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_where=THIS.getWhereSQL()<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql+lc_where<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;RETURN lc_sql<br />
&nbsp;&nbsp;ENDPROC<br />
<br />
&nbsp;&nbsp;PROCEDURE getUpdateSQL<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL i<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL lc_sql<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL lc_fields, lc_values, lc_type, lc_where<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_fields=""<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_values=""<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_sql="update " + THIS.m_table + " set "<br />
&nbsp;&nbsp;&nbsp;&nbsp;FOR i=1 TO THIS.m_pairs.COUNT<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql+THIS.m_pairs.GETKEY(i)+"="<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_type=VARTYPE( THIS.m_pairs.ITEM(i) )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DO CASE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CASE lc_type="C"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql + "'" + THIS.m_pairs.ITEM(i) + "'"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CASE INLIST( lc_type, "N", "Y" )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql + ALLTRIM( STR(THIS.m_pairs.ITEM(i) ) )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CASE lc_type="D"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql + "{^" + DTOC( THIS.m_pairs.ITEM(i) ) + "}"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CASE lc_type="L"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_values=lc_sql + IIF( THIS.m_pairs.ITEM(i), ".T.", ".F." )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CASE lc_type="X"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_values=lc_sql + ".null."<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDCASE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IF( i+1 <= THIS.m_pairs.COUNT )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql+","<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br />
&nbsp;&nbsp;&nbsp;&nbsp;NEXT<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;* handle where clause.<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_where=THIS.getWhereSQL()<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_sql=lc_sql+lc_where<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;RETURN lc_sql<br />
&nbsp;&nbsp;ENDPROC<br />
	<br />
&nbsp;&nbsp;PROCEDURE getKeyFieldList()<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL i<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL lc_fields<br />
&nbsp;&nbsp;&nbsp;&nbsp;IF( THIS.m_keys.COUNT >=1 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_fields=""<br />
&nbsp;&nbsp;&nbsp;&nbsp;ELSE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RETURN ""<br />
&nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br />
&nbsp;&nbsp;&nbsp;&nbsp;FOR i=1 TO THIS.m_keys.COUNT<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_fields=lc_fields+this.m_keys.getKey(i)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IF( i+1 <= THIS.m_keys.COUNT )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_fields=lc_fields+","<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br />
&nbsp;&nbsp;&nbsp;&nbsp;NEXT<br />
&nbsp;&nbsp;&nbsp;&nbsp;RETURN lc_fields<br />
&nbsp;&nbsp;ENDPROC<br />
	<br />
&nbsp;&nbsp;PROCEDURE getTable()<br />
&nbsp;&nbsp;&nbsp;&nbsp;RETURN THIS.m_table<br />
&nbsp;&nbsp;ENDPROC<br />
	<br />
&nbsp;&nbsp;PROCEDURE getUpdatableFieldList()<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL i, lc_fields<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_fields=""<br />
&nbsp;&nbsp;&nbsp;&nbsp;FOR i=1 TO THIS.m_pairs.COUNT<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_fields=lc_fields+THIS.m_pairs.GETKEY(i)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IF( i+1 <= THIS.m_pairs.COUNT )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_fields=lc_fields+","<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br />
&nbsp;&nbsp;&nbsp;&nbsp;NEXT<br />
&nbsp;&nbsp;&nbsp;&nbsp;RETURN lc_fields<br />
&nbsp;&nbsp;ENDPROC<br />
	<br />
&nbsp;&nbsp;PROCEDURE getUpdateNameList()<br />
&nbsp;&nbsp;&nbsp;&nbsp;LOCAL i, lc_fields<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_fields=""<br />
&nbsp;&nbsp;&nbsp;&nbsp;FOR i=1 TO THIS.m_pairs.COUNT<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_fields=lc_fields+THIS.m_pairs.GETKEY(i)+" "<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_fields=lc_fields+this.m_table+"."+this.m_pairs.getkey(i)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IF( i+1 <= THIS.m_pairs.COUNT )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lc_fields=lc_fields+","<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br />
&nbsp;&nbsp;&nbsp;&nbsp;NEXT<br />
&nbsp;&nbsp;&nbsp;&nbsp;RETURN lc_fields<br />
&nbsp;&nbsp;ENDPROC<br />
ENDDEFINE<br />
* 類別定義結束<br />
</blockquote>		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/554004.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/554004.html</guid>
	<category>Visual FoxPro</category>
	<pubDate>Tue, 16 Mar 2004 12:46:36 +0800</pubDate>
</item>
<item>
	<title>FontCharSet in VFP8</title>
	<description><![CDATA[
	剛剛用 VFP8 在做 form 的時候
發現 TextBox 的 property 裡面居然有個以前沒看過的傢伙: FontCharSet
翻了一下 Help, 原來他可以指定 font 的 charset (字元集)
換言之,只要變動 Fontname, fontcharset, 與內容(value or caption), 應該就可以達到 localization (l10n)的目的了.
	]]>
	</description>
	<content:encoded><![CDATA[
	剛剛用 VFP8 在做 form 的時候<br />
發現 TextBox 的 property 裡面居然有個以前沒看過的傢伙: FontCharSet<br />
翻了一下 Help, 原來他可以指定 font 的 charset (字元集)<br />
換言之,只要變動 Fontname, fontcharset, 與內容(value or caption), 應該就可以達到 localization (l10n)的目的了.		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553981.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553981.html</guid>
	<category>Visual FoxPro</category>
	<pubDate>Sun, 01 Feb 2004 20:59:08 +0800</pubDate>
</item>
<item>
	<title>base64 encode/decode</title>
	<description><![CDATA[
	忘記從哪兒節錄下來的了,在這裡向作者說聲抱歉,因為那時沒有記下出處.
如果有侵犯版權的話,請來信告訴我,我會拿掉.
兩個 function 是作 base64 encode/decode 的.

不過, VFP7 以後,微軟已經擴充了 STRCONV() 的功能,讓他也能作 base64 encode/decode.

用法如下:
? STRCONV("Hallo",13)  encodes to Base64
? STRCONV("SGFsbG8=",14) decodes from Base64

=====

FUNCTION Base64Encode(lcInput,loXML)
LOCAL loNode

IF VARTYPE(loXML) # "O"
   loXML = CREATEOBJECT("MSXML2.DOMDocument")
   loXML.Async = .F.
ENDIF

loXML.loadXML("&lt;node&gt;&lt;/node&gt;")
loNode = loXML.DocumentElement

loNode.dataType = "bin.base64"
loNode.nodeTypedValue =  CREATEBINARY(lcInput)

RETURN loNode.Text


FUNCTION Base64Decode(lcInput,loXML)
LOCAL lcDocument

IF VARTYPE(loXML) # "O"
   loXML = CREATEOBJECT("MSXML2.DomDocument")
   loXML.Async = .F.
ENDIF

lcDocument = [&lt;node xmlns:dt="urn:schemas-microsoft-com:datatypes"
dt:dt="bin.base64"&gt;] + lcInput + [&lt;/node&gt;]

loXML.loadXML(lcDocument)

RETURN  loXML.DocumentElement.nodeTypedValue
	]]>
	</description>
	<content:encoded><![CDATA[
	忘記從哪兒節錄下來的了,在這裡向作者說聲抱歉,因為那時沒有記下出處.<br />
如果有侵犯版權的話,請來信告訴我,我會拿掉.<br />
兩個 function 是作 base64 encode/decode 的.<br />
<br />
不過, VFP7 以後,微軟已經擴充了 STRCONV() 的功能,讓他也能作 base64 encode/decode.<br />
<br />
用法如下:<br />
? STRCONV("Hallo",13)  encodes to Base64<br />
? STRCONV("SGFsbG8=",14) decodes from Base64<br />
<br />
=====<br />
<br />
FUNCTION Base64Encode(lcInput,loXML)<br />
LOCAL loNode<br />
<br />
IF VARTYPE(loXML) # "O"<br />
   loXML = CREATEOBJECT("MSXML2.DOMDocument")<br />
   loXML.Async = .F.<br />
ENDIF<br />
<br />
loXML.loadXML("&lt;node&gt;&lt;/node&gt;")<br />
loNode = loXML.DocumentElement<br />
<br />
loNode.dataType = "bin.base64"<br />
loNode.nodeTypedValue =  CREATEBINARY(lcInput)<br />
<br />
RETURN loNode.Text<br />
<br />
<br />
FUNCTION Base64Decode(lcInput,loXML)<br />
LOCAL lcDocument<br />
<br />
IF VARTYPE(loXML) # "O"<br />
   loXML = CREATEOBJECT("MSXML2.DomDocument")<br />
   loXML.Async = .F.<br />
ENDIF<br />
<br />
lcDocument = [&lt;node xmlns:dt="urn:schemas-microsoft-com:datatypes"<br />
dt:dt="bin.base64"&gt;] + lcInput + [&lt;/node&gt;]<br />
<br />
loXML.loadXML(lcDocument)<br />
<br />
RETURN  loXML.DocumentElement.nodeTypedValue		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553966.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553966.html</guid>
	<category>Visual FoxPro</category>
	<pubDate>Mon, 19 Jan 2004 21:25:33 +0800</pubDate>
</item>
<item>
	<title>以 VFP 實作 Singleton Pattern</title>
	<description><![CDATA[
	Singleton,確保類別永遠只有一份實體

Singleton pattern, 簡言之,是一個確保類別永遠只有一份實體的範式(Pattern).
在什麼情況下,我們會用到這個 Pattern 呢??
比如,一台電腦裡在同一時間只能有一個視窗管理員在運行.

一般性的做法,是讓類別自行管理這個唯一個物件實體,讓他確保絕對無法生出第二個物件個體.

那麼在 VFP 裡面要如何實現呢??
讓我們來試試看,首先先定義出 CSingleton 這個Class
*
* Class Singleton
*
DEFINE CLASS CSingleton as Custom 
&nbsp;&nbsp;HIDDEN m_singleton
&nbsp;&nbsp;m_singleton=.NULL.
&nbsp;&nbsp;PROCEDURE getInstance
&nbsp;&nbsp;&nbsp;&nbsp;IF m_singleton==.NULL.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_singleton=CREATEOBJECT("CSingleton")
&nbsp;&nbsp;&nbsp;&nbsp;ENDIF
&nbsp;&nbsp;&nbsp;&nbsp;RETURN m_singleton
&nbsp;&nbsp;ENDPROC
&nbsp;&nbsp;PROCEDURE getClassName
&nbsp;&nbsp;&nbsp;&nbsp;return "I am Singleton Class"
&nbsp;&nbsp;ENDPROC
ENDDEFINE
我們將 m_singleton 隱藏起來,讓外界無法直接存取,並且提供 getInstance method,讓外界可以透過此 method 取得 m_singleton 這個 instance.
所以當我們需要這個類別的實體時,就可以這麼寫:
lo_object=CSingleton::getInstance()

嘿,等等,別的語言是可以這麼寫,VFP 允許我們這樣用嗎??
此外,我們也沒有辦法隱喻地將 m_instance 放到 heap,像別的語言可以用 static 表明 m_instance 要放到 heap 中,確保只會有一份實體.
VFP 必須要先為 CSingleton 類別產生實體,才能呼叫 getInstance()
也就是要先這樣子
local lo_class, lo_object
lo_class=createobject("CSingleton")
lo_object=lo_class.getInstance()
才能讓 lo_object 取得實體.


那這不就違反我們的本意了嗎??
當使用者呼叫了多次 createobject("CSingleton"), 等於是創建了好幾次 m_singleton,我們就無法讓 m_singleton 是系統中唯一的一個實體了.

那麼還有別的方法嗎??
嗯,用全域變數如何??
我們可以用全域變數搭配一個Function來使用.
所以就可以這麼寫
public m_instance
....
* 主程式
m_instance=.null.
...
* Function
function getInstance
&nbsp;&nbsp;if m_instance==.null.
&nbsp;&nbsp;&nbsp;&nbsp;m_instance=createobject("CSingleton")
&nbsp;&nbsp;endif
&nbsp;&nbsp;return m_instance
endfunc
...
* 要使用的時候
lo_object=getInstance()
? lo_object.getClassName()
...
這樣子總算是解決問題了,只要維持一個良好的撰寫習慣
就可以保證CSingleton的實體是唯一.
可是萬一後繼者不明白,直接去存取了全域變數 m_instance 的話,該怎麼辦呢??

嗯~~再換個方向來想
VFP 不是有個函數叫做 getobject() 嗎??
如果我們將類別轉為 OLEPUBLIC 之後,再使用 getobject() 去取得 instance,就可以取得唯一的實體來使用了.
這也不失為一個不錯的解法.

綜觀上面推論,或許還有其他的方法,是我沒有想到的.(我有想過用 fopen() 或 flock()...等等的)
但是就目前看來,在 VFP 裡面對 Singleton 並沒有一個完美的解法.
只有期待 VFP 未來能加入新的語言特性,讓我們能更靈活的運用了.

附錄: C++ 的解法
class CSingleton {
private:
&nbsp;&nbsp;static CSingleton* m_instance;
protected:	//保護起來,不讓 constructor 直接被叫用.
&nbsp;&nbsp;Singleton();
&nbsp;&nbsp;Singleton(const Singleton&amp;);
&nbsp;&nbsp;Singleton& operator= (const Singleton&amp;);
public:
&nbsp;&nbsp;CSingleton* getInstance(void) {
&nbsp;&nbsp;&nbsp;&nbsp;if( m_instance==NULL )
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_instance=new CSingleton();
&nbsp;&nbsp;&nbsp;&nbsp;return m_instance;
&nbsp;&nbsp;}
&nbsp;&nbsp;char* getClassName(void) {
&nbsp;&nbsp;&nbsp;&nbsp;return "Hello! Singleton Pattern!!\n";
&nbsp;&nbsp;}
}
CSingleton* CSingleton::m_instance=NULL;	//因為宣告為 static,所以可以這樣給值.
int main( int argc, char* argv[] ) {
&nbsp;&nbsp;CSingleton* obj=CSingleton::getInstance;
&nbsp;&nbsp;printf( "%s", obj->getClassName());
}
	]]>
	</description>
	<content:encoded><![CDATA[
	Singleton,確保類別永遠只有一份實體<br />
<br />
Singleton pattern, 簡言之,是一個確保類別永遠只有一份實體的範式(Pattern).<br />
在什麼情況下,我們會用到這個 Pattern 呢??<br />
比如,一台電腦裡在同一時間只能有一個視窗管理員在運行.<br />
<br />
一般性的做法,是讓類別自行管理這個唯一個物件實體,讓他確保絕對無法生出第二個物件個體.<br />
<br />
那麼在 VFP 裡面要如何實現呢??<br />
讓我們來試試看,首先先定義出 CSingleton 這個Class<br />
*<br />
* Class Singleton<br />
*<br />
DEFINE CLASS CSingleton as Custom <br />
&nbsp;&nbsp;HIDDEN m_singleton<br />
&nbsp;&nbsp;m_singleton=.NULL.<br />
&nbsp;&nbsp;PROCEDURE getInstance<br />
&nbsp;&nbsp;&nbsp;&nbsp;IF m_singleton==.NULL.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_singleton=CREATEOBJECT("CSingleton")<br />
&nbsp;&nbsp;&nbsp;&nbsp;ENDIF<br />
&nbsp;&nbsp;&nbsp;&nbsp;RETURN m_singleton<br />
&nbsp;&nbsp;ENDPROC<br />
&nbsp;&nbsp;PROCEDURE getClassName<br />
&nbsp;&nbsp;&nbsp;&nbsp;return "I am Singleton Class"<br />
&nbsp;&nbsp;ENDPROC<br />
ENDDEFINE<br />
我們將 m_singleton 隱藏起來,讓外界無法直接存取,並且提供 getInstance method,讓外界可以透過此 method 取得 m_singleton 這個 instance.<br />
所以當我們需要這個類別的實體時,就可以這麼寫:<br />
lo_object=CSingleton::getInstance()<br />
<br />
嘿,等等,別的語言是可以這麼寫,VFP 允許我們這樣用嗎??<br />
此外,我們也沒有辦法隱喻地將 m_instance 放到 heap,像別的語言可以用 static 表明 m_instance 要放到 heap 中,確保只會有一份實體.<br />
VFP 必須要先為 CSingleton 類別產生實體,才能呼叫 getInstance()<br />
也就是要先這樣子<br />
local lo_class, lo_object<br />
lo_class=createobject("CSingleton")<br />
lo_object=lo_class.getInstance()<br />
才能讓 lo_object 取得實體.<br />
<br />
<br />
那這不就違反我們的本意了嗎??<br />
當使用者呼叫了多次 createobject("CSingleton"), 等於是創建了好幾次 m_singleton,我們就無法讓 m_singleton 是系統中唯一的一個實體了.<br />
<br />
那麼還有別的方法嗎??<br />
嗯,用全域變數如何??<br />
我們可以用全域變數搭配一個Function來使用.<br />
所以就可以這麼寫<br />
public m_instance<br />
....<br />
* 主程式<br />
m_instance=.null.<br />
...<br />
* Function<br />
function getInstance<br />
&nbsp;&nbsp;if m_instance==.null.<br />
&nbsp;&nbsp;&nbsp;&nbsp;m_instance=createobject("CSingleton")<br />
&nbsp;&nbsp;endif<br />
&nbsp;&nbsp;return m_instance<br />
endfunc<br />
...<br />
* 要使用的時候<br />
lo_object=getInstance()<br />
? lo_object.getClassName()<br />
...<br />
這樣子總算是解決問題了,只要維持一個良好的撰寫習慣<br />
就可以保證CSingleton的實體是唯一.<br />
可是萬一後繼者不明白,直接去存取了全域變數 m_instance 的話,該怎麼辦呢??<br />
<br />
嗯~~再換個方向來想<br />
VFP 不是有個函數叫做 getobject() 嗎??<br />
如果我們將類別轉為 OLEPUBLIC 之後,再使用 getobject() 去取得 instance,就可以取得唯一的實體來使用了.<br />
這也不失為一個不錯的解法.<br />
<br />
綜觀上面推論,或許還有其他的方法,是我沒有想到的.(我有想過用 fopen() 或 flock()...等等的)<br />
但是就目前看來,在 VFP 裡面對 Singleton 並沒有一個完美的解法.<br />
只有期待 VFP 未來能加入新的語言特性,讓我們能更靈活的運用了.<br />
<br />
附錄: C++ 的解法<br />
class CSingleton {<br />
private:<br />
&nbsp;&nbsp;static CSingleton* m_instance;<br />
protected:	//保護起來,不讓 constructor 直接被叫用.<br />
&nbsp;&nbsp;Singleton();<br />
&nbsp;&nbsp;Singleton(const Singleton&amp;);<br />
&nbsp;&nbsp;Singleton& operator= (const Singleton&amp;);<br />
public:<br />
&nbsp;&nbsp;CSingleton* getInstance(void) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;if( m_instance==NULL )<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_instance=new CSingleton();<br />
&nbsp;&nbsp;&nbsp;&nbsp;return m_instance;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;char* getClassName(void) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;return "Hello! Singleton Pattern!!\n";<br />
&nbsp;&nbsp;}<br />
}<br />
CSingleton* CSingleton::m_instance=NULL;	//因為宣告為 static,所以可以這樣給值.<br />
int main( int argc, char* argv[] ) {<br />
&nbsp;&nbsp;CSingleton* obj=CSingleton::getInstance;<br />
&nbsp;&nbsp;printf( "%s", obj->getClassName());<br />
}		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553913.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553913.html</guid>
	<category>Visual FoxPro</category>
	<pubDate>Tue, 04 Nov 2003 22:59:00 +0800</pubDate>
</item>
<item>
	<title>Design Pattern-Dispatcher</title>
	<description><![CDATA[
	看完 RUN!PC 六月號由李維先生所寫的"由軟體品質檢驗談Design Pattern的應用"一文,我覺得用 VFP 就可以很簡單的作到,所以就以 VFP 實作.

該文以計算牌照稅為引子
計算牌照稅的時候,會需要依照汽缸的cc數以及私人/營業用車來判定收費的標準,照一般的寫法,很自然就會用到很多 If...Then...Else 或 do case...endcase, 可是這樣子程式碼就會變得很長很長,而難以維護.
像是這樣:

If cc < 500
&nbsp;&nbsp;money=900
else
&nbsp;&nbsp;if cc < 600 
&nbsp;&nbsp;&nbsp;&nbsp;money=1200
&nbsp;&nbsp;&nbsp;&nbsp;&amp;&amp;..... 略
&nbsp;&nbsp;endif
endif

用 do case 的話則是這樣
do case
&nbsp;&nbsp;case between( 0, 500 )
&nbsp;&nbsp;&nbsp;&nbsp;money=900
&nbsp;&nbsp;case between( 501, 599 )
&nbsp;&nbsp;&nbsp;&nbsp;money=1200
&nbsp;&nbsp;&amp;&amp; ... 略
endcase


李先生以一個 Dispatcher pattern 解決此一問題.
在 VFP 裡面可以直接結合資料庫作更好的解法

*
* ITax.prg
*
DEFINE CLASS ITax as custom
&nbsp;&nbsp;PROCEDURE Init()
&nbsp;&nbsp;&nbsp;&nbsp;create cursor crTax( cc_lo I, cc_hi I, PrivateTax Y,  BusinesTax Y )
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values (    0,  500, $ 1620, $  900 )
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values (  501,  599, $ 2160, $ 1200 )
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values (  600, 1199, $ 4320, $ 2160 )
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values ( 1200, 1799, $ 7120, $ 3060 )
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values ( 1800, 2399, $11230, $ 6480 )
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values ( 2400, 2999, $15210, $ 9900 )
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values ( 3000, 4199, $28220, $16380 )
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values ( 4200, 5399, $46170, $24300 )
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values ( 5400, 6599, $69690, $33660 )
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values ( 6600, 7799, $117000, $44460 )
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values ( 7800, 9999999, $117000, $44460 )
&nbsp;&nbsp;ENDPROC
&nbsp;&nbsp;PROCEDURE Destroy()
&nbsp;&nbsp;&nbsp;&nbsp;use in crTax
&nbsp;&nbsp;ENDPROC
&nbsp;&nbsp;PROCEDURE GetTax( cc, theKind )
&nbsp;&nbsp;&nbsp;&nbsp;local ly_result
&nbsp;&nbsp;&nbsp;&nbsp;local lc_oldalias
&nbsp;&nbsp;&nbsp;&nbsp;lc_oldalias=alias()

&nbsp;&nbsp;&nbsp;&nbsp;select("crTax")
&nbsp;&nbsp;&nbsp;&nbsp;go top
&nbsp;&nbsp;&nbsp;&nbsp;locate for between( cc, crTax.cc_lo, crTax.cc_hi )
&nbsp;&nbsp;&nbsp;&nbsp;if found()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ly_result=crTax.&theKind
&nbsp;&nbsp;&nbsp;&nbsp;else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ly_result=0
&nbsp;&nbsp;&nbsp;&nbsp;endif

&nbsp;&nbsp;&nbsp;&nbsp;select( lc_oldalias )
&nbsp;&nbsp;&nbsp;&nbsp;return ly_result
&nbsp;&nbsp;ENDPROC
&nbsp;&nbsp;PROCEDURE GetPrivateTax( cc )
&nbsp;&nbsp;&nbsp;&nbsp;return this.GetTax( cc, "PrivateTax" )
&nbsp;&nbsp;ENDPROC
&nbsp;&nbsp;PROCEDURE GetBusinessTax( cc )
&nbsp;&nbsp;&nbsp;&nbsp;return this.GetTax( cc, "BusinesTax" )
&nbsp;&nbsp;ENDPROC
ENDDEFINE

*
* test_itax.prg
*
LOCAL lo_obj
set procedure to itax.prg
lo_obj=createobject("ITax")
? lo_obj.GetPrivateTax( 1000 )
? lo_obj.GetBusinessTax( 2000 )

瞧,這樣不是簡單多了嗎??
在這裡拋磚引玉一下,希望大家如果有更好的解法
也 post 上來,讓大家觀摩一下
^_^
	]]>
	</description>
	<content:encoded><![CDATA[
	看完 RUN!PC 六月號由李維先生所寫的"由軟體品質檢驗談Design Pattern的應用"一文,我覺得用 VFP 就可以很簡單的作到,所以就以 VFP 實作.<br />
<br />
該文以計算牌照稅為引子<br />
計算牌照稅的時候,會需要依照汽缸的cc數以及私人/營業用車來判定收費的標準,照一般的寫法,很自然就會用到很多 If...Then...Else 或 do case...endcase, 可是這樣子程式碼就會變得很長很長,而難以維護.<br />
像是這樣:<br />
<br />
If cc < 500<br />
&nbsp;&nbsp;money=900<br />
else<br />
&nbsp;&nbsp;if cc < 600 <br />
&nbsp;&nbsp;&nbsp;&nbsp;money=1200<br />
&nbsp;&nbsp;&nbsp;&nbsp;&amp;&amp;..... 略<br />
&nbsp;&nbsp;endif<br />
endif<br />
<br />
用 do case 的話則是這樣<br />
do case<br />
&nbsp;&nbsp;case between( 0, 500 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;money=900<br />
&nbsp;&nbsp;case between( 501, 599 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;money=1200<br />
&nbsp;&nbsp;&amp;&amp; ... 略<br />
endcase<br />
<br />
<br />
李先生以一個 Dispatcher pattern 解決此一問題.<br />
在 VFP 裡面可以直接結合資料庫作更好的解法<br />
<br />
*<br />
* ITax.prg<br />
*<br />
DEFINE CLASS ITax as custom<br />
&nbsp;&nbsp;PROCEDURE Init()<br />
&nbsp;&nbsp;&nbsp;&nbsp;create cursor crTax( cc_lo I, cc_hi I, PrivateTax Y,  BusinesTax Y )<br />
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values (    0,  500, $ 1620, $  900 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values (  501,  599, $ 2160, $ 1200 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values (  600, 1199, $ 4320, $ 2160 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values ( 1200, 1799, $ 7120, $ 3060 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values ( 1800, 2399, $11230, $ 6480 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values ( 2400, 2999, $15210, $ 9900 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values ( 3000, 4199, $28220, $16380 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values ( 4200, 5399, $46170, $24300 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values ( 5400, 6599, $69690, $33660 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values ( 6600, 7799, $117000, $44460 )<br />
&nbsp;&nbsp;&nbsp;&nbsp;insert into crTax values ( 7800, 9999999, $117000, $44460 )<br />
&nbsp;&nbsp;ENDPROC<br />
&nbsp;&nbsp;PROCEDURE Destroy()<br />
&nbsp;&nbsp;&nbsp;&nbsp;use in crTax<br />
&nbsp;&nbsp;ENDPROC<br />
&nbsp;&nbsp;PROCEDURE GetTax( cc, theKind )<br />
&nbsp;&nbsp;&nbsp;&nbsp;local ly_result<br />
&nbsp;&nbsp;&nbsp;&nbsp;local lc_oldalias<br />
&nbsp;&nbsp;&nbsp;&nbsp;lc_oldalias=alias()<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;select("crTax")<br />
&nbsp;&nbsp;&nbsp;&nbsp;go top<br />
&nbsp;&nbsp;&nbsp;&nbsp;locate for between( cc, crTax.cc_lo, crTax.cc_hi )<br />
&nbsp;&nbsp;&nbsp;&nbsp;if found()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ly_result=crTax.&theKind<br />
&nbsp;&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ly_result=0<br />
&nbsp;&nbsp;&nbsp;&nbsp;endif<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;select( lc_oldalias )<br />
&nbsp;&nbsp;&nbsp;&nbsp;return ly_result<br />
&nbsp;&nbsp;ENDPROC<br />
&nbsp;&nbsp;PROCEDURE GetPrivateTax( cc )<br />
&nbsp;&nbsp;&nbsp;&nbsp;return this.GetTax( cc, "PrivateTax" )<br />
&nbsp;&nbsp;ENDPROC<br />
&nbsp;&nbsp;PROCEDURE GetBusinessTax( cc )<br />
&nbsp;&nbsp;&nbsp;&nbsp;return this.GetTax( cc, "BusinesTax" )<br />
&nbsp;&nbsp;ENDPROC<br />
ENDDEFINE<br />
<br />
*<br />
* test_itax.prg<br />
*<br />
LOCAL lo_obj<br />
set procedure to itax.prg<br />
lo_obj=createobject("ITax")<br />
? lo_obj.GetPrivateTax( 1000 )<br />
? lo_obj.GetBusinessTax( 2000 )<br />
<br />
瞧,這樣不是簡單多了嗎??<br />
在這裡拋磚引玉一下,希望大家如果有更好的解法<br />
也 post 上來,讓大家觀摩一下<br />
^_^		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553911.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553911.html</guid>
	<category>Visual FoxPro</category>
	<pubDate>Mon, 03 Nov 2003 21:57:50 +0800</pubDate>
</item>
<item>
	<title>VFP on Linux - 與 Wine 共舞的樂章</title>
	<description><![CDATA[
	寫在前面
========
老實說,這篇文章充其量,祇能說是整理而已.
純粹去網路找文章,實作,遇到與文章不符的地方,就自己動手找資料,實驗,找答案,就這樣而已.
我想,大概有百分之五十是參考資料,百分之四十是翻譯;剩下百分之十,才是我的心得與實作過程.

Wine 的歷史
===========
請直接參考 2003/08/15 的 Wine Traffic
http://kt.zork.net/wine/wn20030815_183.html#5

安裝,from tarball
=================
以 tar xzf 解開之後,進行 patch, patch 檔網址在此:
http://www.paulmcnett.com/vfp/wine/vfpwinepatchwinsize
這個 patch 檔主要是修正 WAIT WINDOW 和 TooltipText window 無法正確顯示的問題.
Patch 方法:
cat vfpwinepatchwinsize | patch -p1
也可以手動編輯 wine/dlls/x11drv/winpos.c
在約 887 行的地方,找到
BOOL
X11DRV_SetWindowPos( WINDOWPOS *winpos )
這個 function,並在裡面的
TRACE(
"hwnd %p ......
之前加上
/* This is needed to flush pending X ConfigureNotify events on this window */
MsgWaitForMultipleObjectsEx( 0, NULL, 0, 0, 0 );
存檔以後即可.

執行 ./tools/wineinstall

它會出現訊息,建議不要以 root 身分來安裝
如果你堅持要以 root 身分來安裝, 請修改此 script,讓他忽略此訊息
(178~184 行,前面加上'#' )
再執行此 script.
當然如果要用別的身分來安裝,請用 su 指令切換到其他 user 帳號
或重新以其他 user 帳號登入即可.

此 script會執行 ./configure,設置必要的設定檔以及路徑.
然後再執行
make depend && make && make install
進行編譯及安裝工作.

若以其他user進行安裝的話,就依照 script指示即可
wine 最後會以 SUID 形式存在.

RPM 安裝
========
請到官方網站下載符合你 Linux distribution 的 RPM 版本
然後執行 rpm -ivh wine-2003xxxx.ix86.rpm
即可~
但要注意的是,不保證可以用,建議還是以 tarball 安裝較佳.這樣也可加上 patch.

Usage
=====
winhelp, notepad, regsvr32, regedit 這幾個不用說明,就跟Windows 上的一樣~
progman 的話就跟 Windows 3.1 上的 Progman 一樣,執行以後,
有用過 Windows 3.1 的人,大概會很懷念~
winefile 則是檔案總管
wcmd 是"命令提示字元"
uninstaller 則是"新增/移除程式"
winecfg 則是調校 wine 設定的程式
執行軟體時,以 wine 執行之,假設你複製了小算盤(calc.exe)進去
那麼就輸入 wine calc.exe 即可~
安裝軟體,也一樣,執行 setup 即可: wine setup.exe

Configuration
=============
"Version"Section 的 Windows key 值改為 win2k
"DllOverrides" Section 中所有 key 值改為 "native, builtin"
此外把你自己 Winnt\system32 或 Windows\system 下的
OLEAUT32.DLL
COMMDLG.DLL
COMDLG32.DLL
SHELL.DLL
SHELL32.DLL
SHFOLDER.DLL
SHLWAPI.DLL
SHDOCVW.DLL
ADVAPI32.DLL
MSVCRT*.DLL
VFP6*.DLL
ODBC32.DLL
ODBC32GT.DLL
ODBC16GT.DLL
ODBCINT.DLL
ODBCCONF.DLL
都複製到 ~/c/windows/system 下
此外,OLE32.DLL, ADVAPI32.DLL, NTDLL.DLL 是不需要複製的,因為會造成無法執行.
REGEDIT.EXE 也請複製到 ~/c/windows 下,因為我們要把 ODBC 的 Registry import 進去.
你也許會問 wine 不是提供了 regedit.exe 嗎?可是根據我自己的試驗,它並無法匯入.
請在 Windows 下執行 regedit.exe 將
HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI\ODBC Drivers
以及
HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI\Microsoft Visual FoxPro Driver
這兩個機碼都匯出.分別存為 ODBCDrivers.reg 及 VFPDriver.reg
然後編輯 ODBCDrivers.reg ,將其他 Driver 都刪掉,只留下 Microsoft Visual FoxPro Driver 即可
接著就拿到 Linux 上,執行 regedit.exe 匯入囉.

開始使用 VFP
============
我自己是建議不要用 setup 安裝,網路上的一些狐友也這樣說
就是用 copy 的方式把 Program Files 目錄下的 VFP 目錄直接複製到 ~/c/Program Files 下
再參考上面的 Configuration 一節作修正.
接著就可以執行 wine VFP6.EXE 啦~~
進入以後,你會發現 command window 無法顯示游標,請用 Alt+TAB 切到別的視窗, 再按一次 Alt+TAB 切回 VFP,即可.

Issue
=====
Declare DLL ok
大部分函數都已經實作出來了~
可能會發生的問題,多半是路徑問題,此問題可以藉著修改 ~/.wine/config 來解決.

Record locking
20030318 版之前是有問題的,但之後的版本就都可以了

ActiveX
有些 ActiveX 會無法使用,那是因為有些 function 還沒實作出來的原因.

中文
對我們來說,這是最大的問題了
Linux 中雖然已經支援中文,可是 wine 看來是還未支援,也因此,文字無法輸入到 VFP 視窗中.
此外,字型名稱也是一大問題,由於小弟對中文字型設定這部分還不是很熟
再加上目前 X 組織又提出一個新的技術 Xft 要解決字型名稱問題,所以請期待吧~

其他
沒有 HTML Help.
在 Class Designer/Form Designer 裡面無法 copy/paste 物件.
Undocked windows 需要被設定為 undockable (在Title band 按下滑鼠右鍵) 或他們無法取得 focus.

EULA(End User Licence Aggreement) - Microsoft 的阻撓
====================================================
以下大致從 http://www.linuxtransfer.com/h/misc_vfplinuxjackofhearts.htm 譯出.
僅將大意譯出,如果譯的不好,還請見諒.

事情是這樣開始的,就在今年的四月左右,Ken Levy,Whil Hentzen在展示一個關於 VFP on Linux 的 Demo 之後,他接到一通來自微軟的電話,告知他說,這樣的一個 Demo 將可能會與 VFP EULA 衝突.後來這件事情就慢慢的擴散....
什麼是 EULA ?? EULA 就是 End User Licence Aggreement 的縮寫.
以下就是與 VFP on Linux 有關的 VFP8 EULA條款:
3.1 General Distribution Requirements.
(a) If you choose to redistribute Sample Code, or Redistributable Code (collectively, the "Redistributables") as described in Section 2, you agree:
(i) except as otherwise noted in Section 2.1 (Sample Code), to distribute the Redistributables only in object code form and in conjunction with and as a part of a software application product developed by you that adds significant and primary functionality to the Redistributables ("Licensee Software");
(ii) that the Redistributables only operate in conjunction with Microsoft Windows platforms;
在 VFP 社群要求微軟作進一步說明的一星期後,這是微軟的說明(澄清):
Visual FoxPro was designed and tested for use in creating applications that run on the Microsoft Windows platform; the same applies to the components that are provided to developers for redistribution with Visual FoxPro-based applications. If a developer wishes to distribute the Visual FoxPro runtime with an application, the runtime may only operate in conjunction with a Microsoft Windows platform. As with any contract, you should seek your own legal counsel's advice when interpreting your rights and obligations under the Visual FoxPro End User License Agreement.
大意是,VFP本身是在 Windows 平台上發展及測試的,和VFP一起提供給開發者散佈的元件也是.如果開發者想要將VFP runtime和應用程式一起發佈,runtime 應該只能(may only)在Microsoft Windows 平台上運行.如同任何的合約,你應該尋求你的法律顧問的建議.
在這篇文章中,http://www.linuxtransfer.com/h/misc_vfplinuxjackofhearts.htm
提出了三點疑問,
1.Running the VFP Development Environment on Linux
VFP 開發者想要在一台 Linux 機器上將 VFP 當作一個開發環境.
這個企圖正是 McNett's FoxTalk 文章的主旨,而且是Levy舉辦該活動的主題,卻又宣告他是與VFP7 和 VFP8 的 EULA 衝突,不允許公開展示.
2.Deploying custom VFP applications on Linux workstations
VFP 如同大家所知的,AP都是要與 runtime 一同包裝,並安裝到客戶那兒.以前很簡單,都只要幾個DLL檔案就行了,現在,最後一版的VFP,都是以MSM 形式給Installer tool使用.只有這些 MSM 檔案列在 REDIST.TXT 中,卻沒有明確的指定是哪些VFP DLL 檔案.
所以只要用這個方法是不是就可以避免了呢?
第一個問題是,如果發布自訂的VFP DLLs 在 EULA 裡是不被允許的,那麼
(1)如果不用 Windows Installer 技術的話,那麼市場上還有許多不使用 Windows Installer 技術的安裝工具,這些工具是不是就與 VFP EULA 衝突了呢??
(2)為什麼從6.0 到7.0,這樣的一個改變,卻沒有任何公告??就正常來說,一個產品的改變應該會被公告於 "Read Me" 或 "What's New" 檔案中.可是這卻被放到 EULA 中,而缺乏任何說明.
ok,假設VFP EULA 禁止散佈 VFP DLLs,只能使用 Windows Installer 技術.
總之,看起來,Microsoft 就是想把它們的應用程式綁在他們的 OS 上.
3.The Business Issues
Microsoft 之所以需要 VFP, FoxBase 和 FoxPro, 主要就是為了要打擊Desktop database市場上其他的對手.那個時候,幾乎所有產品都不需要 runtime license,就這樣,一直延續到現在.Microsoft長期忽略 VFP,大概與Business Model有關,他們寧可開發者用VB 和 SQL Server,這樣就可從 SQL Server 那兒收到 licenses 費用.
VFP 可以在 Linux 上執行是非常吸引人的,想想一套便宜(或免費)的作業系統加上一個不貴又有威力的開發工具,和一個便宜(或免費)的後端資料庫,是多麼的不錯.很簡單的可以看出來為什麼 Microsoft 要試著去對抗他,並強加了和以往不一樣的的授權限制.

參考網址: (謝謝Ruey提供部份資料)
===============================
安裝:
http://www.pinpub.com/FT/FTmag.nsf/0/843B563D8FB169F485256D6700710C3A
http://www.paulmcnett.com/vfp/vfp7wine.html (安裝)
http://www.paulmcnett.com/vfp/wine/foxtalk1.html
相關資料收集:
http://fox.wikis.com/wc.dll?Wiki~VFPandLinux~VFP
http://www.associateddata.co.uk/VFPLinux.htm
微軟的恫嚇與EULA的相關討論:
http://www.linuxworld.com/story/32665.htm
http://mail.linux.ie/pipermail/ilug/2003-April/002197.html
http://www.linuxtransfer.com/h/misc_vfplinuxjackofhearts.htm
http://www.linuxjournal.com/article.php?sid=6869&mode=thread&order=0
Wine 官方網站 Guide:
http://www.winehq.com/Docs/wine-user/
Wine 應用程式相容性:
http://appdb.winehq.com/
http://frankscorner.org/
Wine 所收錄的 VFP Profile:
http://appdb.winehq.com/appview.php?appId=296;PHPSESSID=9e9d479fa55fb6c759ad092fd5aa3184
	]]>
	</description>
	<content:encoded><![CDATA[
	寫在前面<br />
========<br />
老實說,這篇文章充其量,祇能說是整理而已.<br />
純粹去網路找文章,實作,遇到與文章不符的地方,就自己動手找資料,實驗,找答案,就這樣而已.<br />
我想,大概有百分之五十是參考資料,百分之四十是翻譯;剩下百分之十,才是我的心得與實作過程.<br />
<br />
Wine 的歷史<br />
===========<br />
請直接參考 2003/08/15 的 Wine Traffic<br />
http://kt.zork.net/wine/wn20030815_183.html#5<br />
<br />
安裝,from tarball<br />
=================<br />
以 tar xzf 解開之後,進行 patch, patch 檔網址在此:<br />
http://www.paulmcnett.com/vfp/wine/vfpwinepatchwinsize<br />
這個 patch 檔主要是修正 WAIT WINDOW 和 TooltipText window 無法正確顯示的問題.<br />
Patch 方法:<br />
cat vfpwinepatchwinsize | patch -p1<br />
也可以手動編輯 wine/dlls/x11drv/winpos.c<br />
在約 887 行的地方,找到<br />
BOOL<br />
X11DRV_SetWindowPos( WINDOWPOS *winpos )<br />
這個 function,並在裡面的<br />
TRACE(<br />
"hwnd %p ......<br />
之前加上<br />
/* This is needed to flush pending X ConfigureNotify events on this window */<br />
MsgWaitForMultipleObjectsEx( 0, NULL, 0, 0, 0 );<br />
存檔以後即可.<br />
<br />
執行 ./tools/wineinstall<br />
<br />
它會出現訊息,建議不要以 root 身分來安裝<br />
如果你堅持要以 root 身分來安裝, 請修改此 script,讓他忽略此訊息<br />
(178~184 行,前面加上'#' )<br />
再執行此 script.<br />
當然如果要用別的身分來安裝,請用 su 指令切換到其他 user 帳號<br />
或重新以其他 user 帳號登入即可.<br />
<br />
此 script會執行 ./configure,設置必要的設定檔以及路徑.<br />
然後再執行<br />
make depend && make && make install<br />
進行編譯及安裝工作.<br />
<br />
若以其他user進行安裝的話,就依照 script指示即可<br />
wine 最後會以 SUID 形式存在.<br />
<br />
RPM 安裝<br />
========<br />
請到官方網站下載符合你 Linux distribution 的 RPM 版本<br />
然後執行 rpm -ivh wine-2003xxxx.ix86.rpm<br />
即可~<br />
但要注意的是,不保證可以用,建議還是以 tarball 安裝較佳.這樣也可加上 patch.<br />
<br />
Usage<br />
=====<br />
winhelp, notepad, regsvr32, regedit 這幾個不用說明,就跟Windows 上的一樣~<br />
progman 的話就跟 Windows 3.1 上的 Progman 一樣,執行以後,<br />
有用過 Windows 3.1 的人,大概會很懷念~<br />
winefile 則是檔案總管<br />
wcmd 是"命令提示字元"<br />
uninstaller 則是"新增/移除程式"<br />
winecfg 則是調校 wine 設定的程式<br />
執行軟體時,以 wine 執行之,假設你複製了小算盤(calc.exe)進去<br />
那麼就輸入 wine calc.exe 即可~<br />
安裝軟體,也一樣,執行 setup 即可: wine setup.exe<br />
<br />
Configuration<br />
=============<br />
"Version"Section 的 Windows key 值改為 win2k<br />
"DllOverrides" Section 中所有 key 值改為 "native, builtin"<br />
此外把你自己 Winnt\system32 或 Windows\system 下的<br />
OLEAUT32.DLL<br />
COMMDLG.DLL<br />
COMDLG32.DLL<br />
SHELL.DLL<br />
SHELL32.DLL<br />
SHFOLDER.DLL<br />
SHLWAPI.DLL<br />
SHDOCVW.DLL<br />
ADVAPI32.DLL<br />
MSVCRT*.DLL<br />
VFP6*.DLL<br />
ODBC32.DLL<br />
ODBC32GT.DLL<br />
ODBC16GT.DLL<br />
ODBCINT.DLL<br />
ODBCCONF.DLL<br />
都複製到 ~/c/windows/system 下<br />
此外,OLE32.DLL, ADVAPI32.DLL, NTDLL.DLL 是不需要複製的,因為會造成無法執行.<br />
REGEDIT.EXE 也請複製到 ~/c/windows 下,因為我們要把 ODBC 的 Registry import 進去.<br />
你也許會問 wine 不是提供了 regedit.exe 嗎?可是根據我自己的試驗,它並無法匯入.<br />
請在 Windows 下執行 regedit.exe 將<br />
HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI\ODBC Drivers<br />
以及<br />
HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI\Microsoft Visual FoxPro Driver<br />
這兩個機碼都匯出.分別存為 ODBCDrivers.reg 及 VFPDriver.reg<br />
然後編輯 ODBCDrivers.reg ,將其他 Driver 都刪掉,只留下 Microsoft Visual FoxPro Driver 即可<br />
接著就拿到 Linux 上,執行 regedit.exe 匯入囉.<br />
<br />
開始使用 VFP<br />
============<br />
我自己是建議不要用 setup 安裝,網路上的一些狐友也這樣說<br />
就是用 copy 的方式把 Program Files 目錄下的 VFP 目錄直接複製到 ~/c/Program Files 下<br />
再參考上面的 Configuration 一節作修正.<br />
接著就可以執行 wine VFP6.EXE 啦~~<br />
進入以後,你會發現 command window 無法顯示游標,請用 Alt+TAB 切到別的視窗, 再按一次 Alt+TAB 切回 VFP,即可.<br />
<br />
Issue<br />
=====<br />
Declare DLL ok<br />
大部分函數都已經實作出來了~<br />
可能會發生的問題,多半是路徑問題,此問題可以藉著修改 ~/.wine/config 來解決.<br />
<br />
Record locking<br />
20030318 版之前是有問題的,但之後的版本就都可以了<br />
<br />
ActiveX<br />
有些 ActiveX 會無法使用,那是因為有些 function 還沒實作出來的原因.<br />
<br />
中文<br />
對我們來說,這是最大的問題了<br />
Linux 中雖然已經支援中文,可是 wine 看來是還未支援,也因此,文字無法輸入到 VFP 視窗中.<br />
此外,字型名稱也是一大問題,由於小弟對中文字型設定這部分還不是很熟<br />
再加上目前 X 組織又提出一個新的技術 Xft 要解決字型名稱問題,所以請期待吧~<br />
<br />
其他<br />
沒有 HTML Help.<br />
在 Class Designer/Form Designer 裡面無法 copy/paste 物件.<br />
Undocked windows 需要被設定為 undockable (在Title band 按下滑鼠右鍵) 或他們無法取得 focus.<br />
<br />
EULA(End User Licence Aggreement) - Microsoft 的阻撓<br />
====================================================<br />
以下大致從 http://www.linuxtransfer.com/h/misc_vfplinuxjackofhearts.htm 譯出.<br />
僅將大意譯出,如果譯的不好,還請見諒.<br />
<br />
事情是這樣開始的,就在今年的四月左右,Ken Levy,Whil Hentzen在展示一個關於 VFP on Linux 的 Demo 之後,他接到一通來自微軟的電話,告知他說,這樣的一個 Demo 將可能會與 VFP EULA 衝突.後來這件事情就慢慢的擴散....<br />
什麼是 EULA ?? EULA 就是 End User Licence Aggreement 的縮寫.<br />
以下就是與 VFP on Linux 有關的 VFP8 EULA條款:<br />
3.1 General Distribution Requirements.<br />
(a) If you choose to redistribute Sample Code, or Redistributable Code (collectively, the "Redistributables") as described in Section 2, you agree:<br />
(i) except as otherwise noted in Section 2.1 (Sample Code), to distribute the Redistributables only in object code form and in conjunction with and as a part of a software application product developed by you that adds significant and primary functionality to the Redistributables ("Licensee Software");<br />
(ii) that the Redistributables only operate in conjunction with Microsoft Windows platforms;<br />
在 VFP 社群要求微軟作進一步說明的一星期後,這是微軟的說明(澄清):<br />
Visual FoxPro was designed and tested for use in creating applications that run on the Microsoft Windows platform; the same applies to the components that are provided to developers for redistribution with Visual FoxPro-based applications. If a developer wishes to distribute the Visual FoxPro runtime with an application, the runtime may only operate in conjunction with a Microsoft Windows platform. As with any contract, you should seek your own legal counsel's advice when interpreting your rights and obligations under the Visual FoxPro End User License Agreement.<br />
大意是,VFP本身是在 Windows 平台上發展及測試的,和VFP一起提供給開發者散佈的元件也是.如果開發者想要將VFP runtime和應用程式一起發佈,runtime 應該只能(may only)在Microsoft Windows 平台上運行.如同任何的合約,你應該尋求你的法律顧問的建議.<br />
在這篇文章中,http://www.linuxtransfer.com/h/misc_vfplinuxjackofhearts.htm<br />
提出了三點疑問,<br />
1.Running the VFP Development Environment on Linux<br />
VFP 開發者想要在一台 Linux 機器上將 VFP 當作一個開發環境.<br />
這個企圖正是 McNett's FoxTalk 文章的主旨,而且是Levy舉辦該活動的主題,卻又宣告他是與VFP7 和 VFP8 的 EULA 衝突,不允許公開展示.<br />
2.Deploying custom VFP applications on Linux workstations<br />
VFP 如同大家所知的,AP都是要與 runtime 一同包裝,並安裝到客戶那兒.以前很簡單,都只要幾個DLL檔案就行了,現在,最後一版的VFP,都是以MSM 形式給Installer tool使用.只有這些 MSM 檔案列在 REDIST.TXT 中,卻沒有明確的指定是哪些VFP DLL 檔案.<br />
所以只要用這個方法是不是就可以避免了呢?<br />
第一個問題是,如果發布自訂的VFP DLLs 在 EULA 裡是不被允許的,那麼<br />
(1)如果不用 Windows Installer 技術的話,那麼市場上還有許多不使用 Windows Installer 技術的安裝工具,這些工具是不是就與 VFP EULA 衝突了呢??<br />
(2)為什麼從6.0 到7.0,這樣的一個改變,卻沒有任何公告??就正常來說,一個產品的改變應該會被公告於 "Read Me" 或 "What's New" 檔案中.可是這卻被放到 EULA 中,而缺乏任何說明.<br />
ok,假設VFP EULA 禁止散佈 VFP DLLs,只能使用 Windows Installer 技術.<br />
總之,看起來,Microsoft 就是想把它們的應用程式綁在他們的 OS 上.<br />
3.The Business Issues<br />
Microsoft 之所以需要 VFP, FoxBase 和 FoxPro, 主要就是為了要打擊Desktop database市場上其他的對手.那個時候,幾乎所有產品都不需要 runtime license,就這樣,一直延續到現在.Microsoft長期忽略 VFP,大概與Business Model有關,他們寧可開發者用VB 和 SQL Server,這樣就可從 SQL Server 那兒收到 licenses 費用.<br />
VFP 可以在 Linux 上執行是非常吸引人的,想想一套便宜(或免費)的作業系統加上一個不貴又有威力的開發工具,和一個便宜(或免費)的後端資料庫,是多麼的不錯.很簡單的可以看出來為什麼 Microsoft 要試著去對抗他,並強加了和以往不一樣的的授權限制.<br />
<br />
參考網址: (謝謝Ruey提供部份資料)<br />
===============================<br />
安裝:<br />
http://www.pinpub.com/FT/FTmag.nsf/0/843B563D8FB169F485256D6700710C3A<br />
http://www.paulmcnett.com/vfp/vfp7wine.html (安裝)<br />
http://www.paulmcnett.com/vfp/wine/foxtalk1.html<br />
相關資料收集:<br />
http://fox.wikis.com/wc.dll?Wiki~VFPandLinux~VFP<br />
http://www.associateddata.co.uk/VFPLinux.htm<br />
微軟的恫嚇與EULA的相關討論:<br />
http://www.linuxworld.com/story/32665.htm<br />
http://mail.linux.ie/pipermail/ilug/2003-April/002197.html<br />
http://www.linuxtransfer.com/h/misc_vfplinuxjackofhearts.htm<br />
http://www.linuxjournal.com/article.php?sid=6869&mode=thread&order=0<br />
Wine 官方網站 Guide:<br />
http://www.winehq.com/Docs/wine-user/<br />
Wine 應用程式相容性:<br />
http://appdb.winehq.com/<br />
http://frankscorner.org/<br />
Wine 所收錄的 VFP Profile:<br />
http://appdb.winehq.com/appview.php?appId=296;PHPSESSID=9e9d479fa55fb6c759ad092fd5aa3184		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553886.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553886.html</guid>
	<category>Visual FoxPro</category>
	<pubDate>Sat, 23 Aug 2003 20:39:39 +0800</pubDate>
</item>
<item>
	<title>VFP Toolkit for Visual Studio.NET</title>
	<description><![CDATA[
	其實他根本就是一個 wrapper
把 .net 的 code 給包裝起來
看起來的確變的很類似 vfp
但實際上宣告變數以及語法仍然遵循 c# 或 vb.net
而非原來的 vfp 

不過文件上說的沒錯
他提供了絕大部分的 vfp 函數
可是~
存取資料時
仍然要使用 sqlconnect
而不是 use
也因此沒有索引的功能
但有提供索引搜尋跟切換索引的功能
只是這些東西仍然依存於 .net dataview 元件的能力
換言之
如果要細調其執行效能
恐怕仍然需要了解 .net ado.net

source code 很有幫助,並不是很困難
建議也下載回去看
對上手 .net 有相當的助益
	]]>
	</description>
	<content:encoded><![CDATA[
	其實他根本就是一個 wrapper<br />
把 .net 的 code 給包裝起來<br />
看起來的確變的很類似 vfp<br />
但實際上宣告變數以及語法仍然遵循 c# 或 vb.net<br />
而非原來的 vfp <br />
<br />
不過文件上說的沒錯<br />
他提供了絕大部分的 vfp 函數<br />
可是~<br />
存取資料時<br />
仍然要使用 sqlconnect<br />
而不是 use<br />
也因此沒有索引的功能<br />
但有提供索引搜尋跟切換索引的功能<br />
只是這些東西仍然依存於 .net dataview 元件的能力<br />
換言之<br />
如果要細調其執行效能<br />
恐怕仍然需要了解 .net ado.net<br />
<br />
source code 很有幫助,並不是很困難<br />
建議也下載回去看<br />
對上手 .net 有相當的助益		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553849.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553849.html</guid>
	<category>Visual FoxPro</category>
	<pubDate>Sat, 21 Dec 2002 17:14:06 +0800</pubDate>
</item>
<item>
	<title>VFP OLEDB Drivers How-To</title>
	<description><![CDATA[
	限制:
以6.0而言,似乎一定要用 ado 來開啟

範例:
OConn = CREATEOBJECT("ADODB.Connection")
OConn.ConnectionString = "provider=vfpoledb.1;;
data source=.\testdbc.dbc"
OConn.Open

語法:
Provider = VFPOLEDB 
Data Source=cPath 
Specifies the path to the Visual FoxPro database or folder containing free tables. For example, d:\vfp7\samples\data\testdata.dbc 
Mode=cMode 
Specifies one of the following: Read, ReadWrite, Share Deny None (default), Share Deny Read, Share Deny Write, Share Exclusive (inclusive of the previous two modes). 

可以丟如下指令
The Visual FoxPro OLE DB Provider supports the native Visual FoxPro language syntax for the following commands: 

CREATE TABLE - SQL Command 
Creates a table having the specified fields. 
DELETE - SQL Command 
Marks records for deletion. 
DROP TABLE Command 
Removes a table from the database specified with the data source and deletes it from disk. 
INSERT - SQL Command 
Appends a record to the end of a table that contains the specified field values. 
SELECT - SQL Command 
Retrieves data from one or more tables. 
UPDATE - SQL Command 
Updates records in a table with new values. 
The Visual FoxPro Language Reference contains detailed information about the following supported commands: 

ALTER TABLE - SQL Command 
Programmatically modifies the structure of a table. 
CREATE TABLE - SQL Command 
Creates a table having the specified fields. 
Using Data Definition Language (DDL)
You cannot include DDL in the following places: 

In a batch SQL statement that requires a transaction 
Following a previously executed statement that required a transaction if not in auto-commit mode and if your application has not yet called SQLTransact. 
For example, if you want to create a temporary table, you should create the table before you begin the statement requiring a transaction. If you include the CREATE TABLE statement in a batch SQL statement that requires a transaction, the provider returns an error message. 

DELETE - SQL Command 
Marks records for deletion. 
DELETE TAG Command 
Removes a tag or tags from a compound index (.cdx) file. 
DROP TABLE Command 
Removes a table from the database specified with the data source and deletes it from disk. 
INDEX Command 
Creates an index file to display and access table records in a logical order. 
INSERT - SQL Command 
Appends a record to the end of a table that contains the specified field values. 
SELECT - SQL Command 
Retrieves data from one or more tables. 
The Visual FoxPro OLE DB Provider supports the native Visual FoxPro language syntax for this command. 

SET ANSI Command 
Determines how comparisons between strings of different lengths are made with the = operator in Visual FoxPro SQL commands. 
SET BLOCKSIZE Command 
Specifies how disk space is allocated for the storage of memo fields. 
SET COLLATE Command 
Specifies a collation sequence for character fields in subsequent indexing and sorting operations. 
SET DELETED Command 
Specifies whether records marked for deletion are processed and whether they are available for use in other commands. 
SET EXACT Command 
Specifies the rules for comparing two strings of different lengths. 
SET EXCLUSIVE Command 
Specifies whether table files are opened for exclusive or shared use on a network. 
SET NULL Command 
Determines how null values are supported by the ALTER TABLE - SQL, CREATE TABLE - SQL, and INSERT - SQL commands. 
SET PATH Command 
Specifies a path for file searches. 
Provider Remarks
If you issue SET PATH in a stored procedure, it will be ignored by the following functions and commands: SELECT, INSERT, UPDATE, DELETE, and CREATE TABLE 

If you issue SET PATH in a stored procedure and do not subsequently set the path back to its original state, other connections to the database will use the new path (because SET PATH is not scoped to data sessions).

If you want to create, select, or update tables in a directory other than that specified by the data source, specify the full path of the file with your command. 

SET REPROCESS Command 
Specifies how many times or for how long to lock a file or record after an unsuccessful locking attempt. 
SET UNIQUE Command 
Specifies whether records with duplicate index key values are maintained in an index file. 
UPDATE - SQL Command 
Updates records in a table with new values.
	]]>
	</description>
	<content:encoded><![CDATA[
	限制:<br />
以6.0而言,似乎一定要用 ado 來開啟<br />
<br />
範例:<br />
OConn = CREATEOBJECT("ADODB.Connection")<br />
OConn.ConnectionString = "provider=vfpoledb.1;;<br />
data source=.\testdbc.dbc"<br />
OConn.Open<br />
<br />
語法:<br />
Provider = VFPOLEDB <br />
Data Source=cPath <br />
Specifies the path to the Visual FoxPro database or folder containing free tables. For example, d:\vfp7\samples\data\testdata.dbc <br />
Mode=cMode <br />
Specifies one of the following: Read, ReadWrite, Share Deny None (default), Share Deny Read, Share Deny Write, Share Exclusive (inclusive of the previous two modes). <br />
<br />
可以丟如下指令<br />
The Visual FoxPro OLE DB Provider supports the native Visual FoxPro language syntax for the following commands: <br />
<br />
CREATE TABLE - SQL Command <br />
Creates a table having the specified fields. <br />
DELETE - SQL Command <br />
Marks records for deletion. <br />
DROP TABLE Command <br />
Removes a table from the database specified with the data source and deletes it from disk. <br />
INSERT - SQL Command <br />
Appends a record to the end of a table that contains the specified field values. <br />
SELECT - SQL Command <br />
Retrieves data from one or more tables. <br />
UPDATE - SQL Command <br />
Updates records in a table with new values. <br />
The Visual FoxPro Language Reference contains detailed information about the following supported commands: <br />
<br />
ALTER TABLE - SQL Command <br />
Programmatically modifies the structure of a table. <br />
CREATE TABLE - SQL Command <br />
Creates a table having the specified fields. <br />
Using Data Definition Language (DDL)<br />
You cannot include DDL in the following places: <br />
<br />
In a batch SQL statement that requires a transaction <br />
Following a previously executed statement that required a transaction if not in auto-commit mode and if your application has not yet called SQLTransact. <br />
For example, if you want to create a temporary table, you should create the table before you begin the statement requiring a transaction. If you include the CREATE TABLE statement in a batch SQL statement that requires a transaction, the provider returns an error message. <br />
<br />
DELETE - SQL Command <br />
Marks records for deletion. <br />
DELETE TAG Command <br />
Removes a tag or tags from a compound index (.cdx) file. <br />
DROP TABLE Command <br />
Removes a table from the database specified with the data source and deletes it from disk. <br />
INDEX Command <br />
Creates an index file to display and access table records in a logical order. <br />
INSERT - SQL Command <br />
Appends a record to the end of a table that contains the specified field values. <br />
SELECT - SQL Command <br />
Retrieves data from one or more tables. <br />
The Visual FoxPro OLE DB Provider supports the native Visual FoxPro language syntax for this command. <br />
<br />
SET ANSI Command <br />
Determines how comparisons between strings of different lengths are made with the = operator in Visual FoxPro SQL commands. <br />
SET BLOCKSIZE Command <br />
Specifies how disk space is allocated for the storage of memo fields. <br />
SET COLLATE Command <br />
Specifies a collation sequence for character fields in subsequent indexing and sorting operations. <br />
SET DELETED Command <br />
Specifies whether records marked for deletion are processed and whether they are available for use in other commands. <br />
SET EXACT Command <br />
Specifies the rules for comparing two strings of different lengths. <br />
SET EXCLUSIVE Command <br />
Specifies whether table files are opened for exclusive or shared use on a network. <br />
SET NULL Command <br />
Determines how null values are supported by the ALTER TABLE - SQL, CREATE TABLE - SQL, and INSERT - SQL commands. <br />
SET PATH Command <br />
Specifies a path for file searches. <br />
Provider Remarks<br />
If you issue SET PATH in a stored procedure, it will be ignored by the following functions and commands: SELECT, INSERT, UPDATE, DELETE, and CREATE TABLE <br />
<br />
If you issue SET PATH in a stored procedure and do not subsequently set the path back to its original state, other connections to the database will use the new path (because SET PATH is not scoped to data sessions).<br />
<br />
If you want to create, select, or update tables in a directory other than that specified by the data source, specify the full path of the file with your command. <br />
<br />
SET REPROCESS Command <br />
Specifies how many times or for how long to lock a file or record after an unsuccessful locking attempt. <br />
SET UNIQUE Command <br />
Specifies whether records with duplicate index key values are maintained in an index file. <br />
UPDATE - SQL Command <br />
Updates records in a table with new values.		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553848.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553848.html</guid>
	<category>Visual FoxPro</category>
	<pubDate>Sat, 21 Dec 2002 17:04:51 +0800</pubDate>
</item>
<item>
	<title>VFP7 與 Web Service</title>
	<description><![CDATA[
	前言
建立Web Service
建立Web Service Client 端程式
使用其他語言建立的 Web Service
與資料庫結合
結論
參考資料

前言
VFP 這項產品一直被謠傳微軟將不再支援，但在今年六月，微軟將VFP7自Visual Studio.NET中獨立出來銷售，總算是打破了這個謠言。此外，也為這項產品添加了不少功能，比如 Intellisense、對COM的更完整支援、SOAP與Web Service 等等。這裡就為各位解說新增的SOAP與Web Service功能。

SOAP 與 Web Service 這兩個名詞，相信我不用再多做解釋了，大家在這一陣子的耳濡目染之下，應該都大致了解，在此就不再贅述。如果想了解的人請參考前幾期RUN!PC李維老師的文章，有詳細的解說。



建立Web Service
首先讓我們來製作 Web Service，稍後，再來撰寫如何使用 Web Service 的 Client 端程式。
要使用VFP7製作Web Service之前，請先在[Internet 服務管理員]中，新增一個虛擬目錄，在此，我們先命名為 WebServices。
接下來得製作一個COM元件，在這裡我們就以一個簡單的相加函數來作為一個範例。
請先建立一個新專案，名稱叫做MyWebService，在Project Manager(專案管理員)中，切換到 Program頁籤，點選 ”New”，將下列這段程式碼貼到裡面去，然後存檔為 prog1.prg。

DEFINE CLASS MyCalculator AS session OLEPUBLIC
Name = "MyCalculator"
PROCEDURE doAdd
                Parameters A , B
        Return A+B
ENDPROC
ENDDEFINE


這一段程式碼只是將傳入的兩個參數做一個簡單的相加而已。
接著就要進行編譯的工作了，點選”Build”，建立的型態選為”Single-threaded COM Server(dll)”，按下 ”確定” 來建立一個dll型態的COM伺服器。



建立完成後，你會發現VFP7為你產生了一個mywebservice.dll，並已經為你註冊到電腦中了，讓我們驗證一下VFP7是否已經編譯成功，請在命令列中依序輸入：


obj=createobject(“MyWebService.MyCalculator”)
? obj.doAdd(100,100)


如果成功的話，你應該會看到VFP7回應了 200 這個答案，表示COM伺服器已經建立成功。

VFP7提供了一個方便的精靈，讓你可以直接在VFP7的整合環境中建立 Web Service，而不需要另外去執行 SOAP toolkit 來製作。

請你在Project Manager中，以滑鼠右鍵點選叫出快捷選單，選擇Builder。此時，會出現一個選擇精靈的對話方盒(如圖)，選擇Web Services Publisher，並按下確定。



接著會出現Web Services Publisher這個對話盒(如圖)，我們按下 “…” 按鈕，去搜尋我們剛剛建立的MyWebService.MyCalculator COM伺服器。找到並確定之後，再按下”Advanced”，讓我們看看可以再多做些什麼設定。

在 Advanced 設定中，我們可以指定

WSDL 產出的位置。
SOAP Listener型態。
IntelliSense的Script名稱。
告訴VFP7在Project Build之後，是否自動產出相關的 Web Service 檔案。
產出的WSDL檔案是否要以UTF-16 Unicode作為預設編碼。







在確定所有設定無誤之後，按下 Generate，Web Service Publish精靈就會利用SOAP toolkit為我們在指定的位置產出Web Service的相關檔案。


Web Service Client 程式
那麼，我們要如何使用這個 Web Service 呢?
讓我們寫一個簡單的 form 來呼叫

請開一個新檔案，類型選為Form (表單)，在表單裡面放置一個按鈕(如下圖)，在按鈕的OnClick事件內放置如下的程式碼：


x = CREATEOBJECT("MSSOAP.SoapClient")
x.MSSoapInit("http://ellery/webservices/MyCalculator.WSDL", , "MyCalculatorSoapPort")

=messagebox( str( x.doAdd( 100,100 ) ) )


執行這個表單，在click按鈕之後，你會發現你並沒有辦法呼叫!!是VFP7的bug嗎??

首先,因為soap物件會利用 wsdl 檔案做 Initialization 的動作，所以我檢查了一下產出的 wsdl：


…略…
  &lt;message name='MyCalculator.doAdd'&gt;
  &lt;/message&gt;
  &lt;message name='MyCalculator.doAddResponse'&gt;
    &lt;part name='Result' type='xsd:anyType'/&gt;
  &lt;/message&gt;
…略…


在這裡 SOAP toolkit wizard 並沒有為我們的 COM 元件產生參數的宣告。
產出的 WSDL 碼應該是下面這樣子才對的呀.


…略…
  &lt;message name='MyCalculator.doAdd'&gt;
    &lt;part name='A' type='xsd:int'/&gt;
    &lt;part name='B' type='xsd:int'/&gt;
  &lt;/message&gt;

  &lt;message name='MyCalculator.doAddResponse'&gt;
    &lt;part name='Result' type='xsd:anyType'/&gt;
  &lt;/message&gt;
…略…


為了這個疑惑，我將問題貼到 Microsoft Newsgroup(http://communities.microsoft.com/newsgroups/default.asp) 和 Universal Thread(http://www.universalthread.com) 網站去詢問，過了兩天，就有了結果，兩個地方給我的回答都是相同的，我們必須將PROCEDURE doAdd 的宣告修改為

PROCEDURE doAdd(A as Integer, B as Integer)

並把 parameters 敘述移除。

修改後，重新 Build、利用 Wizard 發布之後，再執行剛剛我們建立好的 client 端Form，你應該就可以看到Web Service 被起始後的結果了。


使用其他語言建立的web service
為了驗證一下，VFP也能使用其他語言或其他平台上建立的 Web Service，我們仿效李維老師於RUN!PC 90 期文章中所寫的範例，寫一個取得溫度的簡單範例。

請依照下圖來製作一個表單：

 
然後，在”取得溫度”按鈕的 onclick 事件中，置入下列程式碼：

LOCAL obj
obj = CREATEOBJECT("MSSOAP.SoapClient")
obj.MSSoapInit("http://www.xmethods.net/sd/2001/TemperatureService.wsdl")
thisform.text2.value=obj.getTemp(thisform.text1.value)


在執行這個表單之後，在州代號的欄位中，輸入 07060，再按下 “取得溫度” 按鈕，下面的當地氣溫欄位便顯示出氣溫，表示可以正確的呼叫!

 

與資料庫結合

VFP為了因應 XML 的流行，在這個版本中添加了與XML 相關的三個函數：CURSORTOXML()、XMLTOCURSOR()、XMLUPDATEGRAM()，正好可以用來在 Web Services 中傳遞資料。

請在Project Manager中，新增一個程式檔(program)，然後將下列的程式碼加入，並命名為 prog2.prg：

DEFINE CLASS tastrade AS session OLEPUBLIC
Name = "tastrade"
PROCEDURE getAllEmployee()
        LOCAL handle as Integer 
        LOCAL lc_xml as String 

        handle=SQLSTRINGCONNECT("Driver=Microsoft Visual FoxPro Driver;” + ;
		“UID=;PWD=;” + ;
		“SourceDB=C:\WebService\Data\testdata.dbc;” + ;
		“SourceType=DBC;Exclusive=No;BackgroundFetch=Yes;” + ;
		“Collate=Machine;Null=Yes;Deleted=Yes;")
        =SQLEXEC(handle, ;
		"select emp_id, last_name, first_name, title from employee order by emp_id", ;
		"employees")
        =sqldisconnect(handle)
        CursorToXML("employees", "lc_xml")
        RETURN lc_xml
ENDPROC

PROCEDURE updateEmployee( c_request as string )
        LOCAL handle
        LOCAL o_xmlDom, o_nodelist1, o_nodelist2, o_nodeEmployee, o_node
        LOCAL strWhere as String , strSet as String , strSql as String 
        LOCAL ll_cycle as Boolean , ln_ret as Integer 

        c_request=STRTRAN( c_request, 'encoding="Windows-1252" ', "" )
        o_xmlDom=CreateObject("Microsoft.XMLDOM")
        o_xmlDom.async="false"
        o_xmlDom.loadXML(c_request)

        * parse xml and combine update-sql syntax
        IF o_xmlDom.hasChildNodes()
        ELSE
                RETURN
        ENDIF

        * create connection
        handle=SQLSTRINGCONNECT("Driver=Microsoft Visual FoxPro Driver;” + ;
	“UID=;PWD=;” + ;
	“SourceDB=C:\WebService\Data\testdata.dbc;” + ;
	“SourceType=DBC;Exclusive=No;BackgroundFetch=Yes;” + ;
	“Collate=Machine;Null=Yes;Deleted=Yes;")
        strWhere=""
        strSet=""
        l_cycle=.F.

        for each o_nodelist1 in o_xmlDom.documentElement.childNodes
                FOR EACH o_nodelist2 IN o_nodelist1.childNodes
                        * initialize variables
                        if o_nodelist2.nodeName="updg:before"
                                FOR EACH o_nodeEmployee IN o_nodelist2.childNodes
                                        FOR EACH o_node IN o_nodeEmployee.childNodes
                                                IF o_Node.nodeName="emp_id"
                                                        strWhere=strWhere+o_Node.nodeName+"="+"'" + ;
								TRANSFORM(o_Node.text,"@J 999999") + "' and "
                                                ELSE
                                                        strWhere=strWhere+o_Node.nodeName+"="+"'" + ;
								o_Node.text + "' and "
                                                ENDIF
                                        NEXT
                                NEXT
                        ENDIF
                        IF o_nodelist2.nodeName="updg:after"
                                IF NOT o_nodelist2.hasChildNodes()
                                ELSE
                                        FOR EACH o_nodeEmployee IN o_nodelist2.childNodes
                                                FOR EACH o_node IN o_nodeEmployee.childNodes
                                                        IF o_Node.nodeName="emp_id"
                                                        strSet=strSet+o_Node.nodeName+"="+"'" + ;
								TRANSFORM(o_Node.text,"@J 999999") + "',"
                                                        ELSE
                                                                strSet=strSet+o_Node.nodeName+"="+"'" + o_Node.text + "',"
                                                        ENDIF
                                                NEXT
                                        NEXT
                                ENDIF
                                l_cycle=.T.
                        ENDIF
                        IF l_cycle = .T.
                                * generate sql syntax
                                strWhere=LEFT(strWhere,LEN(strWhere)-4)
                                IF EMPTY(strSet)
                                        strSql="Delete from employee where "+ strWhere
                                ELSE
                                        strSet=LEFT(strSet,LEN(strSet)-1)
                                        strSql="Update employee set " + strSet + " where " + strWhere
                                ENDIF

                                * call sqlexec() to send update-sql
                                * and we can update our database
                                ln_ret=SQLEXEC(handle,strSql)
                                l_cycle=.F.
                                strWhere=""
                                strSet=""
                        ENDIF
                NEXT
        NEXT

        * disconnect    
        =sqldisconnect(handle)
ENDPROC
ENDDEFINE


這段程式碼裡面包含了一個類別，類別裡面包含了兩個方法：

getAllEmployee 利用 SPT 這組函數取得所有員工的員工編號、姓、名以及職稱，然後再利用 CURSORTOXML() 函數將 Cursor 資料轉換為 XML並傳回。

UpdateEmployee 則依據傳來的 update xml 資料作分析並更新。

再依照之前建立 Web Service 的步驟再做一次 Build COM 元件以及發布的動作。

接著，讓我們來撰寫 client 端的畫面，請依照下圖放置一個 Grid 控制項還有兩個 Button 控制項，然後同樣地，將下列的程式碼分別置放到兩個按鈕的 onclick 事件中。

 

在 Retrieve 按鈕的 onclick 事件中置放如下程式碼：


LOCAL x as Object , lc_xml as String 

*因為在[Update]按鈕中的 xmlupdategram() 函數需要設製 buffermode 並將 multilocks 設為 True
SET MULTILOCKS ON 
IF USED("employee")
        thisform.grid1.RecordSource=""
        USE IN employee
ENDIF

x = CREATEOBJECT("MSSOAP.SoapClient")
x.MSSoapInit("http://ellery/webservice/tastrade.WSDL")
lc_xml=x.getAllEmployee( ) 
XMLToCursor(lc_xml,"employee")
thisform.grid1.RecordSource="employee"
thisform.Refresh()

SELECT employee
=CURSORSETPROP("buffering",5)


在 Update 按鈕的 onclick 事件中置放：

LOCAL lc_ret
LOCAL x, lc_xml

*檢查是否已經取得資料
IF NOT USED("employee")
        =MESSAGEBOX("[Retrieve] button not pressed!")
        RETURN
ENDIF

*產生異動的 xml
lc_ret=XMLUPDATEGRAM("employee")
IF EMPTY(lc_ret)    &&如果沒有異動,發出警告
        =MESSAGEBOX("You don't update any thing")
ENDIF
=MESSAGEBOX(lc_ret,"XMLUPDATEGRAM() generates following codes")

*將資料更新回去!
x = CREATEOBJECT("MSSOAP.SoapClient")
x.MSSoapInit("http://ellery/webservice/tastrade.WSDL")
x.updateEmployee( lc_ret )
SELECT employee


然後將這個表單命名為 form2，然後讓我們執行這個表單。

當我們按下 Retrieve 時，這個 client 端的表單就會為我們取回 Server 端的XML資料，並利用 XMLTOCURSOR() 轉換為 Cursor，以便能順利與 VFP 的控制項結合。

當按下更新的時候，我們也能利用 XMLUPDATEGRAM() 函數取得需要更新的必要資料，送回 Server 端，讓 Service 端能根據這份資料來更新資料。


結論
VFP 在7.0版之後就不再被包含在 Visual Studio.NET 之中，但是，VFP開發小組承諾會繼續為這個產品添加更多的功能以迎合使用者的需求。就我個人而言，對 VFP 仍懷有一份特殊的情感，尤其是在使用其他語言開發資料庫時，有時候仍然會忍不住有 “唉!怎麼沒有這個功能，VFP就有” 這個想法，所以我還是會繼續看相關的 VFP 資料。
不過，我感覺到微軟雖然對 VFP 的使用者作出了相當的承諾，可是，從 5.0 到 6.0 到 7.0 ，除了使用者介面與開發COM元件的支援外，其實並沒有什麼太大的改進，我想很多人也會和我有一樣的看法吧。


參考資料：
MSDN - Walkthrough: Creating Web Services with Visual FoxPro
RUN!PC 90 期
XML 網頁製作徹底研究 旗標出版
	]]>
	</description>
	<content:encoded><![CDATA[
	<a href="#overview">前言</a><br />
<a href="#createws">建立Web Service</a><br />
<a href="#wsclient">建立Web Service Client 端程式</a><br />
<a href="#otherws">使用其他語言建立的 Web Service</a><br />
<a href="#databind">與資料庫結合</a><br />
<a href="#conclution">結論</a><br />
<a href="#referencedata">參考資料</a><br />
<br />
<a name="overview"></a>前言<br />
VFP 這項產品一直被謠傳微軟將不再支援，但在今年六月，微軟將VFP7自Visual Studio.NET中獨立出來銷售，總算是打破了這個謠言。此外，也為這項產品添加了不少功能，比如 Intellisense、對COM的更完整支援、SOAP與Web Service 等等。這裡就為各位解說新增的SOAP與Web Service功能。<br />
<br />
SOAP 與 Web Service 這兩個名詞，相信我不用再多做解釋了，大家在這一陣子的耳濡目染之下，應該都大致了解，在此就不再贅述。如果想了解的人請參考前幾期RUN!PC李維老師的文章，有詳細的解說。<br />
<br />
<br />
<a name="createws"></a><br />
建立Web Service<br />
首先讓我們來製作 Web Service，稍後，再來撰寫如何使用 Web Service 的 Client 端程式。<br />
要使用VFP7製作Web Service之前，請先在[Internet 服務管理員]中，新增一個虛擬目錄，在此，我們先命名為 WebServices。<br />
接下來得製作一個COM元件，在這裡我們就以一個簡單的相加函數來作為一個範例。<br />
請先建立一個新專案，名稱叫做MyWebService，在Project Manager(專案管理員)中，切換到 Program頁籤，點選 ”New”，將下列這段程式碼貼到裡面去，然後存檔為 prog1.prg。<br />
<pre><br />
DEFINE CLASS MyCalculator AS session OLEPUBLIC<br />
Name = "MyCalculator"<br />
PROCEDURE doAdd<br />
                Parameters A , B<br />
        Return A+B<br />
ENDPROC<br />
ENDDEFINE<br />
</pre><br />
<br />
這一段程式碼只是將傳入的兩個參數做一個簡單的相加而已。<br />
接著就要進行編譯的工作了，點選”Build”，建立的型態選為”Single-threaded COM Server(dll)”，按下 ”確定” 來建立一個dll型態的COM伺服器。<br />
<br />
<img alt="image002.jpg" src="images/vfp7ws_image002.jpg" border="0" /><br />
<br />
建立完成後，你會發現VFP7為你產生了一個mywebservice.dll，並已經為你註冊到電腦中了，讓我們驗證一下VFP7是否已經編譯成功，請在命令列中依序輸入：<br />
<br />
<pre><br />
obj=createobject(“MyWebService.MyCalculator”)<br />
? obj.doAdd(100,100)<br />
</pre><br />
<br />
如果成功的話，你應該會看到VFP7回應了 200 這個答案，表示COM伺服器已經建立成功。<br />
<br />
VFP7提供了一個方便的精靈，讓你可以直接在VFP7的整合環境中建立 Web Service，而不需要另外去執行 SOAP toolkit 來製作。<br />
<br />
請你在Project Manager中，以滑鼠右鍵點選叫出快捷選單，選擇Builder。此時，會出現一個選擇精靈的對話方盒(如圖)，選擇Web Services Publisher，並按下確定。<br />
<br />
<img alt="image004.jpg" src="images/vfp7ws_image004.jpg" border="0" /><br />
<br />
接著會出現Web Services Publisher這個對話盒(如圖)，我們按下 “…” 按鈕，去搜尋我們剛剛建立的MyWebService.MyCalculator COM伺服器。找到並確定之後，再按下”Advanced”，讓我們看看可以再多做些什麼設定。<br />
<br />
在 Advanced 設定中，我們可以指定<br />
<ol><br />
<li>WSDL 產出的位置。</li><br />
<li>SOAP Listener型態。</li><br />
<li>IntelliSense的Script名稱。</li><br />
<li>告訴VFP7在Project Build之後，是否自動產出相關的 Web Service 檔案。</li><br />
<li>產出的WSDL檔案是否要以UTF-16 Unicode作為預設編碼。<br />
<img alt="image006.jpg" src="images/vfp7ws_image006.jpg" border="0" /><br />
<img alt="image008.jpg" src="images/vfp7ws_image08.jpg" border="0" /><br />
<img alt="image010.jpg" src="images/vfp7ws_image010.jpg" border="0" /><br />
<img alt="image012.jpg" src="images/vfp7ws_image012.jpg" border="0" /><br />
</li><br />
</ol><br />
<br />
在確定所有設定無誤之後，按下 Generate，Web Service Publish精靈就會利用SOAP toolkit為我們在指定的位置產出Web Service的相關檔案。<br />
<br />
<a name="wsclient"></a><br />
Web Service Client 程式<br />
那麼，我們要如何使用這個 Web Service 呢?<br />
讓我們寫一個簡單的 form 來呼叫<br />
<br />
請開一個新檔案，類型選為Form (表單)，在表單裡面放置一個按鈕(如下圖)，在按鈕的OnClick事件內放置如下的程式碼：<br />
<br />
<pre><br />
x = CREATEOBJECT("MSSOAP.SoapClient")<br />
x.MSSoapInit("http://ellery/webservices/MyCalculator.WSDL", , "MyCalculatorSoapPort")<br />
<br />
=messagebox( str( x.doAdd( 100,100 ) ) )<br />
</pre><br />
<br />
執行這個表單，在click按鈕之後，你會發現你並沒有辦法呼叫!!是VFP7的bug嗎??<br />
<br />
首先,因為soap物件會利用 wsdl 檔案做 Initialization 的動作，所以我檢查了一下產出的 wsdl：<br />
<br />
<pre><br />
…略…<br />
  &lt;message name='MyCalculator.doAdd'&gt;<br />
  &lt;/message&gt;<br />
  &lt;message name='MyCalculator.doAddResponse'&gt;<br />
    &lt;part name='Result' type='xsd:anyType'/&gt;<br />
  &lt;/message&gt;<br />
…略…<br />
</pre><br />
<br />
在這裡 SOAP toolkit wizard 並沒有為我們的 COM 元件產生參數的宣告。<br />
產出的 WSDL 碼應該是下面這樣子才對的呀.<br />
<br />
<pre><br />
…略…<br />
  &lt;message name='MyCalculator.doAdd'&gt;<br />
    &lt;part name='A' type='xsd:int'/&gt;<br />
    &lt;part name='B' type='xsd:int'/&gt;<br />
  &lt;/message&gt;<br />
<br />
  &lt;message name='MyCalculator.doAddResponse'&gt;<br />
    &lt;part name='Result' type='xsd:anyType'/&gt;<br />
  &lt;/message&gt;<br />
…略…<br />
</pre><br />
<br />
為了這個疑惑，我將問題貼到 Microsoft Newsgroup(http://communities.microsoft.com/newsgroups/default.asp) 和 Universal Thread(http://www.universalthread.com) 網站去詢問，過了兩天，就有了結果，兩個地方給我的回答都是相同的，我們必須將PROCEDURE doAdd 的宣告修改為<br />
<pre><br />
PROCEDURE doAdd(A as Integer, B as Integer)<br />
</pre><br />
並把 parameters 敘述移除。<br />
<br />
修改後，重新 Build、利用 Wizard 發布之後，再執行剛剛我們建立好的 client 端Form，你應該就可以看到Web Service 被起始後的結果了。<br />
<br />
<a name="otherws"></a><br />
使用其他語言建立的web service<br />
為了驗證一下，VFP也能使用其他語言或其他平台上建立的 Web Service，我們仿效李維老師於RUN!PC 90 期文章中所寫的範例，寫一個取得溫度的簡單範例。<br />
<br />
請依照下圖來製作一個表單：<br />
<img alt="image014.jpg" src="images/vfp7ws_image014.jpg" border="0" /><br />
 <br />
然後，在”取得溫度”按鈕的 onclick 事件中，置入下列程式碼：<br />
<pre><br />
LOCAL obj<br />
obj = CREATEOBJECT("MSSOAP.SoapClient")<br />
obj.MSSoapInit("http://www.xmethods.net/sd/2001/TemperatureService.wsdl")<br />
thisform.text2.value=obj.getTemp(thisform.text1.value)<br />
</pre><br />
<br />
在執行這個表單之後，在州代號的欄位中，輸入 07060，再按下 “取得溫度” 按鈕，下面的當地氣溫欄位便顯示出氣溫，表示可以正確的呼叫!<br />
<img alt="image016.jpg" src="images/vfp7ws_image016.jpg" border="0" /><br />
 <br />
<a name="databind"></a><br />
與資料庫結合<br />
<br />
VFP為了因應 XML 的流行，在這個版本中添加了與XML 相關的三個函數：CURSORTOXML()、XMLTOCURSOR()、XMLUPDATEGRAM()，正好可以用來在 Web Services 中傳遞資料。<br />
<br />
請在Project Manager中，新增一個程式檔(program)，然後將下列的程式碼加入，並命名為 prog2.prg：<br />
<pre><br />
DEFINE CLASS tastrade AS session OLEPUBLIC<br />
Name = "tastrade"<br />
PROCEDURE getAllEmployee()<br />
        LOCAL handle as Integer <br />
        LOCAL lc_xml as String <br />
<br />
        handle=SQLSTRINGCONNECT("Driver=Microsoft Visual FoxPro Driver;” + ;<br />
		“UID=;PWD=;” + ;<br />
		“SourceDB=C:\WebService\Data\testdata.dbc;” + ;<br />
		“SourceType=DBC;Exclusive=No;BackgroundFetch=Yes;” + ;<br />
		“Collate=Machine;Null=Yes;Deleted=Yes;")<br />
        =SQLEXEC(handle, ;<br />
		"select emp_id, last_name, first_name, title from employee order by emp_id", ;<br />
		"employees")<br />
        =sqldisconnect(handle)<br />
        CursorToXML("employees", "lc_xml")<br />
        RETURN lc_xml<br />
ENDPROC<br />
<br />
PROCEDURE updateEmployee( c_request as string )<br />
        LOCAL handle<br />
        LOCAL o_xmlDom, o_nodelist1, o_nodelist2, o_nodeEmployee, o_node<br />
        LOCAL strWhere as String , strSet as String , strSql as String <br />
        LOCAL ll_cycle as Boolean , ln_ret as Integer <br />
<br />
        c_request=STRTRAN( c_request, 'encoding="Windows-1252" ', "" )<br />
        o_xmlDom=CreateObject("Microsoft.XMLDOM")<br />
        o_xmlDom.async="false"<br />
        o_xmlDom.loadXML(c_request)<br />
<br />
        * parse xml and combine update-sql syntax<br />
        IF o_xmlDom.hasChildNodes()<br />
        ELSE<br />
                RETURN<br />
        ENDIF<br />
<br />
        * create connection<br />
        handle=SQLSTRINGCONNECT("Driver=Microsoft Visual FoxPro Driver;” + ;<br />
	“UID=;PWD=;” + ;<br />
	“SourceDB=C:\WebService\Data\testdata.dbc;” + ;<br />
	“SourceType=DBC;Exclusive=No;BackgroundFetch=Yes;” + ;<br />
	“Collate=Machine;Null=Yes;Deleted=Yes;")<br />
        strWhere=""<br />
        strSet=""<br />
        l_cycle=.F.<br />
<br />
        for each o_nodelist1 in o_xmlDom.documentElement.childNodes<br />
                FOR EACH o_nodelist2 IN o_nodelist1.childNodes<br />
                        * initialize variables<br />
                        if o_nodelist2.nodeName="updg:before"<br />
                                FOR EACH o_nodeEmployee IN o_nodelist2.childNodes<br />
                                        FOR EACH o_node IN o_nodeEmployee.childNodes<br />
                                                IF o_Node.nodeName="emp_id"<br />
                                                        strWhere=strWhere+o_Node.nodeName+"="+"'" + ;<br />
								TRANSFORM(o_Node.text,"@J 999999") + "' and "<br />
                                                ELSE<br />
                                                        strWhere=strWhere+o_Node.nodeName+"="+"'" + ;<br />
								o_Node.text + "' and "<br />
                                                ENDIF<br />
                                        NEXT<br />
                                NEXT<br />
                        ENDIF<br />
                        IF o_nodelist2.nodeName="updg:after"<br />
                                IF NOT o_nodelist2.hasChildNodes()<br />
                                ELSE<br />
                                        FOR EACH o_nodeEmployee IN o_nodelist2.childNodes<br />
                                                FOR EACH o_node IN o_nodeEmployee.childNodes<br />
                                                        IF o_Node.nodeName="emp_id"<br />
                                                        strSet=strSet+o_Node.nodeName+"="+"'" + ;<br />
								TRANSFORM(o_Node.text,"@J 999999") + "',"<br />
                                                        ELSE<br />
                                                                strSet=strSet+o_Node.nodeName+"="+"'" + o_Node.text + "',"<br />
                                                        ENDIF<br />
                                                NEXT<br />
                                        NEXT<br />
                                ENDIF<br />
                                l_cycle=.T.<br />
                        ENDIF<br />
                        IF l_cycle = .T.<br />
                                * generate sql syntax<br />
                                strWhere=LEFT(strWhere,LEN(strWhere)-4)<br />
                                IF EMPTY(strSet)<br />
                                        strSql="Delete from employee where "+ strWhere<br />
                                ELSE<br />
                                        strSet=LEFT(strSet,LEN(strSet)-1)<br />
                                        strSql="Update employee set " + strSet + " where " + strWhere<br />
                                ENDIF<br />
<br />
                                * call sqlexec() to send update-sql<br />
                                * and we can update our database<br />
                                ln_ret=SQLEXEC(handle,strSql)<br />
                                l_cycle=.F.<br />
                                strWhere=""<br />
                                strSet=""<br />
                        ENDIF<br />
                NEXT<br />
        NEXT<br />
<br />
        * disconnect    <br />
        =sqldisconnect(handle)<br />
ENDPROC<br />
ENDDEFINE<br />
</pre><br />
<br />
這段程式碼裡面包含了一個類別，類別裡面包含了兩個方法：<br />
<br />
getAllEmployee 利用 SPT 這組函數取得所有員工的員工編號、姓、名以及職稱，然後再利用 CURSORTOXML() 函數將 Cursor 資料轉換為 XML並傳回。<br />
<br />
UpdateEmployee 則依據傳來的 update xml 資料作分析並更新。<br />
<br />
再依照之前建立 Web Service 的步驟再做一次 Build COM 元件以及發布的動作。<br />
<br />
接著，讓我們來撰寫 client 端的畫面，請依照下圖放置一個 Grid 控制項還有兩個 Button 控制項，然後同樣地，將下列的程式碼分別置放到兩個按鈕的 onclick 事件中。<br />
<br />
<img alt="image017.jpg" src="images/vfp7ws_image017.jpg" border="0" /> <br />
<br />
在 Retrieve 按鈕的 onclick 事件中置放如下程式碼：<br />
<br />
<pre><br />
LOCAL x as Object , lc_xml as String <br />
<br />
*因為在[Update]按鈕中的 xmlupdategram() 函數需要設製 buffermode 並將 multilocks 設為 True<br />
SET MULTILOCKS ON <br />
IF USED("employee")<br />
        thisform.grid1.RecordSource=""<br />
        USE IN employee<br />
ENDIF<br />
<br />
x = CREATEOBJECT("MSSOAP.SoapClient")<br />
x.MSSoapInit("http://ellery/webservice/tastrade.WSDL")<br />
lc_xml=x.getAllEmployee( ) <br />
XMLToCursor(lc_xml,"employee")<br />
thisform.grid1.RecordSource="employee"<br />
thisform.Refresh()<br />
<br />
SELECT employee<br />
=CURSORSETPROP("buffering",5)<br />
</pre><br />
<br />
在 Update 按鈕的 onclick 事件中置放：<br />
<pre><br />
LOCAL lc_ret<br />
LOCAL x, lc_xml<br />
<br />
*檢查是否已經取得資料<br />
IF NOT USED("employee")<br />
        =MESSAGEBOX("[Retrieve] button not pressed!")<br />
        RETURN<br />
ENDIF<br />
<br />
*產生異動的 xml<br />
lc_ret=XMLUPDATEGRAM("employee")<br />
IF EMPTY(lc_ret)    &&如果沒有異動,發出警告<br />
        =MESSAGEBOX("You don't update any thing")<br />
ENDIF<br />
=MESSAGEBOX(lc_ret,"XMLUPDATEGRAM() generates following codes")<br />
<br />
*將資料更新回去!<br />
x = CREATEOBJECT("MSSOAP.SoapClient")<br />
x.MSSoapInit("http://ellery/webservice/tastrade.WSDL")<br />
x.updateEmployee( lc_ret )<br />
SELECT employee<br />
</pre><br />
<br />
然後將這個表單命名為 form2，然後讓我們執行這個表單。<br />
<br />
當我們按下 Retrieve 時，這個 client 端的表單就會為我們取回 Server 端的XML資料，並利用 XMLTOCURSOR() 轉換為 Cursor，以便能順利與 VFP 的控制項結合。<br />
<br />
當按下更新的時候，我們也能利用 XMLUPDATEGRAM() 函數取得需要更新的必要資料，送回 Server 端，讓 Service 端能根據這份資料來更新資料。<br />
<br />
<a href="conclution"></a><br />
結論<br />
VFP 在7.0版之後就不再被包含在 Visual Studio.NET 之中，但是，VFP開發小組承諾會繼續為這個產品添加更多的功能以迎合使用者的需求。就我個人而言，對 VFP 仍懷有一份特殊的情感，尤其是在使用其他語言開發資料庫時，有時候仍然會忍不住有 “唉!怎麼沒有這個功能，VFP就有” 這個想法，所以我還是會繼續看相關的 VFP 資料。<br />
不過，我感覺到微軟雖然對 VFP 的使用者作出了相當的承諾，可是，從 5.0 到 6.0 到 7.0 ，除了使用者介面與開發COM元件的支援外，其實並沒有什麼太大的改進，我想很多人也會和我有一樣的看法吧。<br />
<br />
<a name="referencedata"></a><br />
參考資料：<br />
MSDN - Walkthrough: Creating Web Services with Visual FoxPro<br />
RUN!PC 90 期<br />
XML 網頁製作徹底研究 旗標出版		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553841.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553841.html</guid>
	<category>Visual FoxPro</category>
	<pubDate>Fri, 23 Aug 2002 22:09:51 +0800</pubDate>
</item>
<item>
	<title>VFP7研討會後報告</title>
	<description><![CDATA[
	我想這個研討會對各位應該是沒有什麼關係才對~~
為了避免浪費大家的時間,我就在這裡做個簡短報告,如果有興趣摸摸看的話,再私下來找我~

首先我假設各位已經使用過 6.0 了~
7.0 與 6.0 其實差別性不大,幾個重點:
1. UI 加強,以前沒有 IntelliSense,現在有了,什麼是 IntelliSense, 簡單的說,就是像VB一樣,我隨便打個 rsCustomer. (假設rsCustomer是adodb.recordset),VB 就會很貼心的跑出一狗票方法跟屬性讓你選.
2. COM 加強,之前的版本建立 COM 物件,實際上並沒有實做出所有應有的 Interface,以及建立標準的 IDL (Interface Definition Language),所以在某些特定的情況有問題.現在除了遵循標準之外,也更加強了控制 COM 物件的能力.
3. XML 與 Web Service, 提供了三個函數來支援 XML 與 Cursor 之間的轉換,並且多了一個精靈來產生 Web Service,這個精靈實際上是應用 SOAP ToolKit 2.0 sp2 來產生 Web Service 的.
4. Database 提供 Event 以及 OLEDB Provider, 這個我認為不是很重要,因為現在用 vfp 資料庫的人越來越少了.

說完以上幾點,大概就沒什麼了,也因此,這次的研討論,前面廢話一陣之後,直接就介紹 XML 的三個函數與 Web Service 精靈,介紹的範例寫的不錯,令人能一目了然.

會畢,大家提出一些問題~有兩個問題是讓我比較印象深刻的
1.關於 Microsoft 是否繼續推廣 VFP 的問題,基本上,我覺得問的人很笨,因為上課的人並不是微軟的人,就算是,他也沒辦法回答.我個人的答案是,目前桌上型資料庫的市場已經是 VFP 跟 ACCESS 獨大了,所有的敵人都已經死光光(Paradox,dBase,Clipper...等),他根本不需要再推廣,否則等於是拿石頭砸自己的腳.
2.有個傢伙問售後服務,他說能不能主動寄發 Service Pack 給客戶,或者在購買VFP的時候,能主動附上目前最新的 Service Pack. 否則的話,光華賣的補帖服務還比微軟好,因為補帖出貨的時候,連 Service Pack 都燒進去.那幹?還用正版.聽到這裡,深深覺得售後服務的重要性.

會後,填完問卷,還領了一張資源光碟,我還沒有看,待會我會放到 Server 上去.

報告完畢.
	]]>
	</description>
	<content:encoded><![CDATA[
	我想這個研討會對各位應該是沒有什麼關係才對~~<br />
為了避免浪費大家的時間,我就在這裡做個簡短報告,如果有興趣摸摸看的話,再私下來找我~<br />
<br />
首先我假設各位已經使用過 6.0 了~<br />
7.0 與 6.0 其實差別性不大,幾個重點:<br />
1. UI 加強,以前沒有 IntelliSense,現在有了,什麼是 IntelliSense, 簡單的說,就是像VB一樣,我隨便打個 rsCustomer. (假設rsCustomer是adodb.recordset),VB 就會很貼心的跑出一狗票方法跟屬性讓你選.<br />
2. COM 加強,之前的版本建立 COM 物件,實際上並沒有實做出所有應有的 Interface,以及建立標準的 IDL (Interface Definition Language),所以在某些特定的情況有問題.現在除了遵循標準之外,也更加強了控制 COM 物件的能力.<br />
3. XML 與 Web Service, 提供了三個函數來支援 XML 與 Cursor 之間的轉換,並且多了一個精靈來產生 Web Service,這個精靈實際上是應用 SOAP ToolKit 2.0 sp2 來產生 Web Service 的.<br />
4. Database 提供 Event 以及 OLEDB Provider, 這個我認為不是很重要,因為現在用 vfp 資料庫的人越來越少了.<br />
<br />
說完以上幾點,大概就沒什麼了,也因此,這次的研討論,前面廢話一陣之後,直接就介紹 XML 的三個函數與 Web Service 精靈,介紹的範例寫的不錯,令人能一目了然.<br />
<br />
會畢,大家提出一些問題~有兩個問題是讓我比較印象深刻的<br />
1.關於 Microsoft 是否繼續推廣 VFP 的問題,基本上,我覺得問的人很笨,因為上課的人並不是微軟的人,就算是,他也沒辦法回答.我個人的答案是,目前桌上型資料庫的市場已經是 VFP 跟 ACCESS 獨大了,所有的敵人都已經死光光(Paradox,dBase,Clipper...等),他根本不需要再推廣,否則等於是拿石頭砸自己的腳.<br />
2.有個傢伙問售後服務,他說能不能主動寄發 Service Pack 給客戶,或者在購買VFP的時候,能主動附上目前最新的 Service Pack. 否則的話,光華賣的補帖服務還比微軟好,因為補帖出貨的時候,連 Service Pack 都燒進去.那幹?還用正版.聽到這裡,深深覺得售後服務的重要性.<br />
<br />
會後,填完問卷,還領了一張資源光碟,我還沒有看,待會我會放到 Server 上去.<br />
<br />
報告完畢.		]]>
	</content:encoded>
	<link>http://blog.roodo.com/thinkingmore/archives/553818.html</link>
	<guid>http://blog.roodo.com/thinkingmore/archives/553818.html</guid>
	<category>Visual FoxPro</category>
	<pubDate>Thu, 13 Dec 2001 17:02:40 +0800</pubDate>
</item>
<item>
	<title>VFP與SQL Server的連結與最佳化</title>
	<description><![CDATA[
	摘要:介紹VFP與SQL Server連結的最佳化設定
內容:
建置測試環境
  測試
  FetchAsNeeded?? 
點選這裡來下載本文章的範例程式
建置測試環境
1.請先利用 sql server enterprise manager去建立一個 database,叫做demo
  然後利用 Query Analyzer 執行以下的sql script
  use demo
  if exists (select * from sysobjects where id = object_id(N'[dbo].[demo]') and 
  OBJECTPROPERTY(id, N'IsUserTable') = 1)
  drop table [dbo].[demo]
  GO
  
  CREATE TABLE [dbo].[demo] (
  [cus_no] [int] IDENTITY (1, 1) NOT NULL ,
  [cus_name] [char] (10) NOT NULL ,
  [comment] [text] NULL 
  ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
  GO
  
  ALTER TABLE [dbo].[demo] WITH NOCHECK ADD 
  CONSTRAINT [PK_demo] PRIMARY KEY NONCLUSTERED 
  (
  [cus_no]
  ) ON [PRIMARY] 
  GO
  
  
  2.請再設定一下控制台中的ODBC
  
  3.設定一下 class 裡面的 demo.vcx 中的 prjhook QueryRunFile Event 中的路徑
  然後在專案上面點一下滑鼠右鍵
  選擇 專案資訊,接著把專案類別設為 demo.vcx 
  然後以後你執行程式的時候,就只要按 執行 就好了
  
  4.接著
  去 變更一下 database 中的 connection
  

測試
  
  以程式動態模擬 1000 筆資料寫入 sql server 資料庫中
  直接利用 vfp 的 connection + remote view
  硬體: cyrix 200 + 128 mb ram + 4g hd
  軟體: m$ sql server 7 + ado 2.6
情況 1 :
  不設 buffering
  vfp 當然是每新增一筆, 就會寫一筆到 sql server 中
  想當然耳,速度超慢
情況 2 :
  設定 buffering 為 5
  這個時候,新增就粉快了,因為新增的資料都是放到 local 端
  不過 tableupdate 的時候,就超慢了
情況 3 :
  設定 buffering 為 5, batchupdatecount 為 1000, prepared 為 true
  第一次新增 1000 筆,tableupdate 寫入,僅僅花了二分鐘多左右
  效率還算讓人滿意
  在程式執行的同時,我也有利用 sql 的 profiler 去觀察
  vfp的確有先產生 prepare 的指令, 再執行 insert 的動作
  仔細看了一下 tableupdate 的指令說明
  tableupdate 的第一個參數如果帶 1, 而且 buffering 有開啟的時候,就會只把有變動的記錄更新回去
  這一點我呆會會再另外用另外一種方法來測試是否真的有這麼去tableupdate
  第二次執行的時候,花了約四分鐘,這次我發現vfp有多花一些時間在requery()上,所以我把 use demo 改為 use demo nodata
  果然就又回復到約二分鐘多左右了
  這次我把 prepare 拿掉試試看
  發現了很有趣的現象,profiler 中,仍然使用了 prepare 
  不知道是不是因為 odbc driver 的關係
第四次(prog2)
  測試 requery() 的速度
  我知道memo的大小會影響速度,所以直接先把 fetchmemo 設為 false
  程式很快就run完了,我感到質疑
  因此我用browse去看
  我發現 vfp 會 requery() 一個程度之後,開始在background去抓資料
  如果這個時候去browse,速度就變的粉慢
  怎麼變快??
  於是我接著把 FetchAsNeeded 設為 True, FetchSize 設為 100
  重新執行, 果然速度變快很多,因為vfp只抓了 100 筆
  我利用 browse 去觀察的時候,向下 page down, vfp 還會自動往下抓100筆記錄
  這真是太棒了
  不過我後來直接把 scroll bar 移到最下面,這個requery的時間就很久了


  第五次(form1)
  我寫了一個簡單的表單
  寫的過程,遇到一個小問題,我在dataenvironment中拉入view,接著把view拖到表單上
  產生grid的時候粉慢,這時候我建議各位先在指令列下 use demo nodata, 然後再來拖
  會順利很多!!
  做好的畫面如form1
  畫面很簡單
  右上角是我測試的重點之一,我輸入一個數值,按下&quot;跳到&quot;,會自動移到該筆記錄去
  當我 go 100 的時候,果然vfp會自動抓下一百筆資料
  另外當我點選到 memo 欄位時,vfp也會自動去抓取該欄位的memo值
  (因為之前把fetchmemo設為false,所以不會抓memo值)
  不過這時候又產生了另外一個問題
  如果我想知道總共有多少記錄的時候要怎麼辦呢??雖然輔助說明中說requery()之後,
  會把取得的記錄數目存放在_tally變數中,but似乎不是這樣,因為我取得的_tally總是與事實不符
  翻遍輔助說明,看來都沒有適合的指令可以取得
  只好利用 select count() 了
  我把這段指令寫在 &quot;總筆數!!&quot; 這個按鈕上
  千萬不要用 reccount() ,這會把所有資料 dl 下來!!

FetchAsNeeded??
  第六次 (form1)
  這次我們要試的是 tableupdate 是否真的只把我們有更動的資料寫回去
  因此我們使用 tableupdate( 1, .T. ) 來作
  可是粉奇怪,profiler 並沒有顯示任何 update 的動作
  這太奇怪了~~
  我搞了好久
  才在 vfp 的 programmer's guide 找到
  當 fetchasneeded 設為 true 的時候,是無法做任何更新動作的!!!!
  必須要先 sqlcancel 之後才能去做
  最後實驗 tableupdate( 1, .T. )也順利成功
  程式碼在[奇數碼更新]裡面!!
  vfp 果然會只更新有變動的部分而已!!!
	]]>
	</description>
	<content:encoded><![CDATA[
	<p>摘要:介紹VFP與SQL Server連結的最佳化設定</p>
<p>內容:<br/>
<p><a href="#HowToBuild">建置測試環境</a><br/>
  <a href="#Test">測試</a><br/>
  <a href="#FetchAsNeeded">FetchAsNeeded??</a> </p>
<p><a href="files/spt_research_demo.zip">點選這裡來下載本文章的範例程式</a></p>
<a name="HowToBuild"></a><b>建置測試環境</b>
<p>1.請先利用 sql server enterprise manager去建立一個 database,叫做demo<br/>
  然後利用 Query Analyzer 執行以下的sql script<br/>
  <span class="codes">use demo<br/>
  if exists (select * from sysobjects where id = objec