C#调用API向外部程序发送数据

C#调用API向外部程序发送数据

最近有可能要做一个项目。在项目中有这么一个功能,在A程序中调用B程序,同时在A程序中进行登陆后,要将A程序的登录名和密码自动填充到B程序的登陆对话框中,这样B程序就不需要再输入一次用户名和密码了,简化操作人员的操作。刚好最近闲着没事,就在怎么想怎么去实现。经过两天的折腾,基本上完成了上述功能的实现。下面就把实现方法、过程与大家进行分享。

一、原理

要实现上述功能,需要调用Win API来实现。Win32 API即为Microsoft 32位平台的应用程序编程接口(Application Programming Interface)。所有在Win32平台上运行的应用程序都可以调用这些函数。

那么在本程序的实现过程中,需要用到以下三个API函数(函数说明均从网上找的,方便大家查看),以及自己编写的一个FindWindowByIndex函数。

1、static extern int SendMessage1(IntPtr hwnd, uint wMsg, int wParam, int lParam);

顾名思义,SendMessage函数的功能是“发送消息”,即将一条消息发送到指定对象(操作系统、窗口或控件等)上,以产生特定的动作(如滚屏、修改对象外观等)。

其中四个自变量的含义和说明如下:

hWnd:对象的句柄。希望将消息传送给哪个对象,就把该对象的句柄作为实参传送。

wMsg:被发送的消息。根据具体需求和不同的对象,将不同的消息作为实参传送,以产生预期的动作。

wParam、lParam:附加的消息信息。这两个是可选的参数,用来提供关于wMsg消息更多的信息,不同的wMsg可能使用这两个参数中的0、1或2个,如果不需要哪个附加参数,则将实参赋为NULL(在VB中赋为0)。

2、public static extern IntPtr FindWindow(string className, string windowName);

FindWindow函数返回与指定字符创相匹配的窗口类名或窗口名的最顶层窗口的窗口句柄。这个函数不会查找子窗口。

lpClassName:指向一个以null结尾的、用来指定类名的字符串或一个可以确定类名字符串的原子。如果这个参数是一个原子,那么它必须是一个在调用此函数前已经通过GlobalAddAtom函数创建好的全局原子。这个原子(一个16bit的值),必须被放置在lpClassName的低位字节中,lpClassName的高位字节置零。

lpWindowName:指向一个以null结尾的、用来指定窗口名(即窗口标题)的字符串。如果此参数为NULL,则匹配所有窗口名。

返回值:如果函数执行成功,则返回值是拥有指定窗口类名或窗口名的窗口的句柄。如果函数执行失败,则返回值为 NULL 。可以通过调用GetLastError函数获得更加详细的错误信息。

3、static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

在窗口列表中寻找与指定条件相符的第一个子窗口 。该函数获得一个窗口的句柄,该窗口的类名和窗口名与给定的字符串相匹配。这个函数查找子窗口,从排在给定的子窗口后面的下一个子窗口开始。在查找时不区分大小写。

hwndParent:要查找的子窗口所在的父窗口句柄(如果设置了hwndParent,则表示从这个hwndParent指向的父窗口中搜索子窗口)。如果hwndParent为 0 ,则函数以桌面窗口为父窗口,查找桌面窗口的所有子窗口。Windows NT5.0 and later:如果hwndParent是HWND_MESSAGE,函数仅查找所有消息窗口。

hwndChildAfter :子窗口句柄。查找从在Z序中的下一个子窗口开始。子窗口必须为hwndParent窗口的直接子窗口而非后代窗口。如果HwndChildAfter为NULL,查找从hwndParent的第一个子窗口开始。如果hwndParent 和 hwndChildAfter同时为NULL,则函数查找所有的顶层窗口及消息窗口。

lpszClass:指向一个指定了类名的空结束字符串,或一个标识类名字符串的成员的指针。如果该参数为一个成员,则它必须为前次调用theGlobaIAddAtom函数产生的全局成员。该成员为16位,必须位于lpClassName的低16位,高位必须为0。

pszWindow:指向一个指定了窗口名(窗口标题)的空结束字符串。如果该参数为 NULL,则为所有窗口全匹配。

返回值:Long,找到的窗口的句柄。如未找到相符窗口,则返回零。会设置GetLastError如果函数成功,返回值为具有指定类名和窗口名的窗口句柄。如果函数失败,返回值为NULL。

4、static IntPtr FindWindowByIndex(IntPtr hwndParent, int index)

该函数通过隐含的索引来查找相应的控件。

该函数源代码如下:

static IntPtr FindWindowByIndex(IntPtr hwndParent, int index)

{

if (index == 0)

return hwndParent;

else

{

int ct = 0;

IntPtr result = IntPtr.Zero;

do

{

result = FindWindowEx(hwndParent, result, null, null);

if (result != IntPtr.Zero)

++ct;

} while (ct < index && result != IntPtr.Zero);

return result;

}

}

二、API调用方法(注:本段文字从网上摘录)

1、使用相应的命名空间using System.Runtime.InteropServices;

2、使用DllImportAttribute特性来引入api函数,注意声明的是空方法,即方法体为空。

[DllImport("user32.dll")]

public static extern ReturnType FunctionName(type arg1,type arg2,...);

//调用时与调用其他方法并无区别

可以使用字段进一步说明特性,用逗号隔开,如: [ DllImport( "kernel32", EntryPoint="GetVersionEx" )]

DllImportAttribute特性的公共字段如下:

1、CallingConvention 指示向非托管实现传递方法参数时所用的 CallingConvention 值。CallingConvention.Cdecl : 调用方清理堆栈。它使您能够调用具有 varargs 的函数。CallingConvention.StdCall : 被调用方清理堆栈。它是从托管代码调用非托管函数的默认约定。

2、CharSet 控制调用函数的名称版本及指示如何向方法封送 String 参数。

此字段被设置为 CharSet 值之一。如果 CharSet 字段设置为 Unicode,则所有字符串参数在传递到非托管实现之前都转换成 Unicode 字符。这还导致向 DLL EntryPoint 的名称中追加字母“W”。如果此字段设置为 Ansi,则字符串将转换成 ANSI 字符串,同时向 DLL EntryPoint 的名称中追加字母“A”。大多数 Win32 API 使用这种追加“W”或“A”的约定。如果 CharSet 设置为 Auto,则这种转换就是与平台有关的(在 Windows NT 上为 Unicode,在 Windows 98 上为 Ansi)。CharSet 的默认值为 Ansi。CharSet 字段也用于确定将从指定的 DLL 导入哪个版本的函数。CharSet.Ansi 和 CharSet.Unicode 的名称匹配规则大不相同。对于 Ansi 来说,如果将 EntryPoint 设置为“MyMethod”且它存在的话,则返回“MyMethod”。如果 DLL 中没有“MyMethod”,但存在“MyMethodA”,则返回“MyMethodA”。对于 Unicode 来说则正好相反。如果将 EntryPoint 设置为“MyMethod”且它存在的话,则返回“MyMethodW”。如果 DLL 中不存在“MyMethodW”,但存在“MyMethod”,则返回“MyMethod”。如果使用的是 Auto,则匹配规则与平台有关(在 Windows NT 上为 Unicode,在 Windows 98 上为 Ansi)。如果 ExactSpelling 设置为 true,则只有当 DLL 中存在“MyMethod”时才返回“MyMethod”。

3、EntryPoint 指示要调用的 DLL 入口点的名称或序号。

如果你的方法名不想与api函数同名的话,一定要指定此参数,例如:

[DllImport("user32.dll",CharSet="CharSet.Auto",EntryPoint="MessageBox")]
 public static extern int MsgBox(IntPtr
hWnd,string txt,string caption, int type);

4、ExactSpelling 指示是否应修改非托管 DLL 中的入口点的名称,以与 CharSet 字段中指定的
CharSet 值相对应。如果为 true,则当
DllImportAttribute.CharSet 字段设置为 CharSet 的
Ansi 值时,向方法名称中追加字母 A,当 DllImportAttribute.CharSet 字段设置为
CharSet 的 Unicode 值时,向方法的名称中追加字母 W。此字段的默认值是 false。

5、PreserveSig
指示托管方法签名不应转换成返回 HRESULT、并且可能有一个对应于返回值的附加 [out, retval] 参数的非托管签名。

6、SetLastError
指示被调用方在从属性化方法返回之前将调用 Win32 API SetLastError。 true 指示调用方将调用 SetLastError,默认为 false。运行时封送拆收器将调用 GetLastError 并缓存返回的值,以防其被其他 API 调用重写。用户可通过调用 GetLastWin32Error 来检索错误代码。

三、程序实现过程

程序A:两个文本框与一个启动按钮。两个文本框用来输入用户名和密码,启动按钮用来启动程序B。

程序B:两个文本框,用来接受程序A所发送过来的字符串。

1、A程序代码清单如下:

using System.Runtime.InteropServices;

using System.Threading;

private static
System.Diagnostics.Process p;

[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]

static extern int SendMessage1(IntPtr
hwnd, uint wMsg, int
wParam, int lParam);

[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true, CallingConvention = CallingConvention.Winapi,
CharSet = CharSet.Unicode)]

public static extern IntPtr
FindWindow(string className, string windowName);

[DllImport("user32.dll", EntryPoint = "FindWindowEx", CharSet = CharSet.Auto)]

static extern IntPtr FindWindowEx(IntPtr
hwndParent, IntPtr hwndChildAfter, string lpszClass, string
lpszWindow);

static IntPtr
FindWindowByIndex(IntPtr hwndParent, int index)

{

if
(index == 0)

return
hwndParent;

else

{

int
ct = 0;

IntPtr
result = IntPtr.Zero;

do

{

result =
FindWindowEx(hwndParent, result, null, null);

if
(result != IntPtr.Zero)

++ct;

} while
(ct < index && result != IntPtr.Zero);

return
result;

}

}

2、启动按钮事件代码

private
void button4_Click(object
sender, EventArgs e)

{

if
(p == null)

{

p = new
System.Diagnostics.Process();

p.StartInfo.FileName =”B程序Path”;

p.Start();

//必须让线程挂起一定时间,否则字符串不能自动发送过去。

Thread.Sleep(500);

IntPtr
ParenthWnd = new IntPtr(0);

IntPtr
pp = new IntPtr(0);

IntPtr
mwh = IntPtr.Zero;

//通过窗口标题来获取窗口

ParenthWnd = FindWindow(null, "******");

//通过索引来获取B程序的文本编辑框,通过索引先获取该控件的ID,然后将该ID转换为16进制,与Spy++查看到ID进行对比,从而确定控件的索引。

IntPtr
butt = FindWindowByIndex(ParenthWnd, 5);

uint
WM_CHAR = 0x0102;

// SendMessage1每次发送一个字符串,所以通过循环发送完整用户名

foreach
(char c in this.textBox1.Text)

{

SendMessage1(butt, WM_CHAR, c, 0);

}

//获取密码输入框

IntPtr
butt1 = FindWindowByIndex(ParenthWnd, 3);

//发送密码

foreach
(char c in this.textBox2.Text)

{

SendMessage1(butt1, WM_CHAR, c,
0);

}

}

else

{

if
(p.HasExited) //是否正在运行{

p.Start();

}

}

3、程序运行结果

点击启动后,在第二个程序中(前面的),直接获取到第一个程序所发送的用户名和密码。

C#调用API向外部程序发送数据,布布扣,bubuko.com

时间: 2024-10-10 14:26:21

C#调用API向外部程序发送数据的相关文章

.net 调用API并解析Json数据方法

using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using Jyson.IZhanShi.Model.WebApiModel; namespace Jyson.IZhanShi.WinDesktop.Utility { pu

Linux网络之设备接口层:发送数据包流程dev_queue_xmit

转自:http://blog.csdn.net/wdscq1234/article/details/51926808 写在前面 本文主要是分析kernel-3.8的源代码,主要集中在Network的netdevice层面,来贯穿interface传输数据包的流程,kernel 博大精深,这也仅仅是一点个人愚见,作为一个笔记形式的文章,如有错误或者表述不当之处,还请大家留言批评指正,非常感谢! 主要涉及的file:kernel-3.18/net/core/dev.c kernel-3.18/net

java调用API操作HDFS

本文介绍Java调用API从hdfs读取数据 package mongodb; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URI; import java.util.Arrays; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.co

通过调用Twitter API抓取Twitter数据

国内研究weibo的人比较多,资料也相对较多,但是twitter的资料相对较少.今天简单说一下twitter api的使用.最近一小需求,采集含有指定关键词的twitter数据,瞬间想到写个爬虫来抓取,后来突然想到twitter应该有open api可用.使用了vpn翻墙之后简单的了解了twitter.com,决定直接使用 twitter api.由于twitter的open api现在也是基于oauth协议的,因此使用流程和国内一些社区比如说人人网,weibo的api的过程类似. 要想使用tw

Web Api 获取post json数据

这周在做一个webApi的模拟后台.遇到些问题.和大家分享一下,以前没有搞过webapi.所以不是太懂. 当我发post请求的json数据过来的时候,如果我用context.Request .Form去收的时候会发现,一小半的json数据跑到了Key里面,其他数据在form[0]中,很奇怪.后来恍然大悟我发送的又不是表单干嘛用表单去接收,然后去用HttpContext.Current.Request.InputStream.Read(byts, 0, byts.Length);直接读取流然后再转

C# 调用API接口处理公共类 自带JSON实体互转类

using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; using System.Web; n

使用HttpClient发送数据 到WebApi

发送和JSON数据 /=============================webAPI接受POST的JOSN数据=============================/ POST api/<controller> [HttpPost] public string Post([FromBody]string value) { var requestContent = value; var result = "[{\"name\":\"12\&qu

nodejs通过 智验科技API 向用户发送验证码

在学习上不肯钻研的人是不会提出问题的,在事业上缺乏突破力的人是不会有所创新的. 先简单说一下今天的事情,在一个订单页面的开发过程中,我突发奇想的希望能通过前端的技术向用户发送验证码. 于是我整个晚上都着力于这个需求的思路与实现方式: 1:验证码的数据肯定不能存在于html页面以及js文件中,我觉得这是一个安全性的问题,所以验证码数据应该由后台自动生成,那么在我应该使用哪种后端语言? 2:在日常生活中,发送短信肯定是有费用的,所以我们肯定是不可能通过一两段代码就免费的将验证码发送到用户手机的,那么

微信硬件蓝牙扫描绑定JSAPI发送数据

1:登录微信公众平台   查看权限接口  看设备功能是否开通 如果已开通(进行已下操作)   未开通先去开通 2:找到功能里面的  设备功能点击进去 选择添加产品  如果是蓝牙 选择蓝牙系列的 我这里是蓝牙就说蓝牙的 创建完成  回给你个产品ID  继续以下操作 3:开发者工具  在线调式  现获取token 3.1-接着选择 硬件调式api   选择设备授权获取deciceid和二维码/device/getqrcode     下面会出现让你填写刚刚获取到的token 填完token 后面千万