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向外部程序发送数据的更多相关文章

  1. PL/SQL 调用JAVA使用UDP发送数据

    步骤如下 1.直接在SQL命令中写入JAVA代码(用SYS帐号执行,不然权限等太麻烦) create or replace and resolve java source named udp as i ...

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

    using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using Syst ...

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

    转自:http://blog.csdn.net/wdscq1234/article/details/51926808 写在前面 本文主要是分析kernel-3.8的源代码,主要集中在Network的n ...

  4. 一文搞懂 Netty 发送数据全流程 | 你想知道的细节全在这里

    欢迎关注公众号:bin的技术小屋,如果大家在看文章的时候发现图片加载不了,可以到公众号查看原文 本系列Netty源码解析文章基于 4.1.56.Final版本 在<Netty如何高效接收网络数据 ...

  5. [置顶] Xamarin android 调用Web Api(ListView使用远程数据)

    xamarin android如何调用sqlserver 数据库呢(或者其他的),很多新手都会有这个疑问.xamarin android调用远程数据主要有两种方式: 在Android中保存数据或调用数 ...

  6. win32.gui.api.con(前置,鼠标点击,发送数据的Dome)

    # -*- coding: UTF-8 -*- import win32gui, win32con import os import time import win32gui import win32 ...

  7. 使用HttpClient发送数据 到WebApi

    发送和JSON数据 /=============================webAPI接受POST的JOSN数据=============================/ POST api/& ...

  8. Android使用HttpUrlConnection请求服务器发送数据详解

    HttpUrlConnection是java内置的api,在java.net包下,那么,它请求网络同样也有get请求和post请求两种方式.最常用的Http请求无非是get和post,get请求可以获 ...

  9. Java使用UDP发送数据到InfluxDB

    最近在做压测引擎相关的开发,需要将聚合数据发送到InfluxDB保存以便实时分析和控制QPS. 下面介绍对InfluxDB的使用. 什么是InfluxDB InfluxDB是一款用Go语言编写的开源分 ...

随机推荐

  1. c#元组举例

    元组的概要: 数组合并了相同类型的对象,而元组合并了不同类型的对象.元组起源于函数编程语言(如F#) ,在 这些语言中频繁使用元组.在N盯4中,元组可通过.NET Fmmework用于所有的NET语言 ...

  2. jquery重置

    在使用jquery时要先引用 <script type="text/javascript" src="/Themes/Default/Js/jquery-1.11. ...

  3. 设置或者获取CheckboxList控件的选中值

    1.设置CheckBoxList选中的值 /// <summary> /// 设置CheckBoxList中哪些是选中了的 /// </summary> /// <par ...

  4. gym101431B

    以此纪念我都快忘了的后缀自动机(捂脸) 不过这题有两种做法: 用后缀自动机,先把原串再接遍中间加入特殊连接字符再把原串反接两遍,对这个新构造出的串做后缀自动机.(或者直接建立广义后缀自动机) 下面只要 ...

  5. poj1040 Transportation(DFS)

    题目链接 http://poj.org/problem?id=1040 题意 城市A,B之间有m+1个火车站,第一站A站的编号为0,最后一站B站的编号为m,火车最多可以乘坐n人.火车票的票价为票上终点 ...

  6. Java利用Redis实现消息队列

    应用场景 为什么要用redis?二进制存储.java序列化传输.IO连接数高.连接频繁 一.序列化 这里编写了一个java序列化的工具,主要是将对象转化为byte数组,和根据byte数组反序列化成ja ...

  7. anaconda-anaconda无法安装pydot?

    只需先安装graphviz,再安装pydot就可以了 sudo conda install graphviz sudo conda install pydot

  8. list容器详解

    首先说说STL ( STL的目的是标准化组件,这样就不用重新开发,可以使用现成的组件.STL现在是C++的一部分,因此不用额外安装什么.它被内建在你的编译器之内.因为STL的list是一个简单的容器, ...

  9. Java多线程编程——volatile关键字

    (本篇主要内容摘自<Java多线程编程核心技术>) volatile关键字的主要作用是保证线程之间变量的可见性. package com.func; public class RunThr ...

  10. Bzoj[Usaco2018 Feb]5194 Snow Boots(线段树)

    Description 到冬天了,这意味着下雪了!从农舍到牛棚的路上有N块地砖,方便起见编号为1…N,第i块地砖上积了fi英尺的雪 .在Farmer John的农舍的地窖中,总共有B双靴子,编号为1… ...