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. CNN基础

    CNN一般结构 卷积层作用: 1) 提取不同维度的特征,组合不同维度特征,其本质是卷积核,因此,学习一个有效的总卷积核是训练卷积层主要工作 2)寻找不同位置,不同大小的特征 3) 根据卷积核参数计算上 ...

  2. wxPython 画图板

    终于开始Python学习之旅了,姑且以一个“画图板”小项目开始吧.放慢脚步,一点一点地学习. 1月28日更新 第一次遇到的麻烦便是“重绘”,查了好多资料,终于重绘成功了. #-*- encoding: ...

  3. 洛谷 P2871 [USACO07DEC]手链Charm Bracelet 题解

    题目传送门 这道题明显就是个01背包.所以直接套模板就好啦. #include<bits/stdc++.h> #define MAXN 30000 using namespace std; ...

  4. 元组tuple常用方法

    元组tuple的功能类似与列表,元组有的功能,列表都有,列表有的,元组不一定有,下面来看看元组具有的功能:     1.count(self,value) count(self,value)统计元组中 ...

  5. 计算机网络应用层之cookie

    一.生活中的cookie 无论你知不知道Cookie是什么,在你的生活中,肯定有使用过它.还记得你使用浏览器浏览网页时,当你要登陆时,网页上有一个记住密码或自动登陆的选项,当你选择时,你就使用了Coo ...

  6. linux下安装Python3.4.1

    1.下载linux 版本的 Python 我是在Windows下下载的,然后共享到linux下. 2.解压文件 tar -xvf Python-3.4.1.tar x是解压 v是查看所有过程 f是使用 ...

  7. 关于 Unity 的一些小细节,不注意可能会被“坑”一些时间。

    关于 Unity 的一些小细节,不注意可能会被"坑"一些时间. 最近因为一些"小"问题,总是需要找很久的原因,总结一下 UnityEngine.Input 在使 ...

  8. Linux中磁盘还有空间,但创建文件时提示空间不足

    首先需要知道创建文件时,需要满足两个条件:1.磁盘上还有空间:2.inode号还有剩余. 这两个条件可以分别使用"df -h"以及"df -i"查看使用情况 [ ...

  9. Unity 游戏开发技巧集锦之制作一个望远镜与查看器摄像机

    Unity 游戏开发技巧集锦之制作一个望远镜与查看器摄像机 Unity中制作一个望远镜 本节制作的望远镜,在鼠标左键按下时,看到的视图会变大:当不再按下的时候,会慢慢缩小成原来的视图.游戏中时常出现的 ...

  10. Redis学习篇(九)之生存时间

    EXPIRE 设置生存时间,以秒为单位 #### EXPIREAT 设置生存时间,秒时间戳格式 #### PEXPIRE 设置生存时间,毫秒为单位 #### PEXPOREAT 设置生存时间,毫秒时间 ...