惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Visual Studio Blog
Google DeepMind News
Google DeepMind News
V
V2EX
B
Blog RSS Feed
有赞技术团队
有赞技术团队
博客园 - Franky
美团技术团队
月光博客
月光博客
酷 壳 – CoolShell
酷 壳 – CoolShell
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
腾讯CDC
云风的 BLOG
云风的 BLOG
L
LangChain Blog
GbyAI
GbyAI
The Cloudflare Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
C
Check Point Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Stack Overflow Blog
Stack Overflow Blog
博客园 - 【当耐特】
The Register - Security
The Register - Security
大猫的无限游戏
大猫的无限游戏
D
Docker
Vercel News
Vercel News
Blog — PlanetScale
Blog — PlanetScale
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 司徒正美
人人都是产品经理
人人都是产品经理
雷峰网
雷峰网
阮一峰的网络日志
阮一峰的网络日志
P
Proofpoint News Feed
N
Netflix TechBlog - Medium
博客园_首页
A
About on SuperTechFans
J
Java Code Geeks
量子位
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
MongoDB | Blog
MongoDB | Blog
Recent Announcements
Recent Announcements
G
Google Developers Blog
小众软件
小众软件
博客园 - 叶小钗
WordPress大学
WordPress大学
博客园 - 聂微东
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Martin Fowler
Martin Fowler
S
SegmentFault 最新的问题
F
Full Disclosure
Jina AI
Jina AI
H
Help Net Security

博客园 - smalldust

NTT大规模网络故障 亲手焙制一个极其简单但却极其实用的Reflector插件 关于园子里讨论的软件的追求的杂谈 Reflector保护方法初探 .Net 2.0 原汁原味读取注册表 为什么我不用IE7和FireFox 给.Net程序员的PInvoke Tips [2]: Are Strings Immutable? 除了Exception,你还能throw什么? CLR Team程序员出的难题,有兴趣的朋友不妨挑战一下 - smalldust - 博客园 Google Trends发布 也谈用反射实现Enum→String映射:一种重视性能的方法 针对个例的、社区性的维基系统设想(草稿) 数学的思考方式 VS 程序的思考方式 WinForm程序启动时不显示主窗体的实现方法 C#程序模拟鼠标操作 [Simulate Mouse Movement and Click Programmatically] 如何在C#中获取“当前目录” Windows Vista将推迟到2007年1月发布 .Net 2.0实例学习:WebBrowser页面与WinForm交互技巧 使用关键字作为自定义标识符
给.Net程序员的PInvoke Tips [1]: String is Sometimes an Integer
smalldust · 2006-06-13 · via 博客园 - smalldust

习惯了.Net编程,尤其是以前几乎没有用过Basic,Pascal,C/C++等“较古老”的语言的程序员,遇到PInvoke,尤其是COM interoperation的时候,往往是一头雾水不得要领。相信在在将来,一方面是从C#,.Net以及Java开始学习编程的人会越来越多,一方面整个Windows也逐渐往Managed平台迁移,懂得如何直接同Win32API打交道的程序员就会越来越少(当然绝对数量肯定还是很多的,至少比用DDK写驱动程序的多吧……)

但是,对于.Net程序员来说,虽然不用直接使用C++调用API,但是PInvoke的知识还是必不可少的,对于Handle,Unmanaged Thread,COM等等最基本的东西仍然是必修课。因此,我才想通过Tips的方式,对我所了解的最基础的内容做一些整理。

说了很多废话,首先让我们看一个PInvoke时遇到的实际例子:

话说某个C#写的WindowsForm中,要对资源DLL进行读取(Resource DLL,Win32格式的只含有资源的DLL)。其中要用到这么一个API函数:

BOOL EnumResourceTypes(
    HMODULE hModule,
    ENUMRESTYPEPROC lpEnumFunc,
    LONG_PTR lParam
);

其中,ENUMRESTYPEPROC,其定义为:

BOOL CALLBACK EnumResTypeProc(
    HMODULE hModule,
    LPTSTR lpszType,
    LONG_PTR lParam
);

其中的LPTSTR,在C#里应该怎么写呢?我想,想也不想就写出下面代码的朋友不会太少吧:

    public delegate bool EnumResTypeProc(IntPtr hModule, string strType, IntPtr param);

很遗憾,这段代码在执行的时候会出错——用Marshal.GetLastWin32Error()查看,发现是ERROR_NOACCESS(998)。
说明,程序访问了禁止访问的地址空间。经测试,hModule是正确无误的DLL库的Handle,最后的参数也被设置为NULL,那么原因只能是这个strType了。

该参数的类型是LPTSTR,在Unicode环境下也即LPWSTR,也即WCHAR * ——没错啊,不就是字符串么?

但是我们仔细看一下MSDN的说明,就会发现,Windows提供了一些标准的资源类型;例如RT_ICON,RT_STRING,RT_VERSION,……仔细看这些类型的定义就会发现它们都是数字。

也就是说,lpszType这个参数,即可以被当作数字来使用,也可以把其数值当作指针,指向内存中的某个地址。在这种时候,显然就不能用.Net的String类型来表示。正确的做法是,使用IntPtr,如果判断其值不是系统与定义的类型,再将其Marshal为指向字符串的指针,获取字符串。

结论:在.Net中,数据类型是“强类型”的,你不能把一只猫作为一匹马来使用,因此保证了类型安全;但是在C/C++等语言为基础的Win32中,类型却是可以任意变换的,一只猫你可以当作一匹马,甚至一头猪……没有人来保证这种变换的安全性,但是也正因此带来了灵活性。Windows的各种数据类型当中,很多类型都是名称不同,但是实际上的数据结构是相同的(例如关于Handle,就有HWND,HMENU,HINSTANCE,HHOOK等等许多种)。相反,同一个数据类型,时而代表数值,时而代表指针的例子也不少见。