Java 8 Stream API

.
https://blog.csdn.net/hxhaaj/article/details/80725857
https://www.cnblogs.com/jimoer/p/10995574.html

如果你正在使用 Java 编程,那么可以使用 Java 8 Stream API 来做进一步的类比。考虑如下等价概念:

TABLE          : Stream>
 SELECT         : map() 
 DISTINCT       : distinct()
 JOIN           : flatMap()
 WHERE / HAVING : filter()
 GROUP BY       : collect()
 ORDER BY       : sorted()
 UNION ALL      : concat()

在 Java 8 中,“一切都是流”(至少在你开始使用流时是这样)。无论如何转换流,例如,使用 map() 或 filter() 转换,结果类型始终都是流。

mysql主从一致性校验工具-pt

一、环境

1、系统环境

系统IP主机名说明server_id
centos6.7MasterIPmaster数据库:主177 
centos6.7SlaveIPslave数据库:从148

2、软件环境

软件版本安装方式说明
pt工具3.0.4编译安装这是一个综合工具包,包含很多pt命令
mysql数据库5.6.37yum安装主从环境

3、需要用到库

库名表名用途
 perconachecksums 存储pt命令监测的结果,第一次执行检测命令时会自己创建 修复工具修复的时候会读取该表

#本表格也可以自己创建,在使用pt工具的时候指定库表名字,详见下面的参数解释。

注意:自建库表测试尚未通过。

二、为什么要做主从一致性监测

1、主从复制是基于binlog的逻辑复制,难免出现复制数据不一致的风险

2、这个风险不但会引起用户数据访问前后不一致的风险

3、而且会导致后续复制出现1032、1062错误进而引起复制架构停滞的隐患

4、为了及时发现并解决这个问题

5、我们需要定期或不定期地开展主从复制数据一致性的校验和修复工作

三、主从一致性监测原理

四、pt工具监测

用到的命令:

1、pt-table-check       #监测主从一致

2、pt-table-sync         #修复主从一致

mysql主从一致性校验,基于pt工具进行。

#限制及问题

1、在检查阶段,超过1000行的数据,如果没有设定索引或者主键,则报错,该表会跳过检查。

2、在修复阶段,如果表没有设置主键或索引,则修复报错,可以手动进行修复。

3、监测是基于块进行的,如果mysql表的数据没有进行分块,那么当表过大时,会造成监测一段时间后发现没有问题会跳过改表。

4、当数据库两个数据不一致,但是checksum检测一致时,没有比对出不一致。

主库和从库账号一致,密码不一致发现了这种问题。

原因:对于修改密码的操作,chencksum的值并没有发生变化。

五、安装pt工具

最好所有主库从库都安装

1、安装依赖

yum install perl-IO-Socket-SSL perl-DBD-MySQL perl-Time-HiRes perl perl-DBI -y

yum install perl-ExtUtils-CBuilder perl-ExtUtils-MakeMaker -y

2、下载安装包

1、官网下载

3、安装

 tar xzvf percona-toolkit-3.0.4_x86_64.tar.gz

 cd percona-toolkit-3.0.4

perl Makefile.PL   --安装到非默认路径PREFIX=${HOME}

 make

 make test

 make install

六、pt工具常用命令

1、创建监测账号

grant all on *.*  to   'zxfly_check'@'192.168.22.% ' identified by 'zxfly';

flush privileges;

2、监控用的表为:percona.checksum该表会自动创建。

监测:

#指定库名

pt-table-checksum --databases zxfly -u'zxfly' -p'zxfly' -hMasterIP -P3306 2>/logs/pt_error.log 1>/logs/pt_info.log

#不指定库,监测所有数据库(除information_schema、percona、performance_schema库)

pt-table-checksum --quiet -u'zxfly' -p'zxfly' -hMasterIP -P3306 2>/logs/pt_error.log 1>/logs/pt_info.log

pt-table-checksum --replicate-check-only -u'zxfly' -p'zxfly' -hMasterIP -P3306

打印修复sql:指定库表

pt-table-sync  --databases=dataname --tables=table1,table2 h=MasterIP,u=zxfly,p=zxfly h=SlaveIP,u=zxfly,p=zxfly --charset=utf8 --print 

修复:

pt-table-sync  --databases=dataname --tables=table1,table2 h=MasterIP,u=zxfly,p=zxfly h=SlaveIP,u=zxfly,p=zxfly --charset=utf8 --exec

七、pt工具常用参数

1、pt-table-checksum

参数参数说明备注
--[no]check-replication-filters不检查复制过滤器,建议启用。后面可以用--databases来指定需要检查的数据库。当前环境不需要该参数,默认开启
--no-check-binlog-format不检查复制的binlog模式,要是binlog模式是ROW,则会报错。默认是监测,使用默认值,如果添加该参数可能导致diff不出来
--replicate-check-only只显示不同步的信息。 开启这个,可以减少输出并且显示不一致的从库主机名 不过这个只是显示已经检测过的不一致信息,并不能显示当前的。
--replicate=把checksum的信息写入到指定表中,建议直接写到被检查的数据库当中。不需要指定默认会创建percona库下checksum表
-h -u -p -PmasterIP 监测账号 密码 端口 
--databases=指定需要被检查的数据库,多个则用逗号隔开。 
--tables=指定需要被检查的表,多个用逗号隔开 
--recursion-method指定监测从库的模式,默认使用processlist,也可以指定dsn 多个从库可以这样指定。--recursion-method=dsn=h=host,D=pt,t=dsns D 库名 t 表名
--quiet安静模式,最小化打印,纸打印错误行与--replicate-check-only类似,但不显示从库信息

dsn库表结构及用法为:

  1. CREATE TABLE `dsns` ( `id` int(11) NOT NULL AUTO_INCREMENT, `parent_id` int(11) DEFAULT NULL, `dsn` varchar(255) NOT NULL, PRIMARY KEY (`id`) );
  2. -- 写入从库信息
  3.  INSERT INTO dsns (parent_id,dsn) values(1, "h=replica_host,u=checksums,p=password,P=3306"); 
  4. -- 如果有多个从库,就插入多条记录. 
  5. -- 也可以按如下简写
  6. INSERT INTO dsns (parent_id,dsn) values(1, "h=replica_host");

2、pt-table-sync

参数参数说明备注
--replicate=指定通过pt-table-checksum得到的表默认会创建percona库下checksum表时不需要指定
--databases=指定执行同步的数据库在只修复指定的库时使用
--tables=指定需要被修复的表,多个用逗号隔开 
--sync-to-master指定一个DSN,即从的IP会通过show processlist或show slave status 去自动的找主。报错找不到主库时使用
h= u= p=服务器地址,账号,密码命令里有2个ip,第一次出现的是Master的地址,第2次是Slave的地址。
--print打印修复的sql语句只打印不执行。
--exec 或 --execute执行修复 
--algorithms=c指定修复算法 default Chunk,Nibble,GroupBy,Stream 在报错算法问题的时候需要指定算法
--charset=指定默认字符集如果数据中包含中文不指定次字符集的话修复不成功

3、输出信息解释

TS :完成检查的时间。
ERRORS :检查时候发生错误和警告的数量。
DIFFS :0表示一致,1表示不一致。当指定--no-replicate-check时,会一直为0,当指定--replicate-check-only会显示不同的信息。
ROWS :表的行数。
CHUNKS :被划分到表中的块的数目。
SKIPPED :由于错误或警告或过大,则跳过块的数目。
TIME :执行的时间。
TABLE :被检查的表名。

八、pt工具常见报错信息

1、监测报错(找不到从库,使用--recursion-method指定模式)

Diffs cannot be detected because no slaves were found. Please read the --recursion-method documentation for information.

2、binlog模式问题(由于指定为row行复制造成 使用--no-check-binlog-format跳过监测,不过这样有可能监测不出来主从不一致的信息,row行模式对于主从来说不需要进行主从监测)

Replica centos-1 has binlog_format ROW which could cause pt-table-checksum to break replication. Please read "Replicas using row-based replication" in the LIMITATIONS section of the tool's documentation.  If you understand the risks, specify --no-check-binlog-format to disable this check.

3、没有索引或主键导致的,在数据较少的时候(低于1000行,没有测试出该信息,超过1000行会报错)

Cannot checksum table test.t: There is no good index and the table is oversized. at ./pt-table-checksum line 6370.

4、等待信息,已检测的百分比,这是因为设置了主键但是没有索引导致没有分块且表过大造成。

Checksumming database.table:  27% 01:17 remain

九、对多个库所有数据进行监测结果

pt-table-checksum监测数据库结果:

数据大小监测花费时间监测报错的表原因
93G30m28.523s 4个 #字符集bug(工具自带,无法解决) 正式平台这些表都没有
  1个 未监测到(中文表名监测不到) 正式平台无此表
  1个没有主键或者索引(数据条数:132835)

十、pt修复工具pt-table-sync遇到的问题

在使用pt-table-checksum进行检测后,发现主从不一致的情况后可以使用pt-table-sync工具进行修复操作。

其原理为:基于pt-table-checksum监测的结果进行检查并生成修复语句去修复从库中的数据。

遇到的报错:

1、修复时候没有任何提示,但是修复报错。使用–print打印修复语句发现又乱码。

处理方法:查看表的字符集,通过--charset=命令指定默认字符集

2、报错:Failed to prepare TableSyncChunk plugin: Cannot chunk table `zxfly_zxfly1`.`mongo_task_data` using the character column guid, most likely because all values start with the same character. This table must be synced separately by specifying a list of --algorithms without the Chunk algorithm at /usr/local/bin/pt-table-sync line 4088.  while doing table on 192.168.0.177

原因是在默认的算法中,要保证主键字段的数据前一位有不一样字符出现,而该表的主键数据第一个字符是一样的。

解决办法:

使用--algorithms=参数指定算法,当然这种应该最好分库分表进行恢复。

6、修复报错(原因:没有唯一索引或主键导致的,1000以内的,1000行以上没有索引或主键在监测时就会跳过。)

Can't make changes on the master because no unique index exists at /usr/local/bin/pt-table-sync line 10591.

.

jFileServer examples

用户使用 (Token 授权)
上传: POST: /file/upload
bucket={xxx}
token={xxx}
path={xxx} // 可选项,不输入则自动生成路径

访问: GET: /file/get?path={xxx}&token={xxx}&download=0&downname={xxx}
download: 为可选项,设为2时以内联文档打开(一般用于预览),设为1时以资源下载方式处理,不设置或为0时则是以浏览器默认方式处理
downname: 为可选项,download = 1 时,文件下载的名称,不设置则以默认路径名
fsatoken: 可选项,会话授权访问标识,从cookie获取

===============================================

控制使用 (Basic Auth 授权)

POST: /control/delete
path={xxx}
POST: /control/upload
bucket={xxx}
path={xxx} // 可选项,不输入则自动生成路径
GET: /control/getaccesstoken?path={xxx},{xxx},{xxx},{xxx}&fsatoken={xxx}&ttl={xxx}
GET: /control/getuploadtoken?bucket={xxx},{xxx},{xxx},{xxx}
ttl: 可选项,授权超时时间,默认由服务配置定义
fsatoken: 可选项,会话授权标识, fsatoken存储于客户cookie, 同一fsatoken的token授权允许多次访问,随会话失效而失效

================================================

路径构成:
物理存储路径 + 桶名 + 存储路径(必需以 /为开始)
/file/ + mybucket + /2017/00/11.jpg
/file/ + mybucket + /af/ce/head.jpg

存储路径:

/file/mybucket/2017/00/11.jpg
/file/mybucket/af/ce/head.jpg

访问地址:
私桶下载
http://file.example.com/a/{bucket}{path}?token={xxx}&download=1
私桶访问
http://file.example.com/a/{bucket}{path}?token={xxx}
公桶下载
http://file.example.com/a/{bucket}{path}?download=1
公桶访问
http://file.example.com/a/{bucket}{path}

================================================

授权
默认情况下,均为token授权,通过配置桶授权可允许设置为开放访问,级别到 桶
默认公开桶为 p (public的简写)

.

为什么redis集群的最大槽数是16384个?

在redis节点发送心跳包时需要把所有的槽放到这个心跳包里,以便让节点知道当前集群信息,16384=16k,在发送心跳包时使用char进行bitmap压缩后是2k(2 * 8 (8 bit) * 1024(1k) = 2K),也就是说使用2k的空间创建了16k的槽数。

虽然使用CRC16算法最多可以分配65535(2^16-1)个槽位,65535=65k,压缩后就是8k(8 * 8 (8 bit) * 1024(1k) = 8K),也就是说需要需要8k的心跳包,作者认为这样做不太值得;并且一般情况下一个redis集群不会有超过1000个master节点,所以16k的槽位是个比较合适的选择。

.

C# DateTime 日期格式化

在C#中DateTime是一个包含日期、时间的类型,此类型通过ToString()转换为字符串时,可根据传入给Tostring()的参数转换为多种字符串格式。

目录

1. 分类

2. 制式类型

3. 自定义格式类型 

1. 分类

DateTime调用ToString()传入的参数可分为制式和自定义两种:

1) 制式:系统自带的,转入特定的单个字符就可转换为系统已设定好的格式。

2) 自定义:自由组合日期代码(y、M、d、h、m、s、f)来展示丰富的日期格式。

2. 制式类型

说明:转入特定的单个字符就可转换为系统已设定好的格式。

2.1 格式来源

在Windows系统中的区域和语言(位置:控制面板 → 区域和语言)选项中可设置日期和时间的格式。

可设置的分类有:短日期、长日期、短时间、长时间等。

调用ToString()进行转换时,许多转换方式都是通过上面的4个分类进行组合。

2.2 符号对照表

2.2.1 环境

.net版本:4.0

系统版本:Win7

格式信息

2.2.2 对照表

符号 语法 示例(2016-05-09 13:09:55:2350) 格式说明
y DateTime.Now.ToString() 2016/5/9 13:09:55 短日期 长时间
d DateTime.Now.ToString("d") 2016/5/9 短日期
D DateTime.Now.ToString("D") 2016年5月9日 长日期
f DateTime.Now.ToString("f") 2016年5月9日 13:09 长日期 短时间
F DateTime.Now.ToString("F") 2016年5月9日 13:09:55 长日期 长时间
g DateTime.Now.ToString("g") 2016/5/9 13:09 短日期 短时间
G DateTime.Now.ToString("G")  2016/5/9 13:09:55 短日期 长时间
t DateTime.Now.ToString("t") 13:09 短时间
T DateTime.Now.ToString("T") 13:09:55 长时间
u DateTime.Now.ToString("u") 2016-05-09 13:09:55Z  
U DateTime.Now.ToString("U") 2016年5月9日 5:09:55 本初子午线的长日期和长时间
m DateTime.Now.ToString("m") 5月9日  
M DateTime.Now.ToString("M") 5月9日  
r DateTime.Now.ToString("r") Mon, 09 May 2016 13:09:55 GMT  
R DateTime.Now.ToString("R") Mon, 09 May 2016 13:09:55 GMT  
y DateTime.Now.ToString("y") 2016年5月  
Y DateTime.Now.ToString("Y") 2016年5月  
o DateTime.Now.ToString("o") 2016-05-09T13:09:55.2350000  
O DateTime.Now.ToString("O") 2016-05-09T13:09:55.2350000         
s DateTime.Now.ToString("s") 2016-05-09T13:09:55  

2.3 示例图

2.4 win2003版本 

在Win2003默认中的无字符、d、g、G格式中会用'-'符号替代'/'符号(短日期格式为:yyyy-m-d)。

3. 自定义格式类型

开发人员可通过英文字符(y、M、d、h、m、s、f) 分别代替(年、月、日、时、分、秒、毫秒)来自由组合日期时间格式。

3.1 符号对照表

.net版本:4.0

系统版本:Win7

符号 说明
语法 示例(2016-05-09 13:09:55:2350)
yy年份后两位DateTime.Now.ToString("yy")DateTime.Now.ToString("yy"); // => 16
yyyy4位年份DateTime.Now.ToString("yyyy")DateTime.Now.ToString("yyyy"); // => 2016
MM两位月份;单数月份前面用0填充DateTime.Now.ToString("MM")DateTime.Now.ToString("MM"); // => 05
dd日数DateTime.Now.ToString("dd")DateTime.Now.ToString("dd"); // => 09
ddd周几DateTime.Now.ToString("ddd")DateTime.Now.ToString("ddd"); // => 周一
dddd星期几DateTime.Now.ToString("dddd")DateTime.Now.ToString("dddd"); // => 星期一
hh12小时制的小时数DateTime.Now.ToString("hh") DateTime.Now.ToString("hh"); // => 01
HH24小时制的小时数DateTime.Now.ToString("HH")DateTime.Now.ToString("HH"); // => 13
mm分钟数DateTime.Now.ToString("mm")DateTime.Now.ToString("mm"); // => 09
ss秒数DateTime.Now.ToString("ss")DateTime.Now.ToString("ss"); // => 55
ff毫秒数前2位DateTime.Now.ToString("ff")DateTime.Now.ToString("ff"); // => 23
fff毫秒数前3位DateTime.Now.ToString("fff")DateTime.Now.ToString("fff"); // => 235
ffff毫秒数前4位DateTime.Now.ToString("ffff")DateTime.Now.ToString("ffff"); // => 2350
分隔符
可使用分隔符来分隔年月日时分秒。 包含的值可为:-、/、:等非关键字符 DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffff"); // => 2016-05-09 13:09:55:2350 DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:ffff"); // => 2016/05/09 13:09:55:2350 DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss:ffff dddd"); // => 2016/05/09 13:09:55:2350 星期一

3.2 示例图

.

.

windows 定时清理指定目录文件bat

eg:

删除E:\original目录下(包括子目录)所有以.png为后缀的、最后修改时间为7天前的文件

指令:

 forfiles /p E:\original /s /m *.png -d -7 /c "cmd /c del /q /f @path"

解释:

/p - 指定的路径
/s - 包括子目录
/m - 查找的文件名,支持通配符*。
/d - 指定日期,有绝对日期和相对日期, 此处-7指当前日期 的7天前

/c - 运行的命令行 表示为每个文件执行的命令。命令字符串应该
用双引号括起来。

                    默认命令是 "cmd /c echo @file"。下列变量
                    可以用在命令字符串中:
                    @file    - 返回文件名。
                    @fname   - 返回不带扩展名的文件名。
                    @ext     - 只返回文件的扩展。
                    @path    - 返回文件的完整路径。
                    @relpath - 返回文件的相对路径。
                    @isdir   - 如果文件类型是目录,返回 "TRUE";
                               如果是文件,返回 "FALSE"。
                    @fsize   - 以字节为单位返回文件大小。
                    @fdate   - 返回文件上一次修改的日期。
                    @ftime   - 返回文件上一次修改的时间。

版权声明:本文为CSDN博主「pokefade」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_19983129/article/details/73127547

mysql中的utf8mb4、utf8mb4_unicode_ci、utf8mb4_general_ci

1.utf8与utf8mb4(utf8 most bytes 4)

  • MySQL 5.5.3之后增加了utfmb4字符编码
  • 支持BMP(Basic Multilingual Plane,基本多文种平面)和补充字符
  • 最多使用四个字节存储字符

utf8mb4是utf8的超集并完全兼容utf8,能够用四个字节存储更多的字符。

标准的UTF-8字符集编码是可以使用1-4个字节去编码21位字符,这几乎包含了世界上所有能看见的语言。
MySQL里面实现的utf8最长使用3个字符,包含了大多数字符但并不是所有。例如emoji和一些不常用的汉字,如“墅”,这些需要四个字节才能编码的就不支持。

2.字符集、连接字符集、排序字符集

utf8mb4对应的排序字符集有utf8mb4_unicode_ci、utf8mb4_general_ci.

utf8mb4_unicode_ci和utf8mb4_general_ci的对比:

  • 准确性:
    • utf8mb4_unicode_ci是基于标准的Unicode来排序和比较,能够在各种语言之间精确排序
    • utf8mb4_general_ci没有实现Unicode排序规则,在遇到某些特殊语言或者字符集,排序结果可能不一致。
    • 但是,在绝大多数情况下,这些特殊字符的顺序并不需要那么精确。
  • 性能
    • utf8mb4_general_ci在比较和排序的时候更快
    • utf8mb4_unicode_ci在特殊情况下,Unicode排序规则为了能够处理特殊字符的情况,实现了略微复杂的排序算法。
    • 但是在绝大多数情况下发,不会发生此类复杂比较。相比选择哪一种collation,使用者更应该关心字符集与排序规则在db里需要统一。

作者:AmyZYX
出处:http://www.cnblogs.com/amyzhu/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

================================================================

UTF-8是使用1~4个字节,一种变长的编码格式,字符编码。mb4即 most bytes 4,使用4个字节来表示完整的UTF-8。

mysql的 utf8 编码最大字符长度为 3 字节,如果遇到 4 字节的宽字符就会插入异常了。三个字节的 UTF-8 最大能编码的 Unicode 字符是 0xffff,也就是 Unicode 中的基本多文种平面(BMP)。也就是说,任何不在基本多文本平面的 Unicode字符,都无法使用 Mysql 的 utf8 字符集存储。包括 Emoji 表情(Emoji 是一种特殊的 Unicode 编码,常见于 ios 和 android 手机上),和很多不常用的汉字,以及任何新增的 Unicode 字符等等。

总结:MySQL的utf8是utfmb3,只有三个字节,节省空间但不能表达全部的UTF-8。所以推荐使用utf8mb4。

utf8mb4_bin:将字符串每个字符用二进制数据编译存储,区分大小写,而且可以存二进制的内容。

utf8mb4_general_ci:ci即case insensitive,不区分大小写。没有实现Unicode排序规则,在遇到某些特殊语言或者字符集,排序结果可能不一致。但是,在绝大多数情况下,这些特殊字符的顺序并不需要那么精确。

utf8mb4_unicode_ci:是基于标准的Unicode来排序和比较,能够在各种语言之间精确排序,Unicode排序规则为了能够处理特殊字符的情况,实现了略微复杂的排序算法。

utf8mb4_general_ci是一个遗留的 校对规则,不支持扩展,它仅能够在字符之间进行逐个比较。utf8_general_ci校对规则进行的比较速度很快,但是与使用 utf8mb4_unicode_ci的校对规则相比,比较正确性较差。

总结:general_ci 更快,unicode_ci 更准确。但相比现在的CPU来说,它远远不足以成为考虑性能的因素,索引涉及、SQL设计才是。使用者更应该关心字符集与排序规则在db里需要统一。

版权声明:本文为CSDN博主「yzh_1346983557」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yzh_1346983557/article/details/89643071

===================================================================

.

C++ / C# 数据类型对照

C++ 输入输出    C# 
==================================
char chr[255]   O    StringBuilder
KCA_DIR            I    int
LPCSTR             I    string
int                        I    int
LPSTR              O    StringBuilder
int*                      O    out int
DWORD              I    int
DWORD*          O    out int
BOOL                 I    bool
Rc_DBMgr          I    IntPtr
long*                  O    out long
API与C#的数据类型对应关系表
API数据类型类型描述C#类型API数据类型类型描述C#类型
WORD16位无符号整数ushortCHAR字符char
LONG32位无符号整数intDWORDLONG64位长整数long
DWORD32位无符号整数uintHDC设备描述表句柄int
HANDLE句柄,32位整数intHGDIOBJGDI对象句柄int
UINT32位无符号整数uintHINSTANCE实例句柄int
BOOL32位布尔型整数boolHWM窗口句柄int
LPSTR指向字符的32位指针stringHPARAM32位消息参数int
LPCSTR指向常字符的32位指针StringLPARAM32位消息参数int
BYTE字节byteWPARAM32位消息参数int
Wtypes.h 中的非托管类型 非托管 C 语言类型 托管类名 说明
HANDLE void* System.IntPtr 32 位
BYTE unsigned char System.Byte 8 位
SHORT short System.Int16 16 位
WORD unsigned short System.UInt16 16 位
INT int System.Int32 32 位
UINT unsigned int System.UInt32 32 位
LONG long System.Int32 32 位
BOOL long System.Int32 32 位
DWORD unsigned long System.UInt32 32 位
ULONG unsigned long System.UInt32 32 位
CHAR char System.Char 用 ANSI 修饰。
LPSTR char* System.String 或System.StringBuilder 用 ANSI 修饰。
LPCSTR Const char* System.String 或System.StringBuilder 用 ANSI 修饰。
LPWSTR wchar_t* System.String 或System.StringBuilder 用 Unicode 修饰。
LPCWSTR Const wchar_t* System.String 或System.StringBuilder 用 Unicode 修饰。
FLOAT Float System.Single 32 位
DOUBLE Double System.Double 64 位

STDINT.H

typedef signed char int8_t;
typedef signed short int int16_t;
typedef signed int int32_t;
typedef signed __INT64 int64_t;

/* exact-width unsigned integer types */
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef unsigned __INT64 uint64_t;

/* 7.18.1.2 / / smallest type of at least n bits
/ / minimum-width signed integer types */
typedef signed char int_least8_t;
typedef signed short int int_least16_t;
typedef signed int int_least32_t;
typedef signed __INT64 int_least64_t;

/* minimum-width unsigned integer types */
typedef unsigned char uint_least8_t;
typedef unsigned short int uint_least16_t;
typedef unsigned int uint_least32_t;
typedef unsigned __INT64 uint_least64_t;

/* 7.18.1.3 / / fastest minimum-width signed integer types */
typedef signed int int_fast8_t;
typedef signed int int_fast16_t;
typedef signed int int_fast32_t;
typedef signed __INT64 int_fast64_t;

/* fastest minimum-width unsigned integer types */
typedef unsigned int uint_fast8_t;
typedef unsigned int uint_fast16_t;
typedef unsigned int uint_fast32_t;
typedef unsigned __INT64 uint_fast64_t;

typedef signed __INT64 intptr_t;
typedef unsigned __INT64 uintptr_t;

.

C# 调用C++DLL传递指向指针的指针参数的方法

C++结构体定义:

struct DeviceInfo
{    
    char szDeviceName[DEVICE_NAME_LEN];
    char szMACAddress[MAC_ADDRESS_LEN];        
    char szDeviceIP[DEVICE_IP_LEN];
};

C#结构体的定义:

[StructLayout(LayoutKind.Sequential)]
public struct DeviceInfo
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
    public string szDeviceName;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 13)]
    public string szMACAddress;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)]
    public string szDeviceIP; 


}

情况1:C++的dll负责分配内存

C++导出函数的声明

#define DLL_API  extern "C"  __declspec(dllexport)
DLL_API int findAllDevices(DeviceInfo** ppDeviceInfoList,int * pCount);

C#导入函数的声明

[DllImport("IPAlter_d.dll")]
public extern static int findAllDevices(out IntPtr pDeviceInfo, ref int pCount);

C#的调用方法:

IntPtr pBuff = new IntPtr();//直接new一个参数即可
 if (IPAlter.findAllDevices(out pBuff, ref cout) != 1)
 {
     System.Console.WriteLine("搜索失败!");
     System.Console.ReadLine();
     return;
 }
 for (int i = 0; i < cout; i++)
 {
IntPtr pPonitor = new IntPtr(pBuff.ToInt64() + Marshal.SizeOf(typeof(DeviceInfo)) * i);

System.Console.WriteLine(((DeviceInfo)Marshal.PtrToStructure(pPonitor, typeof(DeviceInfo))).szDeviceName); 
 System.Console.WriteLine(((DeviceInfo)Marshal.PtrToStructure(pPonitor, typeof(DeviceInfo))).szDeviceIP); 
 System.Console.WriteLine(((DeviceInfo)Marshal.PtrToStructure(pPonitor, typeof(DeviceInfo))).szMACAddress);
 
 }

情况2:C#负责分配内存

C++导出函数的声明:

DLL_API int findAllDevice(DeviceInfo* ppDeviceInfoList,int * pCount);

 C#导入函数的声明:

[DllImport("IPAlter_d.dll")]
public extern static int findAllDevice(IntPtr pDeviceInfo, ref int pCount);

 C#的调用方法:

DeviceInfo[] DeviceInfoList = new DeviceInfo[50];
 int size = Marshal.SizeOf(typeof(DeviceInfo)) * 50;
 byte[] bytes = new byte[size];
 IntPtr pBuff = Marshal.AllocHGlobal(size);
 if (IPAlter.findAllDevice(pBuff, ref cout) != 1)
 {
     System.Console.WriteLine("搜索失败!");
     System.Console.ReadLine();
     return;
 }
 for (int i = 0; i < cout; i++)
 {

IntPtr pPonitor = new IntPtr(pBuff.ToInt64() + Marshal.SizeOf(typeof(DeviceInfo)) * i);
 DeviceInfoList[i] = (DeviceInfo)Marshal.PtrToStructure(pPonitor, typeof(DeviceInfo));
 System.Console.WriteLine(DeviceInfoList[i].szDeviceName);
 System.Console.WriteLine(DeviceInfoList[i].szDeviceIP);
 System.Console.WriteLine(DeviceInfoList[i].szMACAddress);


 }
 Marshal.FreeHGlobal(pBuff);

可以参考:

http://www.cnblogs.com/cxwx/archive/2010/12/29/1921140.html

http://hi.baidu.com/fanr520/item/e761f9ca0766d462f6c95d55

http://blog.csdn.net/wangweitingaabbcc/article/details/7663949

C#调用C/C++ DLL方式

1、编写一个简单的DLL

设置为导出函数,并采用C风格。函数前加extern "C" __declspec(dllexport)。定义函数在退出前自己清空堆栈,在函数前加__stdcall。

新建一个头文件,在头文件中:

/* 加入任意你想加入的函数定义*/

extern "C" _declspec(dllexport) int _stdcall add(int *x,int *y); // 声明为C编译、链接方式的外部函数
extern "C" _declspec(dllexport) int _stdcall sub(int x,int y); // 声明为C编译、链接方式的外部函数

新建一个.cpp文件

#include "mydll.h"//貌似这两个头文件的顺序不能颠倒。我试了很多次,但是不能确定。

int add(int *x,int *y)//是否可以理解为,VS2010已经默认是 _stdcall,所以函数不用添加该修饰符
{
return *x+*y;
}

int sub(int x,int y)
{
return x-y;
}

把导出函数名称变为标准名称,需加模块定义文件,就是.def文件。

内容如下:(需要注释,前面加分号就可以了,注释需要单独行)

LIBRARY "TEST"

     EXPORTS

          ;add函数

          add

   ;sub函数

         sub

LIBRARY 库名称

EXPORTS 需要导出的各个函数名称

     重新编译之后,再用Depends工具看一下,函数已经变成标准add。这个在动态加载时很有用,特别是在GetProcAddress函数寻找入库函数的时候。

2、静态调用

新建一个C#的控制台项目,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;//添加

namespace mytest
{
  class Program
  {
    //----------------------------------------------------------------------------------------------
    [DllImport("mydll.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
    extern static int add(ref int a, ref int b);

    [DllImport("mydll.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
    extern static int sub(int a, int b);
    //--------------------------------------------------------------------------------------------------

    static void Main(string[] args)
    {

      int a, b,c,d;
      a = 1;
      b = 2;
      c=d= 0;

      Console.WriteLine(add(ref a,ref b).ToString());
      Console.WriteLine(sub(b,a).ToString());
      Console.Read();
    }
  }
}

 3、动态调用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;//添加

namespace mytest
{
  class Program
  {

[DllImport("kernel32.dll")]
private extern static IntPtr LoadLibrary(String path);//path 就是dll路径 返回结果为0表示失败。
[DllImport("kernel32.dll")]
private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);//lib是LoadLibrary返回的句柄,funcName 是函数名称 返回结果为0标识失败。
[DllImport("kernel32.dll")]
private extern static bool FreeLibrary(IntPtr lib);

//声明委托
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
delegate int ADD(ref int x, ref int y);
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
delegate int SUB(int x, int y);

static void Main(string[] args)
{

string dllPath = "G:\\VS2010软件学习\\c#调用C_dll\\mytest\\mytest\\mydll.dll";
string apiName1 = "add";
string apiName2 = "sub";
//使用动态加载

IntPtr hLib = LoadLibrary(dllPath);//加载函数

IntPtr apiFunction1 = GetProcAddress(hLib, apiName1);//获取函数地址
IntPtr apiFunction2 = GetProcAddress(hLib, apiName2);//获取函数地址

int i = Marshal.GetLastWin32Error();
if (apiFunction1.ToInt32() == 0)//0表示函数没找到
return;
if (apiFunction2.ToInt32() == 0)//0表示函数没找到
return;

//获取函数接口,相当于函数指针
ADD add1 = (Delegate)Marshal.GetDelegateForFunctionPointer(apiFunction1, typeof(ADD)) as ADD;
SUB sub1 = (SUB)Marshal.GetDelegateForFunctionPointer(apiFunction2, typeof(SUB));

// //调用函数
int a, b,c;
a = 1;
b = 2;
c= 0;
//add1(ref a,ref b);
c=sub1(b,a);
// //释放句柄

FreeLibrary(hLib );

Console.WriteLine(c.ToString());
//Console.WriteLine(add(ref a,ref b).ToString());
//Console.WriteLine(sub(10,2).ToString());
Console.Read();

}

  }
}

注意:

C#时常需要调用C/C++DLL,当传递参数时时常遇到问题,尤其是传递和返回字符串时。VC++中主要字符串类型为:LPSTR,LPCSTR, LPCTSTR, string, CString, LPCWSTR, LPWSTR等,但转为C#类型却不完全相同。

类型对照:

C/C++----------C#

BSTR ---------  StringBuilder

LPCTSTR --------- StringBuilder

LPCWSTR ---------  IntPtr

handle---------IntPtr

hwnd-----------IntPtr

char *----------string

int * -----------ref int

int &-----------ref int

void *----------IntPtr

unsigned char *-----ref byte

Struct需要在C#里重新定义一个Struct

CallBack回调函数需要封装在一个委托里,delegate static extern int FunCallBack(string str);

注意在每个函数的前面加上public static extern +返回的数据类型,如果不加public ,函数默认为私有函数,调用就会出错。

在C#调用C++ DLL封装库时会出现两个问题:

1. 数据类型转换问题 
2. 指针或地址参数传送问题