空当接龙(译文)
By robot-v1.0
本文链接 https://www.kyfws.com/games/freecell-discombobulator-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 7 分钟阅读 - 3019 个词 阅读量 0空当接龙(译文)
原文地址:https://www.codeproject.com/Articles/11874/FreeCell-Discombobulator
原文作者:Nicholas Butler
译文由本站 robot-v1.0 翻译
前言
Take control of FreeCell
控制空当接龙
- 下载源32.7 KB(Download source - 32.7 KB)
- 下载应用程序-26.5 KB(Download application - 26.5 KB)
注意(Note):此应用程序仅适用于Win 2K和Win XP!(: This app only works on Win 2K and Win XP!)
介绍(Introduction)
FreeCell是Windows附带的游戏之一.与其他纸牌游戏不同,这款游戏的最好之处在于它始终可以赢得胜利,在我看来,这使其成为最佳游戏.但是,如果您跳得太快,那么您很容易进入无法赢得的位置.这个应用程式可让您随意移动卡片,以克服此状态.(FreeCell is one of the games that come with Windows. The best thing about this game is that it is always winnable - unlike the other card games - which in my view makes it the best game. However, if you jump in too quickly, then you can easily get into an un-winnable position. This app allows you to move the cards around as you please to overcome this state.) 但是请注意-如果您"大量欺骗",可能会破坏游戏.(Be warned though - if you “cheat” a lot, it can spoil the game.)
背景(Background)
我碰到了一篇叫做(*I came across an article called*) FreeCell和Hearts,在幕后(FreeCell & Hearts, Behind the scenes) [([) ^)^( ]由(*] by*) 阿里克(arikp) [([) ^)^( ]和他的侦探工作使我能够写出一个散打唱片.我没有使用过他的任何代码,只有他发现的内存地址和结构.他只找到有关Win 2K和Win XP的信息,这就是为什么此应用仅在这些系统上可以使用的原因.(] and his detective work enabled me to write a discombobulator. I have not used any of his code, only the memory addresses and structure that he uncovered. He only found information for Win 2K and Win XP, which is why this app only works on these systems.)
使用应用程式(Using the App)
运行时,此应用程序将自身附加到当前正在运行的FreeCell的第一个实例.它复制了卡阵列,并允许您仅在需要的地方拖放卡.(When run, this app attaches itself to the first instance of FreeCell that is currently running. It reproduces the card array, and allows you to just drag and drop cards wherever you want them.) 它还会复制"游戏"菜单,但与FreeCell的工作方式略有不同:"(It also reproduces the Game menu, but these work a little differently to FreeCell: the “)新游戏(New Game)”,"(", “)选择游戏(Select Game)“和”(” and “)重新开始游戏(Restart Game)“选项在执行其功能之前都会为您赢得当前的游戏;并且”(” options all win your current game for you before performing their functions; and the “)统计(Statistics)“选项可让您编辑FreeCell显示的值.(” option allows you to edit the values shown by FreeCell. The “)刷新(Refresh)“选项只是从FreeCell更新状态,但是不需要激活,因为应用程序在激活后会自动刷新.(” option just updates the state from FreeCell, but this should not be needed as the app refreshes automatically when activated.) “(The “)处理(Process)菜单提供了两个选项:(” menu provides two options: “)杀(Kill)“和”(” and “)重新开始(Restart)".这两个都以极端的偏见杀死了当前的FreeCell进程,但是”(”. Both of these kill the current FreeCell process with extreme prejudice, but the “)重新开始(Restart)“选项将启动一个新的FreeCell实例.如果您对游戏感到厌倦,则这些选项很有用.(” option starts a new instance of FreeCell. These options are useful if you just get fed up with the game.) 还有一个”(There is also an “)复活节彩蛋(Easter Egg)功能:按F12键,disombobulator将按等级和花色排列剩余的卡;只需双击即可赢得比赛!(” function: press F12 and the discombobulator will line up the remaining cards by rank and suit; just double-click and you have won your game!)
代码(The Code)
Arik的文章揭示了FreeCell内部使用的内存地址和结构.此应用程序使用此信息来读取和写入FreeCell进程的虚拟内存.它通过使用(Arik’s article uncovered the memory addresses and structure used internally by FreeCell. This app uses this information to both read and write to the virtual memory of the FreeCell process. It does this by using the) OpenProcess
,(,) ReadProcessMemory
和(and) WriteProcessMemory
在功能(functions in)内核32(Kernel32).(.)
该应用程序还通过模拟键盘和鼠标事件来控制空当接龙过程.我用了(This app also controls the FreeCell process by simulating keyboard and mouse events. I used the) SendKeys
.NET类来模拟键盘事件,并且(.NET class to simulate keyboard events, and the) SendInput
Win32函数来自(Win32 function from)*用户32(User32)*模拟鼠标事件.(to simulate mouse events.)
唯一要提及的是我曾经(The only other thing to mention is that I used)*Cards.dll(Cards.dll)*抽卡,使生活变得轻松.(to draw the cards, which made life easy.)
兴趣点(Points of Interest)
进程内存访问(Process Memory Access)
我使用以下声明访问函数(I used the following declarations to access the functions in) Kernel32
:(:)
[ DllImport( "Kernel32.dll", SetLastError = true ) ]
public static extern IntPtr OpenProcess
(
UInt32 dwDesiredAccess,
bool bInheritHandle,
UInt32 dwProcessId
);
[ DllImport( "Kernel32.dll" ) ]
public static extern bool ReadProcessMemory
(
IntPtr hProcess,
IntPtr lpBaseAddress,
byte[] lpBuffer,
UInt32 nSize,
ref UInt32 lpNumberOfBytesRead
);
[ DllImport( "Kernel32.dll" ) ]
public static extern Int32 WriteProcessMemory
(
IntPtr hProcess,
IntPtr lpBaseAddress,
byte[] buffer,
UInt32 size,
ref UInt32 lpNumberOfBytesWritten
);
发送键(SendKeys)
这个.NET类将密钥发送到前台进程,所以我使用了(This .NET class sends keys to the foreground process, so I used) SetForegroundWindow
在(in)*用户32(User32)*在发送所需的密钥之前将FreeCell置于最前面:(to bring FreeCell to the front before sending the required keys:)
[ DllImport( "User32.dll" ) ]
public static extern bool SetForegroundWindow
(
IntPtr hWnd
);
发送输入(SendInput)
使用起来有点棘手.我使用了以下PInvoke声明:(This was a bit tricky to use. I used the following PInvoke declarations:)
internal class INPUT
{
public const int MOUSE = 0;
public const int KEYBOARD = 1;
public const int HARDWARE = 2;
}
internal class MOUSEEVENTF
{
public const int MOVE = 0x0001 ; /* mouse move */
public const int LEFTDOWN = 0x0002 ; /* left button down */
public const int LEFTUP = 0x0004 ; /* left button up */
public const int RIGHTDOWN = 0x0008 ; /* right button down */
public const int RIGHTUP = 0x0010 ; /* right button up */
public const int MIDDLEDOWN = 0x0020 ; /* middle button down */
public const int MIDDLEUP = 0x0040 ; /* middle button up */
public const int XDOWN = 0x0080 ; /* x button down */
public const int XUP = 0x0100 ; /* x button down */
public const int WHEEL = 0x0800 ; /* wheel button rolled */
public const int VIRTUALDESK = 0x4000 ; /* map to entire
virtual desktop */
public const int ABSOLUTE = 0x8000 ; /* absolute move */
}
[ StructLayout( LayoutKind.Sequential ) ]
internal struct MOUSEINPUT
{
public int dx;
public int dy;
public int mouseData;
public int dwFlags;
public int time;
public IntPtr dwExtraInfo;
}
[ StructLayout( LayoutKind.Explicit ) ]
internal struct Input
{
[ FieldOffset( 0 ) ] public int type;
[ FieldOffset( 4 ) ] public MOUSEINPUT mi;
[ FieldOffset( 4 ) ] public KEYBDINPUT ki;
[ FieldOffset( 4 ) ] public HARDWAREINPUT hi;
}
[ DllImport( "User32.dll" ) ]
public static extern UInt32 SendInput
(
UInt32 nInputs,
Input[] pInputs,
Int32 cbSize
);
的(The) MOUSEINPUT
struct
使用真正奇怪的坐标系统:屏幕在两个轴上都映射为从0到65535的比例.因此,我使用以下代码将像素转换为所需的值:(uses a truly bizarre co-ordinate system: the screen is mapped to a scale from 0 to 65535 in both axis. So I used the following code to convert from pixels to the required values:)
Rectangle screen = Screen.PrimaryScreen.Bounds;
int x2 = ( 65535 * x ) / screen.Width;
int y2 = ( 65535 * y ) / screen.Height;
然后,我使用这些值来创建一个数组(I then used these values to create an array of) Input
struct
s来模拟双击:(s to simulate a double-click:)
Input[] input = new Input[ 4 ];
for ( int i = 0 ; i < input.Length ; i++ )
{
input[ i ].type = INPUT.MOUSE;
input[ i ].mi.dx = x2;
input[ i ].mi.dy = y2;
input[ i ].mi.dwFlags =
MOUSEEVENTF.MOVE | MOUSEEVENTF.ABSOLUTE;
if ( ( i % 2 ) == 0 )
input[ i ].mi.dwFlags |= MOUSEEVENTF.LEFTDOWN;
else
input[ i ].mi.dwFlags |= MOUSEEVENTF.LEFTUP;
}
然后,很容易将(Then it is simple to call the) SendInput
功能:(function:)
User32.SendInput( ( UInt32 ) input.Length, input,
Marshal.SizeOf( input[0]));
双缓冲(Double-buffering)
您需要设置三种样式来双缓冲(You need to set three styles to double-buffer a) Control
(一种((a) Form
来自(is derived from) Control
):():)
public Form1()
{
SetStyle( ControlStyles.UserPaint , true );
SetStyle( ControlStyles.AllPaintingInWmPaint , true );
SetStyle( ControlStyles.DoubleBuffer , true );
InitializeComponent();
}
结论(Conclusion)
这个小应用程序写起来很有趣.事实证明,与控制另一个过程并以原始作者所不希望的方式进行扩展相比,它要容易得多.(This little app has been quite fun to write. It proved to be easier than I thought to take control of another process, and to extend it in ways the original authors didn’t expect.)
历史(History)
- 5(5)日(th)2005年10月:第1版(October, 2005: Version 1)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C# Win2K WinXP Windows .NET .NET1.1 Visual-Studio VS.NET2003 Dev 新闻 翻译