跳到主要內容

在DOS下,進入保護模式(Protect mode)並跳回真實模式

若程式越來越大,並超過512個位元組,則難以在開機磁區執行。

解決方法至少有兩種:

  1. 寫一個開機磁區,可讀取程式並執行它,就好像這程式是個作業系統核心似的
  2. 借助別的東西,例如 DOS。我們可將程式編譯為 COM 檔,讓 DOS 執行它。
這裡採用第二種,以達到目的。

預先準備

  1. 下載 FreeDos,改名為 freedos.img
  2. 用 bximage 產成一個軟碟映像檔(floopy image),命名為 pm.img
  3. 若第一次使用 pm.img,可利用 freedos.img 開機後,格式化 pm.img (ex, format b:)

程式碼

pm.inc

; 描述符圖示

; 圖示一
;
;  ------ ┏━━┳━━┓高地址
;         ┃ 7  ┃ 段 ┃
;         ┣━━┫    ┃
;                  基
;  字節 7 ┆    ┆    ┆
;                  址
;         ┣━━┫ ② ┃
;         ┃ 0  ┃    ┃
;  ------ ┣━━╋━━┫
;         ┃ 7  ┃ G  ┃
;         ┣━━╉──┨
;         ┃ 6  ┃ D  ┃
;         ┣━━╉──┨
;         ┃ 5  ┃ 0  ┃
;         ┣━━╉──┨
;         ┃ 4  ┃ AVL┃
;  字節 6 ┣━━╉──┨
;         ┃ 3  ┃    ┃
;         ┣━━┫ 段 ┃
;         ┃ 2  ┃ 界 ┃
;         ┣━━┫ 限 ┃
;         ┃ 1  ┃    ┃
;         ┣━━┫ ② ┃
;         ┃ 0  ┃    ┃
;  ------ ┣━━╋━━┫
;         ┃ 7  ┃ P  ┃
;         ┣━━╉──┨
;         ┃ 6  ┃    ┃
;         ┣━━┫ DPL┃
;         ┃ 5  ┃    ┃
;         ┣━━╉──┨
;         ┃ 4  ┃ S  ┃
;  字節 5 ┣━━╉──┨
;         ┃ 3  ┃    ┃
;         ┣━━┫ T  ┃
;         ┃ 2  ┃ Y  ┃
;         ┣━━┫ P  ┃
;         ┃ 1  ┃ E  ┃
;         ┣━━┫    ┃
;         ┃ 0  ┃    ┃
;  ------ ┣━━╋━━┫
;         ┃ 23 ┃    ┃
;         ┣━━┫    ┃
;         ┃ 22 ┃    ┃
;         ┣━━┫ 段 ┃
;
;   字節  ┆    ┆ 基 ┆
; 2, 3, 4
;         ┣━━┫ 址 ┃
;         ┃ 1  ┃ ① ┃
;         ┣━━┫    ┃
;         ┃ 0  ┃    ┃
;  ------ ┣━━╋━━┫
;         ┃ 15 ┃    ┃
;         ┣━━┫    ┃
;         ┃ 14 ┃    ┃
;         ┣━━┫ 段 ┃
;
; 字節 0,1┆    ┆ 界 ┆
;
;         ┣━━┫ 限 ┃
;         ┃ 1  ┃ ① ┃
;         ┣━━┫    ┃
;         ┃ 0  ┃    ┃
;  ------ ┗━━┻━━┛低地址
;


; 圖示二

; 高地址………………………………………………………………………低地址

; |   7   |   6   |   5   |   4   |   3   |   2   |   1   |   0    |
; |7654321076543210765432107654321076543210765432107654321076543210| <- 共 8 字節
; |--------========--------========--------========--------========|
; ┏━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━┓
; ┃31..24┃   (見下圖)   ┃     段基址(23..0)    ┃ 段界限(15..0)┃
; ┃      ┃              ┃                      ┃              ┃
; ┃ 基址2┃③│②│    ①┃基址1b│   基址1a     ┃    段界限1   ┃
; ┣━━━╋━━━┳━━━╋━━━━━━━━━━━╋━━━━━━━┫
; ┃   %6 ┃  %5  ┃  %4  ┃  %3  ┃     %2       ┃       %1     ┃
; ┗━━━┻━━━┻━━━┻━━━┻━━━━━━━┻━━━━━━━┛
;         │                \_________
;         │                          \__________________
;         │                                             \________________________________________________
;         │                                                                                              \
;         ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
;         ┃ 7  ┃ 6  ┃ 5  ┃ 4  ┃ 3  ┃ 2  ┃ 1  ┃ 0  ┃ 7  ┃ 6  ┃ 5  ┃ 4  ┃ 3  ┃ 2  ┃ 1  ┃ 0  ┃
;         ┣━━╋━━╋━━╋━━╋━━┻━━┻━━┻━━╋━━╋━━┻━━╋━━╋━━┻━━┻━━┻━━┫
;         ┃ G  ┃D/B ┃ 0  ┃ AVL┃   段界限 2 (19..16)  ┃  P ┃   DPL    ┃ S  ┃       TYPE           ┃
;         ┣━━┻━━┻━━┻━━╋━━━━━━━━━━━╋━━┻━━━━━┻━━┻━━━━━━━━━━━┫
;         ┃      ③: 屬性 2      ┃    ②: 段界限 2      ┃                   ①: 屬性1                  ┃
;         ┗━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━┛
;       高地址                                                                                          低地址
;
;

; 說明:
;
; (1) P:    存在(Present)位。
;  P=1 表示描述符對地址轉換是有效的,或者說該描述符所描述的段存在,即在內存中;
;  P=0 表示描述符對地址轉換無效,即該段不存在。使用該描述符進行內存訪問時會引起異常。
;
; (2) DPL:  表示描述符特權級(Descriptor Privilege level),共2位。它規定了所描述段的特權級,用於特權檢查,以決定對該段能否訪問。
;
; (3) S:   說明描述符的類型。
;  對於存儲段描述符而言,S=1,以區別與系統段描述符和門描述符(S=0)。
;
; (4) TYPE: 說明存儲段描述符所描述的存儲段的具體屬性。
;
;
; 數據段類型 類型值  說明
;   ----------------------------------
;   0  只讀
;   1  只讀、已訪問
;   2  讀/寫
;   3  讀/寫、已訪問
;   4  只讀、向下擴展
;   5  只讀、向下擴展、已訪問
;   6  讀/寫、向下擴展
;   7  讀/寫、向下擴展、已訪問
;
;
;   類型值  說明
; 代碼段類型 ----------------------------------
;   8  只執行
;   9  只執行、已訪問
;   A  執行/讀
;   B  執行/讀、已訪問
;   C  只執行、一致碼段
;   D  只執行、一致碼段、已訪問
;   E  執行/讀、一致碼段
;   F  執行/讀、一致碼段、已訪問
;
;
; 系統段類型 類型編碼 說明
;   ----------------------------------
;   0  <未定義>
;   1  可用286TSS
;   2  LDT
;   3  忙的286TSS
;   4  286調用門
;   5  任務門
;   6  286中斷門
;   7  286陷阱門
;   8  未定義
;   9  可用386TSS
;   A  <未定義>
;   B  忙的386TSS
;   C  386調用門
;   D  <未定義>
;   E  386中斷門
;   F  386陷阱門
;
; (5) G:    段界限粒度(Granularity)位。
;  G=0 表示界限粒度為字節;
;  G=1 表示界限粒度為4K 字節。
;           注意,界限粒度只對段界限有效,對段基地址無效,段基地址總是以字節為單位。
;
; (6) D/B:  此位是一個很特殊的位,在描述可執行段、向下擴展數據段或由SS寄存器尋址的段(通常是堆棧段)的三種描述符中的意義各不相同。
;           ⑴ 在描述可執行段的描述符中,D位決定了指令使用的地址及操作數所默認的大小。
;  ① D=1表示默認情況下指令使用32位地址及32位或8位操作數,這樣的代碼段也稱為32位代碼段;
;  ② D=0 表示默認情況下,使用16位地址及16位或8位操作數,這樣的代碼段也稱為16位代碼段,它與80286兼容。可以使用地址大小前綴和操作數大小前綴分別改變默認的地址或操作數的大小。
;           ⑵ 在向下擴展數據段的描述符中,D位決定段的上部邊界。
;  ① D=1表示段的上部界限為4G;
;  ② D=0表示段的上部界限為64K,這是為了與80286兼容。
;           ⑶ 在描述由SS寄存器尋址的段描述符中,D位決定隱式的堆棧訪問指令(如PUSH和POP指令)使用何種堆棧指針寄存器。
;  ① D=1表示使用32位堆棧指針寄存器ESP;
;  ② D=0表示使用16位堆棧指針寄存器SP,這與80286兼容。
;
; (7) AVL:  軟件可利用位。80386對該位的使用未左規定,Intel公司也保證今後開發生產的處理器只要與80386兼容,就不會對該位的使用做任何定義或規定。
;


;----------------------------------------------------------------------------
; 描述符類型值說明
; 其中:
;       DA_  : Descriptor Attribute
;       D    : 數據段
;       C    : 代碼段
;       S    : 系統段
;       R    : 只讀
;       RW   : 讀寫
;       A    : 已訪問
;       其它 : 可按照字面意思理解
;----------------------------------------------------------------------------
DA_32  EQU 4000h ; 32 位段

DA_DPL0  EQU   00h ; DPL = 0
DA_DPL1  EQU   20h ; DPL = 1
DA_DPL2  EQU   40h ; DPL = 2
DA_DPL3  EQU   60h ; DPL = 3
;----------------------------------------------------------------------------
; 存儲段描述符類型值說明
;----------------------------------------------------------------------------
DA_DR  EQU 90h ; 存在的只讀數據段類型值
DA_DRW  EQU 92h ; 存在的可讀寫數據段屬性值
DA_DRWA  EQU 93h ; 存在的已訪問可讀寫數據段類型值
DA_C  EQU 98h ; 存在的只執行代碼段屬性值
DA_CR  EQU 9Ah ; 存在的可執行可讀代碼段屬性值
DA_CCO  EQU 9Ch ; 存在的只執行一致代碼段屬性值
DA_CCOR  EQU 9Eh ; 存在的可執行可讀一致代碼段屬性值
;----------------------------------------------------------------------------
; 系統段描述符類型值說明
;----------------------------------------------------------------------------
DA_LDT  EQU   82h ; 局部描述符表段類型值
DA_TaskGate EQU   85h ; 任務門類型值
DA_386TSS EQU   89h ; 可用 386 任務狀態段類型值
DA_386CGate EQU   8Ch ; 386 調用門類型值
DA_386IGate EQU   8Eh ; 386 中斷門類型值
DA_386TGate EQU   8Fh ; 386 陷阱門類型值
;----------------------------------------------------------------------------


; 選擇子圖示:
;         ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
;         ┃ 15 ┃ 14 ┃ 13 ┃ 12 ┃ 11 ┃ 10 ┃ 9  ┃ 8  ┃ 7  ┃ 6  ┃ 5  ┃ 4  ┃ 3  ┃ 2  ┃ 1  ┃ 0  ┃
;         ┣━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━╋━━╋━━┻━━┫
;         ┃                                 描述符索引                                 ┃ TI ┃   RPL    ┃
;         ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━┻━━━━━┛
;
; RPL(Requested Privilege Level): 請求特權級,用於特權檢查。
;
; TI(Table Indicator): 引用描述符表指示位
; TI=0 指示從全局描述符表GDT中讀取描述符;
; TI=1 指示從局部描述符表LDT中讀取描述符。
;

;----------------------------------------------------------------------------
; 選擇子類型值說明
; 其中:
;       SA_  : Selector Attribute

SA_RPL0  EQU 0 ; ┓
SA_RPL1  EQU 1 ; ┣ RPL
SA_RPL2  EQU 2 ; ┃
SA_RPL3  EQU 3 ; ┛

SA_TIG  EQU 0 ; ┓TI
SA_TIL  EQU 4 ; ┛
;----------------------------------------------------------------------------





; 宏 ------------------------------------------------------------------------------------------------------
;
; 描述符
; usage: Descriptor Base, Limit, Attr
;        Base:  dd
;        Limit: dd (low 20 bits available)
;        Attr:  dw (lower 4 bits of higher byte are always 0)
%macro Descriptor 3
 dw %2 & 0FFFFh    ; 段界限 1    (2 字節)
 dw %1 & 0FFFFh    ; 段基址 1    (2 字節)
 db (%1 >> 16) & 0FFh   ; 段基址 2    (1 字節)
 dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 屬性 1 + 段界限 2 + 屬性 2  (2 字節)
 db (%1 >> 24) & 0FFh   ; 段基址 3    (1 字節)
%endmacro ; 共 8 字節
;
; 門
; usage: Gate Selector, Offset, DCount, Attr
;        Selector:  dw
;        Offset:    dd
;        DCount:    db
;        Attr:      db
%macro Gate 4
 dw (%2 & 0FFFFh)    ; 偏移 1    (2 字節)
 dw %1     ; 選擇子    (2 字節)
 dw (%3 & 1Fh) | ((%4 << 8) & 0FF00h) ; 屬性     (2 字節)
 dw ((%2 >> 16) & 0FFFFh)   ; 偏移 2    (2 字節)
%endmacro ; 共 8 字節
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</pre>

pmtest2.inc

; ==========================================
; pmtest2.asm
; 編譯方法:nasm pmtest2.asm -o pmtest2.com
; ==========================================

%include "pm.inc" ; 常量, 巨集, 以及一些說明

org 0100h
 jmp LABEL_BEGIN

[SECTION .gdt]
; GDT
;                            段基址,        段界限 , 屬性
LABEL_GDT:         Descriptor    0,              0, 0         ; 空描述符
LABEL_DESC_NORMAL: Descriptor    0,         0ffffh, DA_DRW    ; Normal 描述符
LABEL_DESC_CODE32: Descriptor    0, SegCode32Len-1, DA_C+DA_32; 非一致程式碼段, 32
LABEL_DESC_CODE16: Descriptor    0,         0ffffh, DA_C      ; 非一致程式碼段, 16
LABEL_DESC_DATA:   Descriptor    0,      DataLen-1, DA_DRW    ; Data
LABEL_DESC_STACK:  Descriptor    0,     TopOfStack, DA_DRWA+DA_32; Stack, 32 位
LABEL_DESC_TEST:   Descriptor 0500000h,     0ffffh, DA_DRW
LABEL_DESC_VIDEO:  Descriptor  0B8000h,     0ffffh, DA_DRW    ; 顯示卡記憶體首位址
; GDT 結束

GdtLen  equ $ - LABEL_GDT ; GDT長度
GdtPtr  dw GdtLen - 1 ; GDT界限
  dd 0  ; GDT基位址

; GDT 選擇子
SelectorNormal  equ LABEL_DESC_NORMAL - LABEL_GDT
SelectorCode32  equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorCode16  equ LABEL_DESC_CODE16 - LABEL_GDT
SelectorData  equ LABEL_DESC_DATA  - LABEL_GDT
SelectorStack  equ LABEL_DESC_STACK - LABEL_GDT
SelectorTest  equ LABEL_DESC_TEST  - LABEL_GDT
SelectorVideo  equ LABEL_DESC_VIDEO - LABEL_GDT
; END of [SECTION .gdt]

[SECTION .data1]  ; 資料段
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueInRealMode dw 0
; 字元串
PMMessage:  db "In Protect Mode now. ^-^", 0 ; 在保護模式中顯示
OffsetPMMessage  equ PMMessage - $$
StrTest:  db "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0
OffsetStrTest  equ StrTest - $$
DataLen   equ $ - LABEL_DATA
; END of [SECTION .data1]


; 全局堆疊段
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
 times 512 db 0

TopOfStack equ $ - LABEL_STACK - 1

; END of [SECTION .gs]


[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
 mov ax, cs
 mov ds, ax
 mov es, ax
 mov ss, ax
 mov sp, 0100h

 mov [LABEL_GO_BACK_TO_REAL+3], ax
 mov [SPValueInRealMode], sp

 ; 初始化 16 位程式碼段描述符
 mov ax, cs
 movzx eax, ax
 shl eax, 4
 add eax, LABEL_SEG_CODE16
 mov word [LABEL_DESC_CODE16 + 2], ax
 shr eax, 16
 mov byte [LABEL_DESC_CODE16 + 4], al
 mov byte [LABEL_DESC_CODE16 + 7], ah

 ; 初始化 32 位程式碼段描述符
 xor eax, eax
 mov ax, cs
 shl eax, 4
 add eax, LABEL_SEG_CODE32
 mov word [LABEL_DESC_CODE32 + 2], ax
 shr eax, 16
 mov byte [LABEL_DESC_CODE32 + 4], al
 mov byte [LABEL_DESC_CODE32 + 7], ah

 ; 初始化資料段描述符
 xor eax, eax
 mov ax, ds
 shl eax, 4
 add eax, LABEL_DATA
 mov word [LABEL_DESC_DATA + 2], ax
 shr eax, 16
 mov byte [LABEL_DESC_DATA + 4], al
 mov byte [LABEL_DESC_DATA + 7], ah

 ; 初始化堆疊段描述符
 xor eax, eax
 mov ax, ds
 shl eax, 4
 add eax, LABEL_STACK
 mov word [LABEL_DESC_STACK + 2], ax
 shr eax, 16
 mov byte [LABEL_DESC_STACK + 4], al
 mov byte [LABEL_DESC_STACK + 7], ah

 ; 為加載 GDTR 作準備
 xor eax, eax
 mov ax, ds
 shl eax, 4
 add eax, LABEL_GDT  ; eax <- gdt 基位址
 mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基位址

 ; 加載 GDTR
 lgdt [GdtPtr]

 ; 關中斷
 cli

 ; 打開位址線A20
 in al, 92h
 or al, 00000010b
 out 92h, al

 ; 準備切換到保護模式
 mov eax, cr0
 or eax, 1
 mov cr0, eax

 ; 真正進入保護模式
 jmp dword SelectorCode32:0 ; 執行這一句會把 SelectorCode32 裝入 cs, 並跳轉到 Code32Selector:0  處

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LABEL_REAL_ENTRY:  ; 從保護模式跳回到實模式就到了這裡
 mov ax, cs
 mov ds, ax
 mov es, ax
 mov ss, ax

 mov sp, [SPValueInRealMode]

 in al, 92h  ; `.
 and al, 11111101b ;  | 關閉 A20 位址線
 out 92h, al  ; /

 sti   ; 開中斷

 mov ax, 4c00h ; `.
 int 21h  ; /  回到 DOS
; END of [SECTION .s16]


[SECTION .s32]; 32 位程式碼段. 由實模式跳入.
[BITS 32]

LABEL_SEG_CODE32:
 mov ax, SelectorData
 mov ds, ax   ; 資料段選擇子
 mov ax, SelectorTest
 mov es, ax   ; 測試段選擇子
 mov ax, SelectorVideo
 mov gs, ax   ; 視頻段選擇子

 mov ax, SelectorStack
 mov ss, ax   ; 堆疊段選擇子

 mov esp, TopOfStack


 ; 下面顯示一個字元串
 mov ah, 0Ch   ; 0000: 黑底    1100: 紅字
 xor esi, esi
 xor edi, edi
 mov esi, OffsetPMMessage ; 源資料偏移
 mov edi, (80 * 10 + 0) * 2 ; 目的資料偏移。螢幕第 10 行, 第 0 列。
 cld
.1:
 lodsb
 test al, al
 jz .2
 mov [gs:edi], ax
 add edi, 2
 jmp .1
.2: ; 顯示完畢

 call DispReturn

 call TestRead
 call TestWrite
 call TestRead

 ; 到此停止
 jmp SelectorCode16:0

; ------------------------------------------------------------------------
TestRead:
 xor esi, esi
 mov ecx, 8
.loop:
 mov al, [es:esi]
 call DispAL
 inc esi
 loop .loop

 call DispReturn

 ret
; TestRead 結束-----------------------------------------------------------


; ------------------------------------------------------------------------
TestWrite:
 push esi
 push edi
 xor esi, esi
 xor edi, edi
 mov esi, OffsetStrTest ; 源資料偏移
 cld
.1:
 lodsb
 test al, al
 jz .2
 mov [es:edi], al
 inc edi
 jmp .1
.2:

 pop edi
 pop esi

 ret
; TestWrite 結束----------------------------------------------------------


; ------------------------------------------------------------------------
; 顯示 AL 中的數字
; 默認地:
; 數字已經存在 AL 中
; edi 始終指向要顯示的下一個字元的位置
; 被改變的暫存器:
; ax, edi
; ------------------------------------------------------------------------
DispAL:
 push ecx
 push edx

 mov ah, 0Ch   ; 0000: 黑底    1100: 紅字
 mov dl, al
 shr al, 4
 mov ecx, 2
.begin:
 and al, 01111b
 cmp al, 9
 ja .1
 add al, '0'
 jmp .2
.1:
 sub al, 0Ah
 add al, 'A'
.2:
 mov [gs:edi], ax
 add edi, 2

 mov al, dl
 loop .begin
 add edi, 2

 pop edx
 pop ecx

 ret
; DispAL 結束-------------------------------------------------------------


; ------------------------------------------------------------------------
DispReturn:
 push eax
 push ebx
 mov eax, edi
 mov bl, 160
 div bl
 and eax, 0FFh
 inc eax
 mov bl, 160
 mul bl
 mov edi, eax
 pop ebx
 pop eax

 ret
; DispReturn 結束---------------------------------------------------------

SegCode32Len equ $ - LABEL_SEG_CODE32
; END of [SECTION .s32]


; 16 位程式碼段. 由 32 位程式碼段跳入, 跳出後到實模式
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:
 ; 跳回實模式:
 mov ax, SelectorNormal
 mov ds, ax
 mov es, ax
 mov fs, ax
 mov gs, ax
 mov ss, ax

 mov eax, cr0
 and al, 11111110b
 mov cr0, eax

LABEL_GO_BACK_TO_REAL:
 jmp 0:LABEL_REAL_ENTRY ; 段位址會在程序開始處被設置成正確的值

Code16Len equ $ - LABEL_SEG_CODE16

; END of [SECTION .s16code]

步驟


  1. 編譯 pmtest2.asm (nasm pmtest2.asm -o pmtest2.com)
  2. 將 pmtest2.com 放入 pm.img,範例指定如下:
    mkdir tmp
    sudo mount -o loop pm.img tmp
    sudo cp pmtest2.com tmp
    sudo umount tmp
    rmdir tmp
    
  3. 利用freedos.img開機
  4. 執行 b:\pmtest2.com
執行結果

留言

這個網誌中的熱門文章

用 C# 批次控制 Word 合併列印

前由 我有全區的電話資料,問題在於我要依不同里別來製作出電話簿。結果如下圖: 單純採用合併列印無法達成我的需求。解決方法係用「功能變數」儲存上一個里別,與現在里別進行比較:若不同,則換頁。不過,這樣功能變數還蠻長的。最後,我還是採用 C# 來解決。 解決方案 用 C# 控制 WORD 中合併列印的「資料來源 Data Source」,給予不同里別的「sqlstatement」。迴圈處理不同的里別即可。但可預見其處理過程會很慢,不過還好,我可以不用在意它,有跑出結果即可。 程式碼 IList<string> areas = new List<string>() { "後壁", "侯伯", "嘉苳", "土溝", "嘉田", "嘉民", "菁豊", "崁頂", "後廍", "墨林", "菁寮", "新嘉", "頂長", "平安", "仕安", "竹新", "新東", "長安", "頂安", "福安", "烏樹" }; string root = @"D:\"; // 根目錄 string data = root + @"\data.docm"; // 資料檔(即資料來源) string template = root + @"\template.docx"; // 已設定好格式與合併欄位的 Word 檔 string output = @"d:\Final"; // 輸出之資料夾 object oMissing = System.Reflection.Missing.Va...

VLC c# 順利編譯

原文網址: http://www.cnblogs.com/haibindev/archive/2011/12/21/2296173.html 原文作者: haibindev 原文標題:c#万能视频播放器 本文的重點在於修正 class VlcPlayer,使其能順利在 VC# Express 2010 .Net Framework 4 下順利編譯。 修正重點在於 CallingConvention = CallingConvention. StdCall 改成 CallingConvention = CallingConvention. Cdecl using System; using System.Runtime.InteropServices; using System.Security; using System.Text; namespace VlcDotNet { class VlcPlayer { private IntPtr libvlc_instance_; private IntPtr libvlc_media_player_; private double duration_; public VlcPlayer(string pluginPath) { string plugin_arg = "--plugin-path=" + pluginPath; string[] arguments = { "-I", "dummy", "--ignore-config", "--no-video-title", plugin_arg }; libvlc_instance_ = LibVlcAPI.libvlc_new(arguments); libvlc_media_player_ = LibVlcAPI.libvlc_media_player_new(libvlc_instance_); } public ...

[Symfony+Doctrine] 透過非 Id 來使用 Pessimistic Lock

根據 文件 ,Doctrine 有 Pessimistic Lock,又分為兩種: LockMode::PESSIMISTIC_WRITE:對應至 MySQL 的 Select FOR UPDATE LockMode::PESSIMISTIC_READ:對應至 MySQL 的 Select LOCK IN SHARE MODE 差別在於 LOCK IN SHARE MODE 會將在 row 的資料鎖定(row-level lock),在非同一交易(Transaction)下,不給寫入,其他交易可以讀取, 且可以繼續 Select LOCK IN SHARE MODE 。而 FOR UPDATE 不僅鎖定該資料,在非同一交易下,不給寫入,其它交易可以讀取, 但不能 Select LOCK IN SHARE MODE 。MySQL 文件有更詳細的比較與情境使用的說明,參考 網址 。 現在問題是,我們要完全採用 ORM 來處理資料。Doctrine 的文件提到 EntityManager::find 可以採用 Pessimistic Lock, 但 find 是透過 id 來處理 。而其他 find 系列函數(包括:findAll, findBy, findOneBy)皆不支援 LockMode。 因此,勢必要有方法來「透過非 id 來使用 Pessimistic Lock」。透過查看原始碼,簡單的方法是有的,解法之範例如下: 19 public function depositAction() 20 { 21 22 $em = $this->getDoctrine()->getManager(); 23 24 $em->transactional(function ($em) { 25 $entityName = 'AcmeTrainingBundle:Account'; 26 $lockMode = LockMode::PESSIMISTIC_READ; 27 $orderBy = null; 28 $...