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 Box1.Text)
{
SendMessage1(butt, WM_CHAR, c, 0);
}
//获取密码输⼊框
IntPtr butt1 = FindWindowByIndex(ParenthWnd, 3);
//发送密码
foreach (char c Box2.Text)
{
SendMessage1(butt1, WM_CHAR, c, 0);
}
}
else
{
if (p.HasExited) //是否正在运⾏{
p.Start();
}
}
3、程序运⾏结果
点击启动后,在第⼆个程序中(前⾯的),直接获取到第⼀个程序所发送的⽤户名和密码。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。