如何用delphi编写dll文件
一、开使你的第一个dll专案
1.file->close all->file->new﹝dll﹞
代码:
//自动产生code如下
library project2;
//这有段废话
uses
sysutils,
classes;
{$r *.res}
begin
end.
2.加个func进来:
代码:
library project2;
uses
sysutils,
classes;
function mymax ( x , y : integer ) : integer ; stdcall ;
begin
if x > y then
result := x
else
result := y ;
end ;
//切记:library 的名字大小写没关系,可是dll-func的大小写就有关系了。
// 在 dll-func-name写成mymax与mymax是不同的。如果写错了,立即
// 的结果是你叫用到此dll的ap根本开不起来。
//参数的大小写就没关系了。甚至不必同名。如原型中是 (x,y:integer)但引
// 用时写成(a,b:integer),那是没关系的。
//切记:要再加个stdcall。书上讲,如果你是用delphi写dll,且希望不仅给
// delphi-ap也希望bcb/vc-ap等使用的话,那你最好加个stdcall ; 的指示
//参数型态:delphi有很多种它自己的变量型态,这些当然不是dll所喜欢的
// ,windows/dll的母语应该是c。所以如果要传进传出dll的参数,我们
// 尽可能照规矩来用。这两者写起来,后者会麻烦不少。如果你对c不熟
// 的话,那也没关系。我们以后再讲。
{$r *.res}
begin
end.
3.将这些可共享的func送出dll,让外界﹝就是你的delphi-ap啦﹞使用:光如此,你的ap还不能用到这些,你还要加个exports才行。
代码:
{$r *.res}
exports
mymax ;
begin
end.
4.好了,可以按 ctrl-f9编译了。此时可不要按f9。dll不是exe┌不可单独执行的,如果你按f9,会有errormsg的。这时如果dll有error,请修正之。再按ctrl-f9。此时可能有warning,不要紧,研究一下,看看就好。再按ctrl-f9,此时就『done , compiled 』。同目录就会有个 *.dll 。恭喜,大功告成了。
二、进行测试:开个新application:
1.加个tbutton
代码:
showmessage ( inttostr(mymax(30,50)) ) ;
2.告知exe到那里抓个func
代码:
//在form,interface,var后加
function mymax ( x , y : integer ) : integer ; stdcall ; external 'mytestdll.dll' ;
// mytestdll.dll为你前时写的dll项目名字
// dll名字大小写没关系。不过记得要加 extension的 .dll。在win95或nt,
// 是不必加 extension,但这两种os,可能越来越少了吧。要加extension
可以了,简单吧。
上面的例子是不是很简单?熟悉delphi的朋友可以看出以上代码和一般的delphi程序的编写基本是相同的,只是在testdll函数后多了一个stdcall参数并且用exports语句声明了testdll函数。只要编译上面的代码,就可以玫揭桓雒 狣elphi.dll的动态链接库。现在,让我们来看看有哪些需要注意的地方:
1.在dll中编写的函数或过程都必须加上stdcall调用参数。在delphi 1或delphi 2环境下该调用参数是far。从delphi 3以后将这个参数变为了stdcall,目的是为了使用标准的win32参数传递技术来代替优化的register参数。忘记使用stdcall参数是常见的错误,这个错误不会影响dll的编译和生成,但当调用这个dll时会发生很严重的错误,导致操作系统的死锁。原因是register参数是delphi的默认参数。
2.所写的函数和过程应该用exports语句声明为外部函数。
正如大家看到的,testdll函数被声明为一个外部函数。这样做可以使该函数在外部就能看到,具体方法是单激鼠标右键用“快速查看(quick view)”功能查看该dll文件。(如果没有“快速查看”选项可以从windows cd上安装。)testdll函数会出现在export table栏中。另一个很充分的理由是,如果不这样声明,我们编写的函数将不能被调用,这是大家都不愿看到的。
3.当使用了长字符串类型的参数、变量时要引用sharemem。
delphi中的string类型很强大,我们知道普通的字符串长度最大为256个字符,但delphi中string类型在默认情况下长度可以达到2g。(对,您没有看错,确实是两兆。)这时,如果您坚持要使用string类型的参数、变量甚至是记录信息时,就要引用sharemem单元,而且必须是第一个引用的。既在uses语句后是第一个引用的单元。如下例:
uses
sharemem,
sysutils,
classes;
还有一点,在您的工程文件(*.dpr)中而不是单元文件(*.pas)中也要做同样的工作,这一点delphi自带的帮助文件没有说清楚,造成了很多误会。不这样做的话,您很有可能付出死机的代价。避免使用string类型的方法是将string类型的参数、变量等声明为pchar或shortstring(如:s:string[10])类型。同样的问题会出现在当您使用了动态数组时,解决的方法同上所述。
delphi dll动态链库编写教程
windows的发展要求允许同时运行的几个程序共享一组函数的单一拷贝。动态链接库就是在这种情况下出现的。动态链接库不用重复编译或链接,一旦装入内存,dlls函数可以被系统中的任何正在运行的应用程序软件所使用,而不必再将dlls函数的另一拷贝装入内存。 任何应用程序都可以共享由装入内存的dlls管理的内存资源块。只包含共享数据的dlls称为资源文件。
1.一般工程文件的头标用program关键字,而dlls工程文件头标用library 关键字。不同的关键字通知编译器生成不同的可执行文件。用program关键字生成的是.exe文件,而用library关键字生成的是.dll文件;
2.假如dlls要输出供其它应用程序使用的函数或过程,则必须将这些函数或过程列在exports子句中。而这些函数或过程本身必须用export编译指令进行编译。
根据dlls完成的功能,我们把dlls分为如下的三类:
1.完成一般功能的dlls;2.用于数据交换的dlls;3.用于窗体重用的dlls。
一 dll的制作一般分为以下几步:
1 在一个dll工程里写一个过程或函数
2 写一个exports关键字,在其下写过程的名称。不用写参数和调用后缀。
二 参数传递
1 参数类型最好与window c++的参数类型一致。最好少用delphi的数据类型。
2 最好有返回值[即使是一个过程],来报出调用成功或失败,或状态。成功或失败的返回值最好为1[成功]或0[失败].一句话,与windows c++兼容。
3 用stdcall声明后缀。
4 最好大小写敏感。
5 无须用far调用后缀,那只是为了与windows 16位程序兼容。
三 dll的初始化和退出清理[如果需要初始化和退出清理]
1 dllproc[sysutils单元的一个pointer]是dll的入口。在此你可用你的函数替换了它的入口。但你的函数必须符合以下要求[其实就是一个回调函数]。如下:
procedurefar;stdcall;
dwreason参数有四种类型:
dll_process_attach:进程进入时 dll_process_detach进程退出时
dll_thread_attach 线程进入时 dll_thread_detach 线程退出时
在初始化部分写:
dllproc := @dllenterpoint;
dllenterpoint(dll_process_attach);
2 如form上有tdcomconnection组件,就uses activex,在初始化时写一句coinitialize (nil);
3 在退出时一定保证dcomconnection.connected := false,并且数据集已关闭。否则报地址错。
四 全局变量的使用
在widnows 32位程序中,两个应用程序的地址空间是相互没有联系的。虽然dll在内存中是一份,但变量是在各进程的地址空间中,因此你不能借助dll的全局变量来达到两个应用程序间的数据传递,除非你用内存映像文件。
五 调用静态载入
1 客户端函数声名:
1)大小写敏感。
2)与dll中的声明一样。如:far;external'yproject_dll.dll';
3)调用时传过去的参数类型最好也与windows c++一样。
4)调用时dll必须在windows搜索路径中,顺序是:当前目录;path路径;windows;widows\system;windows\ssystem32;
六 调用动态载入
1 建立一种过程类型[如果你对过程类型的变量只是一个指针的本质清楚的话,你就知道是怎么回事了]。如:
type
mypointer=procedure(form:tform);far;external;
var
hinst:thandle;
begin
hinst:=loadlibrary('yproject_dll');//load一个dll,按文件名找。
showform:=getprocaddress(hinst,'showform');//按函数名找,大小写敏感。如果你知道自动化对象的本质就清楚了。
showform(application.mainform);//找到函数入口指针就调用。
freelibrary(hinst);
end;
例:
一、开始第一个dll
1.file->close all->file->new﹝dll﹞
代码://自动产生code如下
library project2;
uses
sysutils, classes;
{$r *.res}
begin
end.
2.加个func进来:
代码:
library project2;
uses
sysutils, classes;
function stdcall ;
begin
if x > y then result := x
else result := y ;
end ;
//切记:library 的名字大小写没关系,可是dll-func的大小写就有关系了。在 dll-func-name写成mymax与mymax是不同的。如果写错了,结果是此dll的ap根本打不开。参数的大小写就没关系了。甚至不必同名。如原型中是 (x,y:integer)但引用时写成(a,b:integer),那是没关系的。
//切记:要再加个stdcall。书上讲,如果你是用delphi写dll,且希望不仅给delphi-ap也希望bcb/vc-ap等使用的话,那你最好加个stdcall ; 的指示
//参数型态:delphi有很多种它自己的变量型态,这些当然不是dll所喜欢的,windows/dll的母语是c。所以如果要传进传出dll的参数,我们尽可能照规矩来用。
{$r *.res}
begin
end.
3.将这些可共享的func送出dll,让外界﹝就是你的delphi-ap啦﹞使用:光如此,你的ap还不能用到这些,你还要加个exports才行。
代码:
{$r *.res}
exports
mymax ;
begin
end.
4.好了,可以按 ctrl-f9编译了。此时可不要按f9。dll不是exe不可单独执行的,如果你按f9,会有errormsg的。这时如果dll有error,请修正之。再按ctrl-f9。此时可能有warning,不要紧,研究一下,看看就好。再按ctrl-f9,此时就『done , compiled 』。同目录就会有个 *.dll 。恭喜,大功告成了。
二、进行测试:开个新application:
1.加个tbutton
代码:showmessage ( inttostr(mymax(30,50)) ) ;
2.告知exe到那里抓个func
代码://在form,interface,var后加
function stdcall ; external 'mytestdll.dll' ;
// mytestdll.dll为你前时写的dll项目名字
// dll名字大小写没关系。不过记得要加 extension的 .dll。在win95或nt,是不必加 extension,但这两种os,可能越来越少了吧。要加extension
三、dll注意事项
在delphi 1或delphi 2环境下该调用参数是far。从delphi 3以后将这个参数变为了stdcall,目的是为了使用标准的win32参数传递技术来代替优化的register参数。
1.在dll中编写的函数或过程都必须加上stdcall调用参数。
2.所写的函数和过程应该用exports语句声明为外部函数。
3.当使用了长字符串类型的参数、变量时要引用sharemem。
一、正如大家看到的,我们在external语句中指定了所要调用的dll文件的名称。没有写路径是因为该dll文件和调用它的主程序在同一目录下。如果该dll文件在c:\,则我们可将上面的引用语句写为external ’c:\delphi.dll’。注意文件的后缀.dll必须写上。
二、不能从dll中调用全局变量
如果我们在dll中声明了某种全局变量,如:var s:byte 。这样在dll中s这个全局变量是可以正常使用的,但s不能被调用程序使用,既s不能作为全局变量传递给调用程序。不过在调用程序中声明的变量可以作为参数传递给dll。
三、被调用的dll必须存在
这一点很重要,使用静态调用方法时要求所调用的dll文件以及要调用的函数或过程等等必须存在。如果不存在或指定的路径和文件名不正确的话,运行主程序时系统会提示“启动程序时出错”或“找不到*.dll文件”等运行错误。
四、调试技巧
1 、我们知道dll在编写时是不能运行和单步调试的。有一个办法可以,那就是在run|parameters菜单中设置一个宿主程序。在local页的host application栏中添上宿主程序的名字就可进行单步调试、断点观察和运行了。
2 、添加dll的版本信息。开场白中提到了版本信息对于dll是很重要的,如果包含了版本信息,dll的大小会增加2kb。增加这么一点空间是值得的。很不幸我们如果直接使用project|options菜单中version选项是不行的,这一点delphi的帮助文件中没有提到,经笔者研究发现,只要加一行代码就可以了。如下例:
library delphi;
uses
sysutils, classes;
{$r *.res}
//注意,上面这行代码必须加在这个位置
functionstdcall;
begin result:=i; end;
exports testdll;
begin
end.
五、调用技巧
1 、在用静态方法时,可以给被调用的函数或过程更名。在前面提到的c++编写的dll例子中,如果去掉extern ”c”语句,c++会编译出一些奇怪的函数名,原来的testc函数会被命名为@testc$s等等可笑的怪名字,这是由于c++采用了c++ name mangling技术。这个函数名在delphi中是非法的,我们可以这样解决这个问题:
改写引用函数为
functionstdcall;
external ’cpp.dll’;name ’@testc$s’;其中name的作用就是重命名。
2 、可把我们编写的dll放到windows目录下或者windows\system目录下。这样做可以在external语句中或loadlibrary语句中不写路径而只写dll的名称。但这样做有些不妥,这两个目录下有大量重要的系统dll,如果您编的dll与它们重名的话其后果简直不堪设想,况且您的编程技术还不至于达到将自己编写的dll放到系统目录中的地步吧!
六、在delphi中动态调用dll top
动态调用dll相对复杂很多,但非常灵活。为了全面的说明该问题,这次我们举一个调用由c++编写的dll的例子。首先在c++中编译下面的dll源程序。
#include
extern ”c” _declspec(dllexport)
int winapi testc(int i)
{ return i; }
编译后生成一个dll文件,在这里我们称该文件为cpp.dll,该dll中只有一个返回整数类型的函数testc。为了方便说明,我们仍然引用上面的调用程序,只是将原来的button1click过程中的语句用下面的代码替换掉了。
procedure
type
tintfunc=function(i:integer):integer;stdcall;
var
th:thandle; tp:tfarproc;
begin
th:=loadlibrary(’cpp.dll’); {装载dll}
if th>0 then
try
tp:=getprocaddress(th,pchar(’testc’));//按函数名找,大小写敏感。
if tpnil then
begin
tf:=tintfunc(tp);
edit1.text:=inttostr(tf(1)); {调用testc函数}
end
else showmessage(’testc函数没有找到’);
finally
freelibrary(th); {释放dll}
end
else showmessage(’cpp.dll没有找到’);
end;
大家已经看到了,这种动态调用技术很复杂,但只要修改参数,如修改loadlibrary(’cpp.dll’)中的dll名称为’delphi.dll’就可动态更改所调用的dll。
一、定义所要调用的函数或过程的类型
在上面的代码中我们定义了一个tintfunc类型,这是对应我们将要调用的函数testc的。在其他调用情况下也要做同样的定义工作。并且也要加上stdcall调用参数。
二、释放所调用的dll
我们用loadlibrary动态的调用了一个dll,但要记住必须在使用完后手动地用freelibrary将该dll释放掉,否则该dll将一直占用内存直到您退出windows或关机为止。
现在我们来评价一下两种调用dll的方法的优缺点。静态方法实现简单,易于掌握并且一般来说稍微快一点,也更加安全可靠一些;但是静态方法不能灵活地在运行时装卸所需的dll,而是在主程序开始运行时就装载指定的dll直到程序结束时才释放该dll,另外只有基于编译器和链接器的系统(如delphi)才可以使用该方法。动态方法较好地解决了静态方法中存在的不足,可以方便地访问dll中的函数和过程,甚至一些老版本dll中新添加的函数或过程;但动态方法难以完全掌握,使用时因为不同的函数或过程要定义很多很复杂的类型和调用方法。对于初学者,笔者建议您使用静态方法,待熟练后再使用动态调用方法。
七、例1:在dll建立一个tform
1 把你的form uses到dll中,你的form用到的关联的单元也要uses进来[这是最麻烦的一点,因为你的form或许uses了许多特殊的单元或函数]
2 传递一个application参数,用它建立form.
在dll中建立一个tmdichildform
1 dll中的mdiform.formstyle不用为fmmdichild.
2 在createform后写以下两句:
functionstdcall
var
form1: tform1;
begin
ptr:=@(application.mainform);//先把dll的mainform句柄保存起来,也无须释放,只不过是替换一下
ptr^:=longint(mainform);//用主调程序的mainform替换dll的mainform。mainform是特殊的window,它专门管理//application中的forms资源.为什么不直接application.mainform := mainform,因//为application.mainform是只读属性
form1:=tform1.create(mainform);//用参数建立
end;
备注:参数是主调程序的application.mainform
delphi中高级dll的编写和调用
根据delphi提供的有关 dll编写和调用的帮助信息,你可以很快完成一般的 dll编写和调用的 应用程序。本文介绍的主题是如何编写和调用能够传递各种参数(包括对象实例)的 dll。例如, 主叫程序传递给 dll一个adoconnection 对象示例作为参数, dll中的函数和过程调用通过该对象 实例访问数据库。
需要明确一些基本概念。对于 dll,需要在主程序中包含 exports子句,用于向外界提供调用 接口,子句中就是一系列函数或过程的名字。对于主叫方(调用 dll的应用程序或其它的 dll), 则需要在调用之前进行外部声明,即external保留字指示的声明。这些是编写 dll和调用 dll必须 具备的要素。
另外需要了解object pascal 中有关调用协议的内容。在object pascal 中,对于过程和函数 有以下五种调用协议:
指示字 参数传递顺序 参数清除者 参数是否使用寄存器
register 自左向右 被调例程 是
pascal 自左向右 被调例程 否
cdecl 自右向左 调用者 否
stdcall 自右向左 被调例程 否
safecall 自右向左 被调例程 否
这里的指示字就是在声明函数或过程时附加在例程标题之后的保留字,默认为register,即是 唯一使用 cpu寄存器的参数传递方式,也是传递速度最快的方式;
pascal: 调用协议仅用于向后兼容,即向旧的版本兼容;
cdecl: 多用于 c和 c++语言编写的例程,也用于需要由调用者清除参数的例程;
stdcall: 和safecall主要用于调用windows api 函数;其中safecall还用于双重接口。
在本例中,将使用调用协议cdecl ,因为被调用的 dll中,使用的数据库连接是由主叫方传递 得到的,并且需要由主叫方处理连接的关闭和销毁。
下面是 dll完整源程序和主叫程序完整源程序。包括以下四个文件:
project1.dpr {主叫程序}
unit1.pas {主叫程序单元}
project2.dpr {dll}
unit2.pas {dll单元}
{---------- dll 主程序 project2.dpr ----------}
library project2;
uses
sysutils,
classes,
unit2 in ‘unit2.pas‘ {form1};
{$r *.res}
{ 下面的语句用于向调用该 dll的程序提供调用接口 }
exports
dotest; { 过程来自单元unit2 }
begin
end.
{---------- dll中的单元 unit2.pas ----------}
unit unit2;
interface
uses
windows, messages, sysutils, classes, graphics, controls, forms, dialogs,
db, adodb, stdctrls, menus;
type
tform1 = class(tform)
adoconnection1: tadoconnection;{ 本地数据库连接 }
memo1: tmemo; { 用于显示信息 }
private
public
end;
{ 该过程向外提供 }
procedure dotest(h: thandle; { 获得调用者的句柄 }
aconn: tadoconnection;{ 获得调用者的数据库连接 }
s: string; { 获得一些文本信息 }
n: integer); { 获得一些数值信息 }
cdecl; { 指定调用协议 }
implementation
{$r *.dfm}
procedure dotest(h: thandle; aconn: tadoconnection; s: string; n: integer);
begin
application.handle := h; { 将过程的句柄赋值为调用者的句柄 }
{ 上面语句的作用在于, dll的句柄和调用者的句柄相同,在任务栏中就不会 }
{ 各自出现一个任务标题了。 }
with tform1.create(application) do try{ 创建窗体 }
memo1.lines.append(‘成功调用‘); { 显示一行信息 }
adoconnection1 := aconn; { 获得数据库连接的实例 }
memo1.lines.append(
adoconnection1.connectionstring +
‘ - ‘ + s + ‘ - ‘ + inttostr(n)); { 根据得到的参数显示另一行信息 }
showmodal; { 模式化显示窗体 }
finally
free; { 调用结束时销毁窗口 }
end;
end;
end.
{---------- 调用者 project1.dpr,很普通的工程文件 ----------}
program project1;
uses
forms,
unit1 in ‘unit1.pas‘ {form1};
{$r *.res}
begin
application.initialize;
application.createform(tform1, form1);
application.run;
end.
{---------- 调用者单元unit1.pas ----------}
unit unit1;
interface
uses
windows, messages, sysutils, classes, graphics, controls, forms, dialogs,
stdctrls, db, adodb;
type
tform1 = class(tform)
button1: tbutton; { 按此按钮进行调用 }
adoconnection1: tadoconnection; { 本地数据库连接,将传递给 dll }
procedure button1click(sender: tobject);{ 调用 dll}
private
public
end;
var
form1: tform1;
implementation
{$r *.dfm}
{ 外部声明必须和 dll中的参数列表一致,否则会运行时错误 }
procedure dotest(h: thandle; { 传递句柄 }
aconn: tadoconnection; { 传递数据库连接 }
s: string; { 传递文本信息 }
n: integer); { 传递数值信息 }
cdecl; { 指定调用协议 }
external ‘project2.dll‘;{ 指定过程来源 }
{ 调用过程 }
procedure tform1.button1click(sender: tobject);
begin
dotest(application.handle,
adoconnection1,
‘call ok‘,
256);
end;
end.
delphi中高级dll的编写和调用(2)
{---------- dll中的单元 unit2.pas ----------}
unit unit2;
interface
uses
windows, messages, sysutils, classes, graphics, controls, forms, dialogs,
db, adodb, stdctrls, menus;
type
tform1 = class(tform)
adoconnection1: tadoconnection;{ 本地数据库连接 }
memo1: tmemo; { 用于显示信息 }
private
public
end;
{ 该过程向外提供 }
procedure dotest(h: thandle; { 获得调用者的句柄 }
aconn: tadoconnection;{ 获得调用者的数据库连接 }
s: string; { 获得一些文本信息 }
n: integer); { 获得一些数值信息 }
cdecl; { 指定调用协议 }
implementation
{$r *.dfm}
procedure dotest(h: thandle; aconn: tadoconnection; s: string; n: integer);
begin
application.handle := h; { 将过程的句柄赋值为调用者的句柄 }
{ 上面语句的作用在于, dll的句柄和调用者的句柄相同,在任务栏中就不会 }
{ 各自出现一个任务标题了。 }
with tform1.create(application) do try{ 创建窗体 }
memo1.lines.append(‘成功调用‘); { 显示一行信息 }
adoconnection1 := aconn; { 获得数据库连接的实例 }
memo1.lines.append(
adoconnection1.connectionstring +
‘ - ‘ + s + ‘ - ‘ + inttostr(n)); { 根据得到的参数显示另一行信息 }
showmodal; { 模式化显示窗体 }
finally
free; { 调用结束时销毁窗口 }
end;
end;
end.
{---------- 调用者 project1.dpr,很普通的工程文件 ----------}
program project1;
uses
forms,
unit1 in ‘unit1.pas‘ {form1};
{$r *.res}
begin
application.initialize;
application.createform(tform1, form1);
application.run;
end.
{---------- 调用者单元unit1.pas ----------}
unit unit1;
interface
uses
windows, messages, sysutils, classes, graphics, controls, forms, dialogs,
stdctrls, db, adodb;
type
tform1 = class(tform)
button1: tbutton; { 按此按钮进行调用 }
adoconnection1: tadoconnection; { 本地数据库连接,将传递给 dll }
procedure button1click(sender: tobject);{ 调用 dll}
private
public
end;
var
form1: tform1;
implementation
{$r *.dfm}
{ 外部声明必须和 dll中的参数列表一致,否则会运行时错误 }
procedure dotest(h: thandle; { 传递句柄 }
aconn: tadoconnection; { 传递数据库连接 }
s: string; { 传递文本信息 }
n: integer); { 传递数值信息 }
cdecl; { 指定调用协议 }
external ‘project2.dll‘;{ 指定过程来源 }
{ 调用过程 }
procedure tform1.button1click(sender: tobject);
begin
dotest(application.handle,
adoconnection1,
‘call ok‘,
256);
end;
华为全场景新品发布会有哪些值得期待的亮点呢?
新能源汽车产业呈现出四大特征 预计2035年后纯电动车将成市场主体
ABB将投资将建立一座机器人工厂
诺基亚8:情怀+高配 京东预售3188元,千万人为它抛弃苹果!
中国电信表示,2018年国内市场卡槽空间规模近5亿台
如何用Delphi编写dll文件
稳压管的稳压特性解析
AliOS将参与智能网联汽车软件相关标准,落实中国智能网联汽车标准体系
电子元器件引脚标准科普
iPhone 12成二手市场中最受欢迎的5G手机
道路监控补光灯安装时需要注意哪些问题?
未来一年智能手机的发展方向分析
ip网络对讲音频模块SV-2101V高性能网络音频模块
腾讯领投,即构科技宣布完成C轮五千万美元融资
我国智能制造发展总体态势呈现“四核带动”格局
重磅!应对肺炎疫情,深圳十六条举措支持企业共渡难关!
Marvell蓄势待发,布局中国4G时代
汽车连接器测试标准
LPWAN在物联网设备中的作用是什么
最强直屏旗舰 iQOO 5将于8月24日正式开售