在使用别人的程序时,我们常发现一些好的功能,想通过自己的程序来重复调用。可偏偏这个程序没有留可编程接口,无法通过API、DLL、COM等方式实现调用。早些年与同仁们讨论时,常对此深表遗憾。最近,通过研究Windows API的使用方法,终于从理论上解决了这一问题,即可通WinAPI中SendMessage、EnumChildWindows等,从模拟操作的角度来调用指定程序的指定功能。
我们知道,Windows是消息驱动的,即Windows窗口、控件的任何操作,都是通过消息事件来完成的。从理论上讲,在我们自己的程序中,只要能准确地找到相应功能所在的窗口或控件的句柄Handle,发出相应的消息,即可完成相应任务。从这个层面上,这种技术可以应用在所有windows程序上。只是这种技术的处理,需要非常细心。因为在实际应用中,从某一个程序中找到相应的控件(包括Parent/Child/Next等)还是比较麻琐的,一不小心,调用的功能就实法实现,还有可能导致程序甚至系统的崩溃。所以,这种技术的关键在于两个地方:一是找准句柄,二是找对消息。
推荐分析一个窗体(控件)的句柄或消息的工具:SPY++,这在Visual Studio Tools中有,操作起来很简单。
C#中实现外部程序调用,可以通过封装User32.dll中sendmessage等函数来实现。我已将常用的功能封装成一个类:
using
System;
using
System.Collections.Generic;
using
System.Text;
using
System.Runtime.InteropServices;
namespace
MuliCall
{
class
WinApi
{
#region
宏定义
public
const
int
WM_KEYDOWN
=
0x100
;
public
const
int
WM_KEYUP
=
0x101
;
public
const
int
VK_CONTROL
=
0x11
;
public
const
int
VK_F5
=
0x74
;
public
const
int
KEYEVENTF_KEYUP
=
0x2
;
public
const
int
VK_MENU
=
0x12
;
public
const
int
WM_SETTEXT
=
0xC
;
public
const
int
WM_CLEAR
=
0x303
;
public
const
int
BN_CLICKED
=
0
;
public
const
int
WM_LBUTTONDOWN
=
0x201
;
public
const
int
WM_LBUTTONUP
=
0x202
;
public
const
int
WM_CLOSE
=
0x10
;
public
const
int
WM_COMMAND
=
0x111
;
public
const
int
WM_SYSKEYDOWN
=
0x104
;
#endregion
public
delegate
bool
EnumChildWindowsProc(IntPtr hwnd,
int
lParam);
#region
WinAPI定义
[DllImport(
"
User32.dll
"
, EntryPoint
=
"
SendMessage
"
)]
public
static
extern
int
SendMessage(
int
hWnd,
//
handle to destination window
int
Msg,
//
message
int
wParam,
//
first message parameter
int
lParam
//
second message parameter
);
[DllImport(
"
User32.dll
"
, EntryPoint
=
"
SendMessage
"
)]
public
static
extern
int
SendTxtMessage(
int
hWnd,
//
handle to destination window
int
Msg,
//
message
int
wParam,
//
first message parameter
char
[] lParam
//
int lParam
//
second message parameter
);
[DllImport(
"
user32.dll
"
, EntryPoint
=
"
PostMessage
"
)]
public
static
extern
int
PostMessage(
int
hwnd,
int
wMsg,
int
wParam,
int
lParam
);
[DllImport(
"
user32.dll
"
, EntryPoint
=
"
FindWindow
"
)]
public
static
extern
int
FindWindow(
string
lpClassName,
string
lpWindowName
);
[DllImport(
"
user32.dll
"
, EntryPoint
=
"
FindWindowEx
"
)]
public
static
extern
int
FindWindowEx(
int
hwndParent,
int
hwndChildAfter,
string
lpszClass,
string
lpszWindow
);
[DllImport(
"
user32.dll
"
, EntryPoint
=
"
EnumChildWindows
"
)]
public
static
extern
int
EnumChildWindows(
int
hWndParent,
int
lpEnumFunc,
int
lParam
);
[DllImport(
"
user32.dll
"
, EntryPoint
=
"
EnumChildWindows
"
)]
public
static
extern
int
EnumChildWindows(
int
hWndParent,
EnumChildWindowsProc lpEnumFunc,
int
lParam
);
[DllImport(
"
user32.dll
"
, EntryPoint
=
"
SetFocus
"
)]
public
static
extern
int
SetFocus(
int
hWnd
);
[DllImport(
"
user32.dll
"
, EntryPoint
=
"
SetWindowText
"
)]
public
static
extern
int
SetWindowText(
int
hwnd,
string
lpString
);
[DllImport(
"
user32.dll
"
, EntryPoint
=
"
keybd_event
"
)]
public
static
extern
void
keybd_event(
byte
bVk,
byte
bScan,
int
dwFlags,
int
dwExtraInfo
);
[DllImport(
"
user32.dll
"
, EntryPoint
=
"
SetForegroundWindow
"
)]
public
static
extern
int
SetForegroundWindow(
int
hwnd
);
[DllImport(
"
user32.dll
"
, EntryPoint
=
"
GetClassName
"
)]
public
static
extern
int
GetClassName(
IntPtr hwnd,
StringBuilder lpClassName,
int
nMaxCount
);
[DllImport(
"
user32.dll
"
, EntryPoint
=
"
GetWindowText
"
)]
public
static
extern
int
GetWindowText(
IntPtr hwnd,
StringBuilder lpString,
int
cch
);
#endregion
//
综合处理全局HWD
private
static
int
CurrnetFormHandle
=
0
;
//
综合处理函数
public
static
void
SetCurrnetFormHandle(
string
strWindow)
{
SetCurrnetFormHandle(
null
, strWindow,
false
);
}
public
static
void
SetCurrnetFormHandle(
string
strClass,
string
strWindow,
bool
beForeground)
{
CurrnetFormHandle
=
FindWindow(strClass, strWindow);
if
(beForeground)
SetForegroundWindow(CurrnetFormHandle);
}
public
static
void
SetCurrnetFormHandle(
int
hwd,
bool
beForeground)
{
CurrnetFormHandle
=
hwd;
if
(beForeground)
SetForegroundWindow(CurrnetFormHandle);
}
public
static
void
SetCurrnetFormHandle(
int
hwd)
{
CurrnetFormHandle
=
hwd;
}
public
static
int
GetCurrentFormHandle()
{
return
CurrnetFormHandle;
}
//
模拟单击按钮
public
static
void
ClickButton(
string
strWindow)
{
ClickButton(
"
Button
"
, strWindow);
}
public
static
void
ClickButton(
string
strClass,
string
strWindow)
{
ClickButton(CurrnetFormHandle,
0
, strClass, strWindow);
}
public
static
void
ClickButton(
int
hwdParent,
int
hwndChildAfter,
string
strClass,
string
strWindow)
{
int
hwdButton
=
FindWindowEx(hwdParent, hwndChildAfter, strClass, strWindow);
SendMessage(hwdButton, WM_LBUTTONDOWN,
0
,
0
);
SendMessage(hwdButton, WM_LBUTTONUP,
0
,
0
);
}
//
修改文本框内容
public
static
void
SetWindowsText(
string
Parnet_strClass,
string
Parnet_strText,
string
strClass,
string
strText)
{
int
hwdParent
=
FindWindowEx(CurrnetFormHandle,
0
, Parnet_strClass, Parnet_strText);
//
停止 ThunderRT6OptionButton
int
bntHwd
=
FindWindowEx(hwdParent,
0
,
"
ThunderRT6OptionButton
"
,
"
停止
"
);
int
hwdText
=
FindWindowEx(hwdParent, bntHwd, strClass,
null
);
//
ThunderRT6TextBox
SendTxtMessage(hwdText, WM_SETTEXT,
0
, strText.ToCharArray());
}
public
static
void
SetWindowsText(
string
strClass,
string
strText)
{
SetWindowsText( strClass,
null
, strText);
}
public
static
void
SetWindowsText(
string
strClass,
string
strWindow,
string
strText)
{
SetWindowsText(CurrnetFormHandle,
0
, strClass, strWindow, strText);
}
public
static
void
SetWindowsText(
int
hwdParent,
int
hwndChildAfter,
string
strClass,
string
strWindow,
string
strText)
{
int
hwdText
=
FindWindowEx(hwdParent, hwndChildAfter, strClass, strText);
SendTxtMessage(hwdText, WM_SETTEXT,
0
, strText.ToCharArray());
}
//
搜索子窗体
public
static
void
SearchChild(
string
strWindow)
{
EnumChildWindowsProc myEnumChild
=
new
EnumChildWindowsProc(EumWinChiPro);
try
{
EnumChildWindows(CurrnetFormHandle, myEnumChild,
0
);
}
catch
(Exception ex)
{
;
//
MessageBox.Show(ex.Message + "\r\n" + ex.Source + "\r\n\r\n" + ex.StackTrace.ToString());
}
}
public
static
bool
EumWinChiPro(IntPtr hWnd,
int
lParam)
{
StringBuilder s
=
new
StringBuilder(
1256
);
GetClassName(hWnd, s,
1257
);
string
ss
=
s.ToString();
if
(ss
==
null
)
ss
=
""
;
ss
=
ss.Trim();
//
MessageBox.Show(ss);
//
ThunderRT6TextBox
StringBuilder s2
=
new
StringBuilder(
1256
);
GetWindowText(hWnd, s2,
1257
);
ss
=
s2.ToString();
return
true
;
}
}
}
使用这个类,在自己的程序中,比较方便地实现外部程序的调用。
int
hwnd
=
(
int
)MainHandlArray[i];
WinApi.SetCurrnetFormHandle(hwnd);
WinApi.SetWindowsText(textBox_Class_EditPanel.Text, textBox_Title_EditPanel.ToString(), textBox_Class_Edit.Text, textBoxWorkID.Text);
使用这种方法,我写个程序多开器,主要实现启动多个实例,能给EDIT框付值,能定时调用其中按钮。
多开程序下载