I'm trying to automate a process in an as400 emulator (IBM's Personal Communications iSeries) and in order to do so, I'm using some code (that I totally did not steal from CodeProject) which uses EHLLAPI to connect to the emulator.
I created a Winforms to implement the code with some buttons and some textboxes to test it. Here's the class to expose the methods from EHLLAPI:
public class EhllapiFunc //Class used to import the DLL.
{
[DllImport("PCSHLL32.dll")]
public static extern UInt32 hllapi(out UInt32 Func, StringBuilder Data, out UInt32 Length, out UInt32 RetC);
}
class EhllapiWrapper
{
/*These are the constants used to as function codes that are sent as parameters to the function in the DLL.
There are a lot more constants but these are the only ones used for the moment*/
const UInt32 HA_CONNECT_PS = 1; /* 000 Connect PS*/
const UInt32 HA_DISCONNECT_PS = 2; /* 000 Disconnect PS*/
const UInt32 HA_COPY_PS_TO_STR = 8; /* 000 Copy PS to String*/
/*EHLLAPI return codes. There are a lot more. I'll leave just some of them as I'm not getting any return codes in my issue*/
const UInt32
HARC_SUCCESS = 0; /* 000 Good return code.*/
const UInt32
HARC99_INVALID_INP = 0; /* 000 Incorrect input*/
const UInt32
HARC_INVALID_PS = 1; /* 000 Invalid PS, Not*/
const UInt32
HARC_BAD_PARM = 2; /* 000 Bad parameter, or*/
//Method to connect to the emulator.
public static UInt32 Connect(string sessionID)
{
StringBuilder Data = new StringBuilder(4);
Data.Append(sessionID);
UInt32 rc = 0;
UInt32 f = HA_CONNECT_PS;
UInt32 l = 4;
return EhllapiFunc.hllapi(out f, Data, out l, out rc);
}
//Method to disconnect.
public static UInt32 Disconnect(string sessionID)
{
StringBuilder Data = new StringBuilder(4);
Data.Append(sessionID);
UInt32 rc = 0;
UInt32 f = HA_DISCONNECT_PS;
UInt32 l = 4;
return EhllapiFunc.hllapi(out f, Data, out l, out rc);
}
/*This is the method where I'm having the problem.*/
public static UInt32 ReadScreen(int position, int len, out string txt)
{
StringBuilder Data = new StringBuilder(3000);
UInt32 rc = (UInt32)position;
UInt32 f = HA_COPY_PS_TO_STR;
UInt32 l = (UInt32)len;
UInt32 r = EhllapiFunc.hllapi(out f, Data, out l, out rc);
txt = Data.ToString();
return r;
}
}
This is the OnClick event I use to call the method on my form:
private void bReadString_Click(object sender, EventArgs e)
{
//I tried cleaning the TextBox where the outputed string is sent before calling the method.
tbReadOut.Text = "";
string s;
//I send the position and string length entered on the form to the method and then "s" is modified inside the method "ReadScreen".
EhllapiWrapper.ReadScreen(Convert.ToInt32(tbReadPos.Text), Convert.ToInt32(tbReadLen.Text), out s);
tbReadOut.Text = s;
}
Here's an example of the issue. I should be getting a string of the size of 2 characters from the cursor position "30":
The garbage I get is on "String Result"
This is the region from the emulator I was supposed to get, just "Sy":
This sometimes works and sometimes it doesn't. I tried on different parts of the emulator and the issue is the same. Getting the cursor position and sending a string work fine, it is just when I'm trying to get a string from the emulator.
I don't even know where to look for answers as this code was originally posted in 2005.
I found the problem. Inside the method ReadScreen
I make a StringBuilder
that expects 3000 characters. As I'm using out
in the parameter of the method, I'm dealing with one memory address instead of just two variables. As I allocated space for at least 3000 characters, when the string I read is small, sometimes it comes with garbage from that "unused" space. What I did was change the expected size of the StringBuilder
to the length of the string I'm trying to read minus 1 (if it was just the length I will still get one garbage character). Here's the code:
public static UInt32 ReadScreen(int position, int len, out string txt)
{
StringBuilder Data = new StringBuilder(len - 1); //This line was the problem.
UInt32 rc = (UInt32)position;
UInt32 f = HA_COPY_PS_TO_STR;
UInt32 l = (UInt32)len;
UInt32 r = EhllapiFunc.hllapi(out f, Data, out l, out rc);
txt = Data.ToString();
return r;
}
My explanation might be wrong, as I'm still learning C# and the code was originally from 2005, but this modification worked for me. Feel free to correct me if I misunderstood something or if I got something wrong.