BCB直接访问硬件端口和物理内存 - WinIO的应用
BCB直接访问硬件端口和物理内存 - WinIO的应用
(读硬盘参数和主板BIOS信息, 支持 Win9x/NT/2k/XP/2003)
关于直接访问端口, 有很多网站很多文章都讨论过, 但总找不到非常理想的办法。
我这里用的是 Yariv Kaplan 的 WinIo 2.0。虽然 WinIO 也有缺陷, 但是是我用过的当中最好的了。
WinIO 是免费的, 并且是开放源代码的, 可以直接到他的主页下载, 也可以在这里下载。
Yariv Kaplan 的主页: http://www.internals.com/
WinIO 的使用非常简单, 在程序的开始调用 InitializeWinIo(); 初始化 WinIO, 在程序的结束使用 ShutdownWinIo();
这样就可以在程序里直接访问端口和物理内存了。
在这里仍然用的是读硬盘参数和主板BIOS信息。
本站在《硬盘参数读取程序》这篇文章里曾经介绍过利用 WinIO 读取硬盘参数, 很多人提出程序太复杂, 并且在程序启动时调用经常无效,
在这里简化了程序, 并且改善了性能, 在程序启动时调用也可读出参数了。
按钮Button1: 硬盘参数:
型 号: MAXTOR 6L040J2
序 列 号: 662202841232
固件版本: AR1.0400
容 量: 38172 Mb
柱 面 数: 16383
磁 头 数: 16
扇 区 数: 63
缓存容量: 1818 kb
ECC 字节: 4 bytes
LBA 支持: 是
DMA 支持: 是 按钮Button2: BIOS信息:
Award Modular BIOS v6.00PG
Copyright (C) 1984-2001, Award Software, Inc.
05/14/02
05/14/2002-i815-ITE87X2-6A69RPQRS-00
有关读写端口函数 inportb 和 outportb 等函数: 在 Win2000 等 NT 内核的 OS 可直接用汇编访问端口, 但 Win9x 反而不可以
#include "WinIO.h"
//---------------------------------------------------------------------------
unsigned char inportbNT(unsigned short p) { asm mov dx, p; asm in al, dx; return _AL; }
unsigned short inportwNT(unsigned short p) { asm mov dx, p; asm in ax, dx; return _AX; }
unsigned long inportdNT(unsigned short p) { asm mov dx, p; asm in eax,dx; return _EAX;}
void outportbNT(unsigned short p, unsigned char v) { asm mov dx, p; asm mov al, v; asm out dx,al; }
void outportwNT(unsigned short p, unsigned short v) { asm mov dx, p; asm mov ax, v; asm out dx,ax; }
void outportdNT(unsigned short p, unsigned long v) { asm mov dx, p; asm mov eax,v; asm out dx,eax;}
//---------------------------------------------------------------------------
unsigned char inportb9x(unsigned short p) { unsigned long v = 0; GetPortVal(p, &v, 1); return v; }
unsigned short inportw9x(unsigned short p) { unsigned long v = 0; GetPortVal(p, &v, 2); return v; }
unsigned long inportd9x(unsigned short p) { unsigned long v = 0; GetPortVal(p, &v, 4); return v; }
void outportb9x(unsigned short p, unsigned char v) { SetPortVal(p,v,1); }
void outportw9x(unsigned short p, unsigned short v) { SetPortVal(p,v,2); }
void outportd9x(unsigned short p, unsigned long v) { SetPortVal(p,v,4); }
//---------------------------------------------------------------------------
unsigned char (*inportb)(unsigned short) = inportbNT;
unsigned short (*inportw)(unsigned short) = inportwNT;
unsigned long (*inportd)(unsigned short) = inportdNT;
void (*outportb)(unsigned short, unsigned char ) = outportbNT;
void (*outportw)(unsigned short, unsigned short) = outportwNT;
void (*outportd)(unsigned short, unsigned long ) = outportdNT;
//---------------------------------------------------------------------------
void InitPortFuncs(void)
{
OSVERSIONINFO osVer = {sizeof(OSVERSIONINFO)};
GetVersionEx(&osVer);
if(osVer.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
inportb = inportbNT; outportb = outportbNT;
inportw = inportwNT; outportw = outportwNT;
inportd = inportdNT; outportd = outportdNT;
}
else
{
inportb = inportb9x; outportb = outportb9x;
inportw = inportw9x; outportw = outportw9x;
inportd = inportd9x; outportd = outportd9x;
}
}
上面的程序 InitPortFuncs 就是判断操作系统是否为 NT 内核, 并且选择合适的函数来访问端口。
经过这样处理, 在 Win2000 下访问端口的速度就要比 98 的快了, Win2000 的速度比较理想。
具体程序: Button1 是读硬盘参数, Button2 是读主板BIOS信息
#include "WinIO.h"
#pragma link "WinIo_bc.lib"
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
bWinIoInitOK = InitializeWinIo();
if(!bWinIoInitOK)
{
Application->MessageBox("不能装载 WinIO 程序!","错误信息",MB_OK|MB_ICONSTOP);
Application->Terminate();
}
InitPortFuncs();
}
//---------------------------------------------------------------------------
__fastcall TForm1::~TForm1()
{
if(bWinIoInitOK)
ShutdownWinIo();
}
//---------------------------------------------------------------------------
bool ReadHddParams(unsigned short *params, int pn, int dn)
{
int i,IdePort[2] = {0x1f0, 0x170}; //primary & secondary IDE Controller
unsigned char HD_Selection[2]={0xa0,0xb0}; // Master Disk: 1010 0000, Slave Disk: 1011 0000
unsigned short BasePort = IdePort[pn];
for(i=0;i<500;i++) //Get HDC Status, wait until HDC not busy
{
if((inportb(BasePort+7)&0x80)==0)
break; //hdc is ready
Sleep(1);
}
if(i>=300)return false; //HDC no response
outportb(BasePort+6, HD_Selection[dn]); //master or slave hard disk
outportb(BasePort+7, 0x10); //HDD status
for(i=0;i<300;i++) //Get HDD Status, wait until HDD not busy
{
if((inportb(BasePort+7)&0x80)==0)
break;
Sleep(1);
}
if(i>=300)return false; //HDC no response
if(inportb(BasePort+7)!=0x50)return false; //HDD ready: 0101 0000
outportb(BasePort+6, HD_Selection[dn]); //master or slave hard disk
outportb(BasePort+7, 0xec); //HDD parameters
for(i=0;i<300;i++) //wait for parameters retrieved
{
if(inportb(BasePort+7)==0x58) //retrieved OK
break;
Sleep(1);
}
if(i>=300)return false; //parameters retrieved error
for(i=0;i<256;i++)
params[i]=inportw(BasePort);
return true;
}
//---------------------------------------------------------------------------
void WordToStr(unsigned char *s, unsigned short *w, int n) //硬盘参数转成字符串
{
int i;
for(i=0; i<n; i++)
{
s[i*2] = w[i]>>8;
s[i*2+1] = w[i]&0x00ff;
}
s[i*2]=0;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
DWORD dwOldProcessP = GetPriorityClass(GetCurrentProcess());
DWORD dwOldThreadP = GetThreadPriority(GetCurrentThread());
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
AnsiString idename[2] = {"IDE0","IDE1"}, diskname[2] = {"主盘","从盘"};
unsigned short params[256]; char Str[256];
for(int pn=0; pn<2; pn++) //primary or secondary
for(int dn=0; dn<2; dn++) //master or slave
{
Memo1->Lines->Add(idename[pn]+" "+diskname[dn]+":");
if(ReadHddParams(params,pn,dn))
{
WordToStr(Str,params+27,20); Memo1->Lines->Add("型 号: "+AnsiString(Str));
WordToStr(Str,params+10,10); Memo1->Lines->Add("序 列 号: "+AnsiString(Str));
WordToStr(Str,params+23, 4); Memo1->Lines->Add("固件版本: "+AnsiString(Str));
unsigned long LbaCap = *(unsigned long *)(¶ms[60])/2048;
unsigned long NomCap = ((unsigned long)(params[1])*(params[3])*(params[6]))/2048;
Memo1->Lines->Add("容 量: " + AnsiString().sprintf("%lu Mb",LbaCap>NomCap?LbaCap:NomCap));
Memo1->Lines->Add(AnsiString().sprintf("柱 面 数: %u", params[1]));
Memo1->Lines->Add(AnsiString().sprintf("磁 头 数: %u", params[3]));
Memo1->Lines->Add(AnsiString().sprintf("扇 区 数: %u", params[6]));
bool DMA = params[49]&0x0100; //D8:是否支持DMA
bool LBA = params[49]&0x0200; //D9:是否支持LBA
Memo1->Lines->Add(AnsiString().sprintf("缓存容量: %u kb", params[21]>>1));
Memo1->Lines->Add(AnsiString().sprintf("ECC 字节: %u bytes", params[22]));
Memo1->Lines->Add(AnsiString().sprintf("LBA 支持: %s", LBA?"是":"否"));
Memo1->Lines->Add(AnsiString().sprintf("DMA 支持: %s", DMA?"是":"否"));
}
else
{
Memo1->Lines->Add("没找到硬盘");
}
Memo1->Lines->Add("");
}
SetThreadPriority(GetCurrentThread(), dwOldThreadP);
SetPriorityClass(GetCurrentProcess(), dwOldProcessP);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
HANDLE hPhyMem; //char *lpInfo = (char far *)0xf0000L;
//下面的语句让 0xf0000 地址的 65536 个字节可直接读写
char *lpInfo = MapPhysToLin((char*)0xf0000,65536,&hPhyMem);
Memo1->Lines->Add(lpInfo+0xe061); //主板BIOS名称 0xFE061
Memo1->Lines->Add(lpInfo+0xe091); //主板BIOS版权 0xFE091
Memo1->Lines->Add(lpInfo+0xfff5); //主板BIOS日期 0xFFFF5
Memo1->Lines->Add(lpInfo+0xec71); //主板BIOS序列号 0xFEC71
UnmapPhysicalMemory(hPhyMem, lpInfo);
}
//---------------------------------------------------------------------------
BCB直接访问硬件端口和物理内存 - WinIO的应用
(读硬盘参数和主板BIOS信息, 支持 Win9x/NT/2k/XP/2003)
(浏览 40794 次)
Victor Chen, (C++ 爱好者)
附完整的源程序(本页最下面的链接)
关于直接访问端口, 有很多网站很多文章都讨论过, 但总找不到非常理想的办法。
我这里用的是 Yariv Kaplan 的 WinIo 2.0。虽然 WinIO 也有缺陷, 但是是我用过的当中最好的了。
WinIO 是免费的, 并且是开放源代码的, 可以直接到他的主页下载, 也可以在这里下载。
Yariv Kaplan 的主页: http://www.internals.com/
WinIO 的使用非常简单, 在程序的开始调用 InitializeWinIo(); 初始化 WinIO, 在程序的结束使用 ShutdownWinIo();
这样就可以在程序里直接访问端口和物理内存了。
在这里仍然用的是读硬盘参数和主板BIOS信息。
本站在《硬盘参数读取程序》这篇文章里曾经介绍过利用 WinIO 读取硬盘参数, 很多人提出程序太复杂, 并且在程序启动时调用经常无效,
在这里简化了程序, 并且改善了性能, 在程序启动时调用也可读出参数了。
按钮Button1: 硬盘参数:
型 号: MAXTOR 6L040J2
序 列 号: 662202841232
固件版本: AR1.0400
容 量: 38172 Mb
柱 面 数: 16383
磁 头 数: 16
扇 区 数: 63
缓存容量: 1818 kb
ECC 字节: 4 bytes
LBA 支持: 是
DMA 支持: 是 按钮Button2: BIOS信息:
Award Modular BIOS v6.00PG
Copyright (C) 1984-2001, Award Software, Inc.
05/14/02
05/14/2002-i815-ITE87X2-6A69RPQRS-00
有关读写端口函数 inportb 和 outportb 等函数: 在 Win2000 等 NT 内核的 OS 可直接用汇编访问端口, 但 Win9x 反而不可以
#include "WinIO.h"
//---------------------------------------------------------------------------
unsigned char inportbNT(unsigned short p) { asm mov dx, p; asm in al, dx; return _AL; }
unsigned short inportwNT(unsigned short p) { asm mov dx, p; asm in ax, dx; return _AX; }
unsigned long inportdNT(unsigned short p) { asm mov dx, p; asm in eax,dx; return _EAX;}
void outportbNT(unsigned short p, unsigned char v) { asm mov dx, p; asm mov al, v; asm out dx,al; }
void outportwNT(unsigned short p, unsigned short v) { asm mov dx, p; asm mov ax, v; asm out dx,ax; }
void outportdNT(unsigned short p, unsigned long v) { asm mov dx, p; asm mov eax,v; asm out dx,eax;}
//---------------------------------------------------------------------------
unsigned char inportb9x(unsigned short p) { unsigned long v = 0; GetPortVal(p, &v, 1); return v; }
unsigned short inportw9x(unsigned short p) { unsigned long v = 0; GetPortVal(p, &v, 2); return v; }
unsigned long inportd9x(unsigned short p) { unsigned long v = 0; GetPortVal(p, &v, 4); return v; }
void outportb9x(unsigned short p, unsigned char v) { SetPortVal(p,v,1); }
void outportw9x(unsigned short p, unsigned short v) { SetPortVal(p,v,2); }
void outportd9x(unsigned short p, unsigned long v) { SetPortVal(p,v,4); }
//---------------------------------------------------------------------------
unsigned char (*inportb)(unsigned short) = inportbNT;
unsigned short (*inportw)(unsigned short) = inportwNT;
unsigned long (*inportd)(unsigned short) = inportdNT;
void (*outportb)(unsigned short, unsigned char ) = outportbNT;
void (*outportw)(unsigned short, unsigned short) = outportwNT;
void (*outportd)(unsigned short, unsigned long ) = outportdNT;
//---------------------------------------------------------------------------
void InitPortFuncs(void)
{
OSVERSIONINFO osVer = {sizeof(OSVERSIONINFO)};
GetVersionEx(&osVer);
if(osVer.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
inportb = inportbNT; outportb = outportbNT;
inportw = inportwNT; outportw = outportwNT;
inportd = inportdNT; outportd = outportdNT;
}
else
{
inportb = inportb9x; outportb = outportb9x;
inportw = inportw9x; outportw = outportw9x;
inportd = inportd9x; outportd = outportd9x;
}
}
上面的程序 InitPortFuncs 就是判断操作系统是否为 NT 内核, 并且选择合适的函数来访问端口。
经过这样处理, 在 Win2000 下访问端口的速度就要比 98 的快了, Win2000 的速度比较理想。
具体程序: Button1 是读硬盘参数, Button2 是读主板BIOS信息
#include "WinIO.h"
#pragma link "WinIo_bc.lib"
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
bWinIoInitOK = InitializeWinIo();
if(!bWinIoInitOK)
{
Application->MessageBox("不能装载 WinIO 程序!","错误信息",MB_OK|MB_ICONSTOP);
Application->Terminate();
}
InitPortFuncs();
}
//---------------------------------------------------------------------------
__fastcall TForm1::~TForm1()
{
if(bWinIoInitOK)
ShutdownWinIo();
}
//---------------------------------------------------------------------------
bool ReadHddParams(unsigned short *params, int pn, int dn)
{
int i,IdePort[2] = {0x1f0, 0x170}; //primary & secondary IDE Controller
unsigned char HD_Selection[2]={0xa0,0xb0}; // Master Disk: 1010 0000, Slave Disk: 1011 0000
unsigned short BasePort = IdePort[pn];
for(i=0;i<500;i++) //Get HDC Status, wait until HDC not busy
{
if((inportb(BasePort+7)&0x80)==0)
break; //hdc is ready
Sleep(1);
}
if(i>=300)return false; //HDC no response
outportb(BasePort+6, HD_Selection[dn]); //master or slave hard disk
outportb(BasePort+7, 0x10); //HDD status
for(i=0;i<300;i++) //Get HDD Status, wait until HDD not busy
{
if((inportb(BasePort+7)&0x80)==0)
break;
Sleep(1);
}
if(i>=300)return false; //HDC no response
if(inportb(BasePort+7)!=0x50)return false; //HDD ready: 0101 0000
outportb(BasePort+6, HD_Selection[dn]); //master or slave hard disk
outportb(BasePort+7, 0xec); //HDD parameters
for(i=0;i<300;i++) //wait for parameters retrieved
{
if(inportb(BasePort+7)==0x58) //retrieved OK
break;
Sleep(1);
}
if(i>=300)return false; //parameters retrieved error
for(i=0;i<256;i++)
params[i]=inportw(BasePort);
return true;
}
//---------------------------------------------------------------------------
void WordToStr(unsigned char *s, unsigned short *w, int n) //硬盘参数转成字符串
{
int i;
for(i=0; i<n; i++)
{
s[i*2] = w[i]>>8;
s[i*2+1] = w[i]&0x00ff;
}
s[i*2]=0;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
DWORD dwOldProcessP = GetPriorityClass(GetCurrentProcess());
DWORD dwOldThreadP = GetThreadPriority(GetCurrentThread());
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
AnsiString idename[2] = {"IDE0","IDE1"}, diskname[2] = {"主盘","从盘"};
unsigned short params[256]; char Str[256];
for(int pn=0; pn<2; pn++) //primary or secondary
for(int dn=0; dn<2; dn++) //master or slave
{
Memo1->Lines->Add(idename[pn]+" "+diskname[dn]+":");
if(ReadHddParams(params,pn,dn))
{
WordToStr(Str,params+27,20); Memo1->Lines->Add("型 号: "+AnsiString(Str));
WordToStr(Str,params+10,10); Memo1->Lines->Add("序 列 号: "+AnsiString(Str));
WordToStr(Str,params+23, 4); Memo1->Lines->Add("固件版本: "+AnsiString(Str));
unsigned long LbaCap = *(unsigned long *)(¶ms[60])/2048;
unsigned long NomCap = ((unsigned long)(params[1])*(params[3])*(params[6]))/2048;
Memo1->Lines->Add("容 量: " + AnsiString().sprintf("%lu Mb",LbaCap>NomCap?LbaCap:NomCap));
Memo1->Lines->Add(AnsiString().sprintf("柱 面 数: %u", params[1]));
Memo1->Lines->Add(AnsiString().sprintf("磁 头 数: %u", params[3]));
Memo1->Lines->Add(AnsiString().sprintf("扇 区 数: %u", params[6]));
bool DMA = params[49]&0x0100; //D8:是否支持DMA
bool LBA = params[49]&0x0200; //D9:是否支持LBA
Memo1->Lines->Add(AnsiString().sprintf("缓存容量: %u kb", params[21]>>1));
Memo1->Lines->Add(AnsiString().sprintf("ECC 字节: %u bytes", params[22]));
Memo1->Lines->Add(AnsiString().sprintf("LBA 支持: %s", LBA?"是":"否"));
Memo1->Lines->Add(AnsiString().sprintf("DMA 支持: %s", DMA?"是":"否"));
}
else
{
Memo1->Lines->Add("没找到硬盘");
}
Memo1->Lines->Add("");
}
SetThreadPriority(GetCurrentThread(), dwOldThreadP);
SetPriorityClass(GetCurrentProcess(), dwOldProcessP);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
HANDLE hPhyMem; //char *lpInfo = (char far *)0xf0000L;
//下面的语句让 0xf0000 地址的 65536 个字节可直接读写
char *lpInfo = MapPhysToLin((char*)0xf0000,65536,&hPhyMem);
Memo1->Lines->Add(lpInfo+0xe061); //主板BIOS名称 0xFE061
Memo1->Lines->Add(lpInfo+0xe091); //主板BIOS版权 0xFE091
Memo1->Lines->Add(lpInfo+0xfff5); //主板BIOS日期 0xFFFF5
Memo1->Lines->Add(lpInfo+0xec71); //主板BIOS序列号 0xFEC71
UnmapPhysicalMemory(hPhyMem, lpInfo);
}
//---------------------------------------------------------------------------
BCB直接访问硬件端口和物理内存 - WinIO的应用的更多相关文章
- 实战DeviceIoControl 之六:访问物理端口
Q 在NT/2000/XP中,如何读取CMOS数据? Q 在NT/2000/XP中,如何控制speaker发声? Q 在NT/2000/XP中,如何直接访问物理端口? A 看似小小问题,难倒多少好汉! ...
- 在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口(老罗学习笔记4)
在上两篇文章中,我们介绍了如何为Android系统的硬件编写驱动程序,包括如何在Linux内核空间实现内核驱动程序和在用户空间实现硬件抽象层接口.实现这两者的目的是为了向更上一层提供硬件访问接口,即为 ...
- 只允许指定IP访问指定端口 ufw
那天工作需要在服务器上指定ip才可以访问指定端口,配置命令如下: (服务器是ubuntu 14.04系统) apt-get install ufw ufw enable ufw default den ...
- 为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
在上两篇文章中,我们介绍了如何为Android系统的硬件编写驱动程序,包括如何在Linux内核空间实现内核驱动程序和在用户空间实现硬件抽象层接 口.实现这两者的目的是为了向更上一层提供硬件访问接口,即 ...
- Linux笔记(固定USB摄像头硬件端口,绑定前后置摄像头)
在Android的系统会有前置摄像头和后置摄像头的定义,摄像头分为SOC类型的摄像头和USB这一类的摄像头,接下要分析就是USB摄像头这一类 . 一般在android或者linux系统中分析一个模块, ...
- tomcat第一次使用正常启动后访问8080端口报404错误
问题:tomcat第一次使用正常启动后访问8080端口报404错误 解决办法:双击tomcat调出tomcat的xml文件页面,Server Locations 默认是选第一行即Use Workspa ...
- iptables/mysql设置指定主机访问指定端口
本周,运维告知部署的服务被扫描发现漏洞,涉及的软件分别为mysql,ZooKeeper与Elasticsearch. 因为最近任务繁重,人力资源紧张,因此无法抽出更多时间调整代码,添加权限认证. 与软 ...
- 解决Windows10下无法对docker容器进行端口访问(端口映射的问题)
解决Windows10下无法对docker容器进行端口访问(端口映射的问题) 问题详情 在Windows10系统服务器中安装了docker和docker-compose 并尝试在其中运行Nginx服务 ...
- iptables只允许指定ip地址访问指定端口
首先,清除所有预设置 iptables -F#清除预设表filter中的所有规则链的规则 iptables -X#清除预设表filter中使用者自定链中的规则 其次,设置只允许指定ip地址访问指定端口 ...
随机推荐
- Elasticsearch增、删、改、查操作深入详解
引言: 对于刚接触ES的童鞋,经常搞不明白ES的各个概念的含义.尤其对“索引”二字更是与关系型数据库混淆的不行.本文通过对比关系型数据库,将ES中常见的增.删.改.查操作进行图文呈现.能加深你对ES的 ...
- javaweb项目配jdk
1.首先在你的本地安装jdk1.6,并将之添加至myeclipse中:window--preferences--installed jres,add添加,格式D:\program files\jd ...
- C++ 类的继承五(类继承中的static关键字)
//类继承中的static关键字 #include<iostream> using namespace std; /* 派生类中的静态成员 基类定义的静态成员,将被所有派生类共享 根据静态 ...
- 第一百五十一节,封装库--JavaScript,表单验证--密码确认验证--回答验证--电子邮件验证加自动补全
封装库--JavaScript,表单验证--密码确认验证--回答验证--电子邮件验证加自动补全 效果图 html <div id="reg"> <h2 class ...
- <!>字体效果
<h1>...</h1>标题字(最大) <h6>...</h6>标题字(最小) <b>...</b>粗体字 <strong ...
- Img src用base64数据
<img src='data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgG ...
- 蓝桥杯 第三届C/C++预赛真题(7) 放棋子(水题)
今有 6 x 6 的棋盘格.其中某些格子已经预先放好了棋子.现在要再放上去一些,使得:每行每列都正好有3颗棋子.我们希望推算出所有可能的放法.下面的代码就实现了这个功能. 初始数组中,“1”表示放有棋 ...
- iOS conformsToProtocol
- (BOOL)conformsToProtocol:(Protocol *)aProtocol; 是用来检查对象(包括其祖先)是否实现了指定协议类的方法. 今天遇到一个问题,一个类没有实现proto ...
- VR应用开发遍地走的日子还有多远
从上世纪60年代美国计算机科学家Ivan Sutherland发明的第一款真正意义上的虚拟现实头盔,到Facebook以20亿美元收购"虚拟现实之眼"Oculus Rift,大批厂 ...
- iOS-地图开发 Plist文件设置权限
解决办法: 在.Plist文件中添加 <key>NSLocationUsageDescription</key> <string>请点击“好”以允许访问. 若不允许 ...