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向外部程序发送数据的更多相关文章
- PL/SQL 调用JAVA使用UDP发送数据
步骤如下 1.直接在SQL命令中写入JAVA代码(用SYS帐号执行,不然权限等太麻烦) create or replace and resolve java source named udp as i ...
- .net 调用API并解析Json数据方法
using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using Syst ...
- Linux网络之设备接口层:发送数据包流程dev_queue_xmit
转自:http://blog.csdn.net/wdscq1234/article/details/51926808 写在前面 本文主要是分析kernel-3.8的源代码,主要集中在Network的n ...
- 一文搞懂 Netty 发送数据全流程 | 你想知道的细节全在这里
欢迎关注公众号:bin的技术小屋,如果大家在看文章的时候发现图片加载不了,可以到公众号查看原文 本系列Netty源码解析文章基于 4.1.56.Final版本 在<Netty如何高效接收网络数据 ...
- [置顶]
Xamarin android 调用Web Api(ListView使用远程数据)
xamarin android如何调用sqlserver 数据库呢(或者其他的),很多新手都会有这个疑问.xamarin android调用远程数据主要有两种方式: 在Android中保存数据或调用数 ...
- win32.gui.api.con(前置,鼠标点击,发送数据的Dome)
# -*- coding: UTF-8 -*- import win32gui, win32con import os import time import win32gui import win32 ...
- 使用HttpClient发送数据 到WebApi
发送和JSON数据 /=============================webAPI接受POST的JOSN数据=============================/ POST api/& ...
- Android使用HttpUrlConnection请求服务器发送数据详解
HttpUrlConnection是java内置的api,在java.net包下,那么,它请求网络同样也有get请求和post请求两种方式.最常用的Http请求无非是get和post,get请求可以获 ...
- Java使用UDP发送数据到InfluxDB
最近在做压测引擎相关的开发,需要将聚合数据发送到InfluxDB保存以便实时分析和控制QPS. 下面介绍对InfluxDB的使用. 什么是InfluxDB InfluxDB是一款用Go语言编写的开源分 ...
随机推荐
- 完美解决doc、docx格式word转换为Html
http://blog.csdn.net/renzhehongyi/article/details/48767597
- 在Windows中安装Boot2Docker 遇到 Unable to load R3 module 的解决方案
引言 这个几乎是所有64位win7用户在virtual box上安装64位的linux都会遇到的问题(如果你用的是买机器的时候自带的win7 64位而且你没有重装过系统的除外). 解决办法 可参考以下 ...
- WERTYU(UVa10082)
C++ 11 代码如下: #include<iostream> using namespace std; const char s[] = { "`1234567890-=QWE ...
- 在ubuntu下安装kaldi基本步骤
注:最近在学习kaldi语音识别工具,在安装过程中遇到了许多问题,在此记录,以备后需. 在一开始,我看了这篇博客(http://blog.topspeedsnail.com/archives/1001 ...
- 【51nod】1340 地铁环线
今天头非常疼,躲在家里没去机房 反正都要颓废了,然后花了一上午研究了一下这道神题怎么做-- 题解 首先我们发现,如果我们设\(dis[i]\)为从\(0\)节点走到\(i\)节点的距离 那么题目中给出 ...
- XV6操作系统代码阅读心得(三):锁
锁是操作系统中实现进程同步的重要机制. 基本概念 临界区(Critical Section)是指对共享数据进行访问与操作的代码区域.所谓共享数据,就是可能有多个代码执行流并发地执行,并在执行中可能会同 ...
- zookeeper分布式算法和部署
算法摘要 安装 配置 监控 创建节点 二阶段提交(Two-Phase Commit) 投票和执行 协调者向参与者发送事务内容,询问是否可以提交,各参与者节点执行事务并向协调者反馈 如果所有参与者反馈y ...
- Calendar日期方法
面试居然让我获取当前月份第一天跟最后一天,主要是尴尬的回答不上来. 废话不说,直接贴代码,工作应该是够用了 public class TestCalendar { // 日期也就是这了 public ...
- 【BZOJ 4503】4503: 两个串 (FFT)
4503: 两个串 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 497 Solved: 226 Description 兔子们在玩两个串的游戏.给 ...
- AtCoder Regular Contest 80
链接 C. 4-adjacent 给定序列$a_i$,询问是否存在一个排列,满足$a_{p[i]}* a_{p[i + 1]}$是4的倍数 贪心构造 首先把只是2的倍数的数拿出来,放在最右边 前面把是 ...