今天翻找以前的代码, 突然发现, 以前曾经用汇编写的二叉树, 以前对汇编过于执着. 是好事, 也是坏事. 个人觉得, 用MASM写代码其实和C语言的差别是非常小的, 但是毕竟是不同的语言, 就和C和C++的差别一样, C++在C的基础之上多了一点东西, 多的这点东西带来的是思想上的转变, 汇编也是一样, 用汇编写的代码, 我觉得带来的其实也是一种思想. 一种用汇编想问题的思想.

有个朋友C++学的很不错, 但是他始终对代码编译出的二进制模型比较模糊, 当然如果使用汇编, 长期养成的习惯就不会有这个问题了. 当然这个汇编不只是32位了.. 16位现在虽然已经过时了. 但是不可否认从16位开始学起, 会更加了解什么是代码, 什么是数据. 目前的masm的版本是11了. 跟随着vs2012发布, 很多人写16位的代码还是用masm5. 究其原因就是16位的代码都用masm5, 32位都用masm6xx以上, 其实这种观点是不对的, 包括最新的masm11在内, 都是可以写16位的代码的, 我要特别提到这点, 空间里面有很多的保护模式下面的文章, 我都是用masm9写的.. 哪个说不能写16位代码??

使用汇编是其中一个, 另外一个为了组件自己的开发环境, 没有使用masm32包, 我自己搞了个大杂烩 我当时使用了vs2010 ml nmak rc link等工具, 外加cl. 然后把Windows SDK的头文件, CRT头文件, MFC头文件, 库文件打包到一起, 开发包不到50M.. 写C/C++ MFC. 都够了.

虽然现在基本不怎么用汇编写代码了. 但是感谢曾经有那么一段执着的日子. 下面这段二叉树操作的代码摘抄自曾经写的一个<<学员管理系统>> 可以看到里面调用了很多类C的函数, 比如cprintf. 这个函数在MASM32里面是真实存在的, 只是名字是_imp___cprintf. 还有MASM32的作者对msvcrt.lib进行了改动, 他改动的这个msvcrt.lib可以用在汇编上, 另外剔除了C++ 使用的一些库函数, 只保留了C的一些函数, 比如printf, scanf啊一类的.. 而且这个库有个优点, 不用初始化, C/C++里面带那个msvcrt.lib必须给初始化一下非常恶心.. so 汇编也可以调用C的函数和系统提供的api..

二叉树的话, 也是增, 删, 改, 查. 下面基本都有了. 比较难的是删除. 删除考虑的事情有点多.. 另外二叉树也是不平衡的, 最坏的情况就变链表了.. 所以二叉树还应该做平衡, 有时间再看看AVL树..

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
PATH_ASSEMBLY	equ 1
	Include Common.inc
NameStudent	struct
	lpStudent	dword	?	;当前学员地址
	dwNumber	dword	?	;当前学员编号
	lpLeft		dword	?	;左节点指针
	lpRight		dword	?	;右节点指针
	lpBefor		dword	?	;如果有重复的话, 前一个名称地址
	lpNext		dword	?	;如果有重复的话, 下一个名称地址
	SzName		byte  32 dup(?)	;学员名字	
NameStudent 	ends
NumberStudent	struct
	lpStudent	dword	?	;当前学员地址
	dwNumber	dword	?	;学员编号
	lpLeft		dword	?	;左节点指针
	lpRight		dword	?	;右节点指针	
NumberStudent 	ends
		.data?
SzIniFile	byte	MAX_PATH dup(?)	;ini文件名称
dwCount		dword		?	;遍历计数器
		.code
;============================================================================
;遍历学员信息
;_lpStProInfo:程序信息头
;_lpStNumberHead:编号树头节点
;返回true表示遍历成功, 返回false表示没有节点给遍历
_IterateStudentNode proc public  _lpStProInfo:dword, _lpStNumberHead:dword
	
	assume	ebx:ptr NumberStudent
	assume	esi:ptr Student
	assume	edi:ptr Score
	assume	edx:ptr Course
	
	mov	ebx, _lpStNumberHead
	
	.if	ebx != null
;============================================================================
		;先从左边开始遍历, 如果左边有那么就会一直追溯进去, 在堆栈中
		;很明显就会形成大的在上面, 小的在下面, 然后在左边遍历完了以后
		;开始返回, 返回一个打印一个, 达到了遍历的目的
;============================================================================		
		Invoke	_IterateStudentNode,_lpStProInfo, [ebx].lpLeft
		
		mov	ebx, _lpStNumberHead
		mov	esi, [ebx].lpStudent
		Invoke	cprintf, cfm$( "============================================================================n" )
		Invoke	cprintf, cfm$( "学员编号:    %d\n学员名字:    %s\n课程总数:    %d\n" ), \
			[esi].dwNumber, addr [esi].szName, [esi].dwScoreLen
		
		lea	edi, [esi].StScore
		mov	ecx, [esi].dwScoreLen
		
	@@:	
;============================================================================
		;对于右边就先打印了才去遍历, 因为右边是从小到大, 总之这样就
		;完成了遍历
;============================================================================
		push	ecx
		Invoke	_FindCourseNode, _lpStProInfo, [edi].dwCourse ;去查找该课程的字符串表示
		mov	edx, eax
		
		.if	!edx
			Invoke	cprintf, cfm$( "课程编号:    %d(无效课程, 请尽快修改)\n课程分数:    %d\n" ),\
				 [edi].dwCourse,  [edi].dwScore
		.else
			Invoke	cprintf, cfm$( "课程编号:    %d\n课程名称:    %s\n任课老师:    %s\n课程分数:    %d\n" ),\
				 [edi].dwCourse, addr [edx].szName, addr [edx].szTeach, [edi].dwScore
		.endif
		add	edi, 8
		pop	ecx
		loop	@b
		
		inc	dwCount
;============================================================================
		;如果用户输入了想退出, 因为这里是链表, 也许会有很多层, 很难跳
		;我这里就在外面挂了一个异常, 如果想跳出去, 直接触发一样..
		;外面捕获自己就出去了
;============================================================================	
		.if	dwCount == 20
			push	0
			pop	dwCount
			
			Invoke	Jpause
			.if	!eax
				xor	eax, eax
				mov	dword ptr [eax], 0
			.endif
		.endif
		Invoke	_IterateStudentNode,_lpStProInfo, [ebx].lpRight
	.endif
_ret:
	ret
_IterateStudentNode endp
;============================================================================
;以编号查询一个学生
;_lpStNumberHead:编号树头
;_dwNumber:要查找的编号
;找到返回该节点指针, 没有找到返回NULL
_FindNumberStudnet	proc	public _lpStNumberHead:dword, _dwNumber:dword
	
	assume	ebx:ptr NumberStudent
	mov	ebx, _lpStNumberHead
	
	.if	ebx != null
		
		mov	eax, [ebx].dwNumber	
		.if	eax < _dwNumber		;大于向右
			mov	ebx, dword ptr [ebx].lpRight	;这里已经是二级指针了, 不用转了
			
			Invoke	_FindNumberStudnet, ebx, _dwNumber
			
		.elseif	eax > _dwNumber		;小于向左
			mov	ebx, dword ptr [ebx].lpLeft	;这里已经是二级指针了, 不用转了
			
			Invoke	_FindNumberStudnet,ebx, _dwNumber
			
		.elseif	eax == _dwNumber	;等于返回
			mov	eax, [ebx].lpStudent
		.endif
	.else
		xor	eax, eax	
	.endif
_ret:
	ret
_FindNumberStudnet 	endp
;============================================================================
;以名称查询一个学生
;_lpStNumberHead:编号树头
;_lpSzName:要查找的名字
;找到返回树中节点的指针, 在外面自己要重新做解析, 没有找到返回NULL
_FindNameStudnet proc	public _lpStNameHead:dword, _lpSzName:dword
;============================================================================
	
	assume	ebx:ptr NameStudent
	mov	ebx, _lpStNameHead

	.if	ebx != null
		
		lea	ecx, [ebx].SzName
		Invoke	lstrcmp, ecx, _lpSzName	
		
		cmp	eax, 0	;.if	eax > 0	
		jg	_else2	
		cmp	eax, 0	;.elseif eax < 0	
		jl	_else1
		jmp	_else3	;.elseif eax == 0
;============================================================================
_else1:		
		mov	ebx, dword ptr [ebx].lpRight ;大的到右边
		Invoke	_FindNameStudnet,ebx, _lpSzName
		jmp	_ret
_else2:			
		mov	ebx, dword ptr [ebx].lpLeft ;小的到左边
		Invoke	_FindNameStudnet, ebx, _lpSzName
		jmp	_ret
_else3:
;============================================================================
	;注意, 这里返回的指针, 和其他函数有点不一样.由于这里需要返回多个同名
	;的结构指针, 所以直接把内部的链表结构指针返回了
;============================================================================
		mov	eax, ebx		   
	.else
		xor	eax, eax
	.endif
_ret:
	ret
_FindNameStudnet 	endp

;============================================================================
	.data?
lpFather	dword	?
	.code
;============================================================================
;查找某个需要删除的值的位置, 并保存其父节点
;============================================================================
_FindNumberNode	proc  private uses ebx	 _lpCurHead:dword, _lpHead:dword, _dwValue:dword
;==============================================================

	assume	ebx:ptr NumberStudent

	mov	ebx, _lpHead
	;mov	ebx, dword ptr [ebx]
	
	.if	ebx != NULL	
		mov	eax, [ebx].dwNumber	;学员编号
		
		.if	eax > _dwValue		;从左边查找
			Invoke	_FindNumberNode, ebx,  [ebx].lpLeft, _dwValue
		.elseif	eax < _dwValue		;从右边查找
			Invoke	_FindNumberNode, ebx,  [ebx].lpRight, _dwValue
		.else
			push	_lpCurHead	;将父节点保存起来
			pop	lpFather
				
			mov	eax, ebx	;将当前节点返回
		.endif
	.else
		xor	eax, eax
	.Endif
	ret
_FindNumberNode	Endp
;============================================================================
;在一个树中, 从右边查找比头节点只大一号的值, 这个值一定是从该树的右子树, 一直往
;左,  直到左边的那个节点的左子树为NULL为止..
;lpRightFather:这个值的作用是, 如果右边找到的最小值如果其右子树还有值, 那么就要
;把他们挂上来...
_FindNumberRightMin	proc	private _lpHead:dword
	
	assume	ebx:ptr NumberStudent
	mov	ebx, _lpHead
	
	.if	[ebx].lpLeft != NULL			;如果右子树不为NULL
		Invoke	_FindNumberRightMin, [ebx].lpLeft;那就直到查找到为NULL为止
	.else
		mov	eax, ebx			;遍历到了最右边的返回就可以了
	.endif
	
	ret
_FindNumberRightMin 	endp
;============================================================================
;删除一个学员节点(同时删除在编号树中的节点)
;_lpStProInfo:程序信息头
;_dwNumber:要删除的编号
_DelNumberStudentNodo	proc	public _lpStProInfo:dword, _dwNumber:dword
;============================================================================
	local	_lpDelNode	:dword	;需要删除的节点
	local	_lpWrieteMem	:dword	;要写入内存中的值
	
	
	
	assume	ebx:ptr NumberStudent
	assume	esi:ptr NumberStudent
	push	-1
	pop	_lpWrieteMem
	push	NULL
	pop	lpFather
;============================================================================
	mov	eax, _lpStProInfo
	mov	eax, [eax+ProInfo.lpNumberStudent]
	Invoke	_FindNumberNode, NULL, eax, _dwNumber	;查找某个需要删除的值
	mov	_lpDelNode, eax
	
	
	mov	ebx, _lpDelNode
	
	;如果左子树和右子树都为NULL删除就比较容易了
	.if	[ebx].lpLeft == NULL && [ebx].lpRight == NULL
		
		;if要删除的父节点为NULL(说明整个程序只有一个节点)
		.if	lpFather == NULL
			mov	_lpWrieteMem, NULL	;;写入NULL到_lpWrieteMem
			
		;else要删除的父节点不为NULL判断该节点是左还是右, 置为NULL就可以了
		.else
			mov	ebx, lpFather
			mov	eax, _lpDelNode
			.if	[ebx].lpLeft == eax		;如果是左边
				mov	[ebx].lpLeft, NULL	;左边置NULL
			.else
				mov	[ebx].lpRight, NULL	;右边置为NULL
			.endif
		.endif
			
	;如果左子树和右子树都不为NULL
	.elseif [ebx].lpLeft != NULL && [ebx].lpRight != NULL
		
		;在右子树中查找最小的一个值的指针
		Invoke	_FindNumberRightMin, [ebx].lpRight
		.if	!eax
			jmp	_ret
		.endif
		
		;将左子树挂到右子树中最小的那个节点的左子树中
		mov	ebx, _lpDelNode
		push	[ebx].lpLeft
		
		mov	ebx, eax
		pop	[ebx].lpLeft
		
		;if要删除的父节点为NULL(说明要删除的是头节点
		.if	lpFather == NULL
			mov	ebx, _lpDelNode
			push	[ebx].lpRight	;将要删除的右子树作为新的头节点
			pop	_lpWrieteMem
			
		;否则需要判断是需要删除的节点是左子树还是右子树
		.else
			mov	ebx, lpFather
			mov	eax, _lpDelNode
			
			mov	esi, _lpDelNode
			push	[esi].lpRight
			.if	[ebx].lpLeft == eax
				pop	[ebx].lpLeft	;如果要删除的节点原来在左边就挂左边
			.else
				pop	[ebx].lpRight	;如果要删除的节点原来在右边就挂右边
			.endif
		.endif
	
	;如果只有左边为NULL
	.elseif [ebx].lpLeft == NULL
		
		;if如果要删除的父节点为NULL
		.if	lpFather == NULL
			push	[ebx].lpRight	;右节点成为新的头节点
			pop	_lpWrieteMem	;写入_lpWrieteMem中
			
		;如果要删除的父节点不为NULL
		.else
			push	[ebx].lpRight	;要删除节点的右子树
			mov	ebx, _lpDelNode	
			
			mov	esi, lpFather	;将右边挂到父节点下面
			.if	[esi].lpLeft == ebx
				pop	[esi].lpLeft ;左边相等挂左边
			.else
				pop	[esi].lpRight;右边相等挂右边
			.endif
			
		.endif
				
	;如果只有右子树为NULL
	.elseif [ebx].lpRight == NULL
		
		;if如果要删除的父节点为NULL
		.if	lpFather == NULL
			push	[ebx].lpRight	;左节点成为新的头节点
			pop	_lpWrieteMem	;写入_lpWrieteMem中
		
		;如果要删除的节点不为NULL
		.else
			push	[ebx].lpLeft
			mov	esi, lpFather	;被删除值的左子树
			
			.if	[esi].lpLeft == ebx
				pop	[esi].lpLeft	;将左边挂到父节点下面
			.else
				pop	[esi].lpRight	;如果右边相等就挂右边
			.endif
			
		.endif
	.endif
	
	;.if	_lpWrieteMem != -1
	.if	_lpWrieteMem != -1			;如果不为NULL就写入ini文件, 和内存中
		
		mov	ebx, eax
		Invoke	JWritePrivateProfileInt, 	;写入注册表中
			offset SzSection, offset SzKeylpNumStu, _lpWrieteMem , offset SzIniFile	
		.if	!eax
			jmp	_ret
		.endif		
	
		mov	esi, _lpStProInfo	 	;更新文件中的结构
		push	_lpWrieteMem
		pop	[esi+ProInfo.lpNumberStudent]
		
	.endif
	
	;释放内存
	mov	ebx, _lpDelNode
	Invoke	Jfree, [ebx].lpStudent
	Invoke	Jfree, ebx
_ret:
	ret
_DelNumberStudentNodo 	endp
;============================================================================
;插入一个节点到编号树中(是个递归函数)
;_lpStNumberHead:头节点指针, 二级指针
;_lpNewStudent:新学员指针
;_dwNumber:编号
;成功返回生成的头指针, 否则返回false
_InsertNumberTree	proc	private    _lpStNumberHead:dword, _lpNewStudent:dword, _dwNumber:dword
;============================================================================
	
	assume	ebx:ptr NumberStudent
	assume	esi:ptr NumberStudent
;============================================================================
	;如果树目前不是空的, 那么就递归调用, 直到空为止
;============================================================================
	mov	ebx, _lpStNumberHead	
	mov	eax, dword ptr [ebx]	;二级指针
	
	.if	eax
		mov	eax, _dwNumber
		;这句非常重要, 因为这里是3级指针, 比上面的又多加了一层..
		;昨天调试了一晚上就是这个地方
		mov	ebx, dword ptr [ebx]			
	
		.if	[ebx].dwNumber <= eax			;大的到右边		
			lea	ecx, [ebx].lpRight
			Invoke	_InsertNumberTree, ecx, _lpNewStudent, _dwNumber
		.else						;小的到左边
			lea	ecx, [ebx].lpLeft
			Invoke	_InsertNumberTree,ecx , _lpNewStudent, _dwNumber
		.endif	
	.else
;============================================================================
;	生成一个新的树节点
;============================================================================		
		Invoke	Jmalloc, sizeof NumberStudent
		.if	!eax
			jmp	_ret
		.endif
		
		xchg	esi, eax
		push	_lpNewStudent			;新的头节点
		pop	[esi].lpStudent
	
		push	null
		pop	[esi].lpLeft			;左子树为NULL
		push	null
		pop	[esi].lpRight			;右子树为NULL
		
		push	_dwNumber
		pop	[esi].dwNumber			;编号
		
		mov	dword ptr [ebx], esi		;返回该地址指针
		mov	eax, esi
	.endif
;============================================================================		
_ret:
	ret
_InsertNumberTree endp
;============================================================================
;插入一个节点到名称树中(是个递归函数)
;_lpStNumberHead:头节点指针, 二级指针
;_lpNewStudent:新学员指针
;_lpSzName:名字
;成功返回生成的头指针, 否则返回false
_InsertNameTree	 proc	private    _lpStNumberHead:dword, _lpNewStudent:dword, _lpSzName:dword
	
	assume	ebx:ptr NameStudent
	assume	esi:ptr NameStudent
	
;============================================================================
	;如果树目前不是空的, 那么就递归调用, 直到空为止
;============================================================================
	mov	ebx, _lpStNumberHead	
	mov	eax, dword ptr [ebx]	;二级指针

	.if	eax
		mov	eax, _lpSzName
		;这句非常重要, 因为这里是3级指针, 比上面的又多加了一层..
		;昨天调试了一晚上就是这个地方
		mov	ebx, dword ptr [ebx]			
		
		lea	ecx, [ebx].SzName
		Invoke	lstrcmp, ecx, eax
		
		cmp	eax, 0	;.if	eax > 0	
		jg	_else1	
		cmp	eax, 0	;.elseif eax < 0	
		jl	_else2
		jmp	_else3	;.elseif eax == 0
;============================================================================
_else1:		
		lea	ecx, [ebx].lpLeft  	;小的到左边
		Invoke	_InsertNameTree,ecx , _lpNewStudent, _lpSzName
		jmp	_ret
_else2:		
		lea	ecx, [ebx].lpRight 	;大的到右边	
		Invoke	_InsertNameTree, ecx, _lpNewStudent, _lpSzName
		jmp	_ret	
_else3:
;============================================================================
;		插入同名的学员, 远比我们想象的复杂
;============================================================================
		Invoke	Jmalloc, sizeof NameStudent
		.if	!eax
			jmp	_ret
		.endif
		xchg	esi, eax
		push	_lpNewStudent			;新的头节点
		pop	[esi].lpStudent
	
		push	null
		pop	[esi].lpLeft			;左子树为NULL
		push	null
		pop	[esi].lpRight			;右子树为NULL
		push	NULL
		pop	[esi].lpNext			;下一个同名节点为NULL
		
		mov	eax, _lpNewStudent
		mov	eax, [eax+Student.dwNumber]	;填写学员编号
		push	eax
		pop	[esi].dwNumber
		
		Invoke	RtlMoveMemory, addr [esi].SzName, _lpSzName, sizeof Student.szName 
;============================================================================
		;这里也是由原来的单向链表我修改成了双向的, 不过还是在链表末尾添加
;============================================================================
		.if	[ebx].lpNext == NULL
			mov	[ebx].lpNext, esi	;将同名的学员链在学员后面
			push	ebx
			pop	[esi].lpBefor		;之前一个就置为ebx, 这样就双向了
		.else
		@@:	mov	ebx, [ebx].lpNext
			or	[ebx].lpNext, 0		;在同名学员链表最后添加
			jnz	@b
			
			push	esi			;天下下一个
			pop	[ebx].lpNext
			
			push	ebx			;之前的一个就是保存的前一个节点了
			pop	[esi].lpBefor
		.endif	
		mov	eax, esi			;返回该地址指针
;============================================================================		
	
	.else
;============================================================================
;	生成一个新的树节点
;============================================================================		
		Invoke	Jmalloc, sizeof NameStudent
		.if	!eax
			jmp	_ret
		.endif
		
		xchg	esi, eax
		push	_lpNewStudent			;新的头节点
		pop	[esi].lpStudent
	
		push	null
		pop	[esi].lpLeft			;左子树为NULL
		push	null
		pop	[esi].lpRight			;右子树为NULL
		push	NULL
		pop	[esi].lpNext			;下一个同名节点为NULL
		push	NULL
		pop	[esi].lpBefor			;前一个同名节点为NULL
		
		
		mov	eax, _lpNewStudent
		mov	eax, [eax+Student.dwNumber]	;填写学员编号
		push	eax
		pop	[esi].dwNumber
		
		Invoke	RtlMoveMemory, addr [esi].SzName, _lpSzName, sizeof Student.szName 
		
		mov	dword ptr [ebx], esi		;返回该地址指针
		mov	eax, esi
	.endif	
_ret:
	ret
_InsertNameTree  endp
;============================================================================
;将一条学员信息插入到树中
;_lpStProInfo:程序基本信息结构
;_lpStStudent:指向学员信息结构
;成功返回true, 否则返回false
_AddStudentNode	proc	public _lpStProInfo:dword, _lpStStudent:dword
;============================================================================
	local	_lpNewStudent	:dword
	local	_dwLen		:dword
	local	_dwBuf		:dword
	
	assume	ebx:ptr Student
	assume	esi:ptr ProInfo

	mov	ebx, _lpStStudent
	mov	esi, _lpStProInfo
;============================================================================
;	在文件中构造一个新的学员对象
;============================================================================
	mov	eax, [ebx].dwScoreLen
	lea	eax, [eax*8]
	add	eax, sizeof Student - 8 	
	xchg	_dwLen, eax			;由于学员信息结构是变长的, 这里求长度

	Invoke	Jmalloc, _dwLen			;分配一个学员大小的内存
	.if	!eax
		jmp	Error
	.endif
	xchg	_lpNewStudent, eax
	Invoke	RtlMoveMemory, _lpNewStudent, _lpStStudent, _dwLen
	
	Invoke	lstrcpy, offset SzIniFile, offset SzCurrentDir
	invoke	lstrcat, offset SzIniFile, offset SzIniPath
;============================================================================
;	将新建立的学员节点插入树中(编号树)
;============================================================================	
	mov	esi, _lpStProInfo
	mov	ebx, _lpNewStudent

	.if	[esi].lpNumberStudent == null		;如果是首次插入
	
;============================================================================
		;插入到编号树中
;============================================================================	
		mov	_dwBuf, null
		Invoke	_InsertNumberTree, addr _dwBuf , _lpNewStudent, [ebx].dwNumber
		.if	!eax
			jmp	Error
		.endif
		
		mov	ebx, eax
		Invoke	JWritePrivateProfileInt, 	;写入注册表中
			offset SzSection, offset SzKeylpNumStu, ebx , offset SzIniFile	
		.if	!eax
			jmp	Error
		.endif		
	
		mov	esi, _lpStProInfo		;更新文件中的结构
		push	ebx
		pop	[esi].lpNumberStudent
		
		mov	_dwBuf, NULL
;============================================================================
		;插入到名称树中	
;============================================================================	
		mov	ebx, _lpNewStudent		;插入名称树中
		Invoke	_InsertNameTree, addr _dwBuf, _lpNewStudent, addr [ebx].szName
		.if	!eax
			jmp	Error
		.endif		
		
		mov	ebx, eax
		Invoke	JWritePrivateProfileInt, 	;写入注册表中
			offset SzSection, offset SzKeylpNameStu, ebx , offset SzIniFile	
		.if	!eax
			jmp	Error
		.endif	
		
		mov	esi, _lpStProInfo		;更新文件中的结构
		push	ebx
		pop	[esi].lpNameStudent	
;============================================================================
	.else				
		lea	ecx, [esi].lpNumberStudent	;插入到编号树中	
		Invoke	_InsertNumberTree, ecx ,_lpNewStudent, [ebx].dwNumber
		.if	!eax
			jmp	Error
		.endif	
		
		mov	esi, _lpStProInfo
		mov	ebx, _lpNewStudent
		lea	ecx, [esi].lpNameStudent	;插入到名称树中
		Invoke	_InsertNameTree, ecx ,_lpNewStudent, addr [ebx].szName	
	.endif
;============================================================================
	mov	eax, true
Error:
	ret
_AddStudentNode endp

End