WinAPI SendMessage from .NET - pinvoke

I have an example of winapi code:
struct CommunicationInfo {
long internalMsg;
const TCHAR * srcModuleName;
void * info;
};
...
const TCHAR* szText = _T("Hello from my plugin!\n(test message)");
CommunicationInfo ci = { 0x0401, cszMyPlugin, (void *) szText };
::SendMessage( hNppWnd, 0x111, (WPARAM) _T("NppExec.dll"), (LPARAM) &ci );
I want make the same call from .net and i wrote such wrapper:
[StructLayout(LayoutKind.Sequential)]
public struct CommunicationInfo
{
public Int64 internalMsg;
[MarshalAs(UnmanagedType.LPWStr)]
public StringBuilder srcModuleName;
[MarshalAs(UnmanagedType.LPWStr)]
public StringBuilder data;
};
...
[DllImport("user32")]
public static extern IntPtr SendMessage(IntPtr hWnd,
NppMsg Msg, IntPtr wParam,
[MarshalAs(UnmanagedType.Struct)] CommunicationInfo communicationInfo);
...
SendMessage(hNppWnd, 0x111,
Marshal.StringToHGlobalUni("NppExec.dll"),
new CommunicationInfo
{
data = new StringBuilder("test test"),
internalMsg = 0x0401,
srcModuleName = new StringBuilder("ModuleName")
});
But this code doesn't work. Where did I make a mistake ?

"long" field in CommunicationInfo struct is 32-bit in WinAPI, I believe. So try defining "internalMsg" as System.Int32 in C#
To be sure, try calling printf("%d\n", sizeof(CommunicationInfo)) in C/C++ to know the actual size. If it is (4 + 4 + 4) on a 32-bit system, then the C# struct must also be of 12 byte size.
The "char*" pointer must also be the pointer to unmanaged memory, so the StringBuilder just won't do.
See this PInvoke error when marshalling struct with a string in it for the marshalling sample

As Viktor points out, C/C++ long is 32 bits in size so needs to be matched with C# int. On top of that, the passing of the struct is not handled correctly. In addition the call to StringToHGlobalUni leaks since you never call FreeHGlobal.
I'd probably handle the marshalling something like this:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct CommunicationInfo
{
public int internalMsg;
public string srcModuleName;
public string data;
};
....
[DllImport("user32")]
public static extern IntPtr SendMessage(
IntPtr hWnd,
uint Msg,
[MarshalAs(UnmanagedType.LPWStr)] string wParam,
ref CommunicationInfo communicationInfo
);
....
CommunicationInfo communicationInfo = new CommunicationInfo
{
internalMsg = 0x0401,
srcModuleName = "ModuleName",
data = "test test"
};
SendMessage(hNppWnd, 0x111, "NppExec.dll", ref communicationInfo);

Related

Using UnManaged C++ Dll in C#

Hi this is my Function in C++ dll.
I am Trying to use it in My C# Application. Have tried so many things but nothing seems to work for me.
xyz_API LONG __stdcall xyz_Login(char *dwIP,unsigned short dwPort,char *dwUseName,char
*dwPassword,MyDetail dwInfo,char *dwInfo);
* Parameter:
[in]
dwIP
dwpPort
dwUseName
dwPassword
[out]
MyDetail (struct)
This is the Struct :
typedef struct
{
int xyz_id;
int xyz_ch;
int xyz_total;
int my_id;
char my_Info[10];
char m_status;
}MyDetail ,*MyDetail ;
I made a Class for this Struct in my code as:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MyDetail
{
public int xyz_id;
public int xyz_ch;
public int xyz_total;
public int my_id;
[MarshalAs(UnmanagedType.ByValArray,
ArraySubType = UnmanagedType.LPStr, SizeConst = 10)]
public char[] my_Info;
public sbyte m_status;
}
I am using following line of code to do use it in C# application:
[DllImport("MYC.dll", EntryPoint = "xyz_Login", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern int xyz_Login([MarshalAs(UnmanagedType.LPWStr)]string dwIP, ushort dwPort, [MarshalAs(UnmanagedType.LPWStr)] string dwUseName, [MarshalAs(UnmanagedType.LPWStr)] string dwPassword, MyDetail dwmyInfo, [MarshalAs(UnmanagedType.LPWStr)]string dwInfo);
My Form Calling:
MyDetail obj= new MyDetail();`enter code here`
int result = xyz_Login("192.168.1.10", 9001, "admin", "admin", obj, null);
code works perfectly but there is not output. MyDetail object always return null.
Is There a problem in marshalling. thanks in advance
First, the C++ function:
xyz_API LONG __stdcall xyz_Login(char *dwIP,unsigned short dwPort,char *dwUseName,char
*dwPassword,MyDetail dwInfo,char *dwInfo);
The paramter "dwInfo" is a struct type, not a pointer or a reference type, so the parameter is passed by value, so in the function xyz_Login, you are modifying a local copy of the struct, no changes to the struct in C#. So you should change it: MyDetail* dwInfo
Meanwhile, you should change the dllimport in C# to:
[DllImport("MYC.dll", EntryPoint = "xyz_Login", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern int xyz_Login(string dwIP, ushort dwPort, string dwUseName, string dwPassword, ref MyDetail dwmyInfo, string dwInfo);
In a PInvoke call, a ref struct is translated into a pointer to the struct; also, char* is ansi, not unicode.
I got this running using following:
Struct As:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MyDetail
{
public int xyz_id;
public int xyz_ch;
public int xyz_total;
public int my_id;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string my_Info;
public sbyte m_status;
}
DLLImport Statement AS:
[DllImport("MYC.dll", EntryPoint = "NET_DVR_Login")]
public static extern Int32 xyz_Login(string dwIP, UInt16 dwPort, string Name,string dwPassword, IntPtr myDetail, char[] info);

How to marshal this nested, and Pointer Used C structure in C#

typedef struct pt_input_bir
{
PT_BYTE byForm;
union {
PT_BIR *pBIR; ///< Used when byForm = PT_FULLBIR_INPUT */
PT_LONG lSlotNr; ///< Used when byForm = PT_SLOT_INPUT */
PT_BYTE abyReserved[20]; /** For future use */
} InputBIR;
} PT_INPUT_BIR
typedef struct pt_bir {
PT_BIR_HEADER Header;
PT_BYTE Data[1];
} PT_BIR
typedef struct pt_bir_header {
PT_DWORD Length;
PT_BYTE HeaderVersion;
PT_BYTE Type;
PT_WORD FormatOwner;
PT_WORD FormatID;
PT_CHAR Quality;
PT_BYTE Purpose;
PT_DWORD FactorsMask;
} PT_BIR_HEADER
and the C function is
PT_STATUS StoreFinger (
IN PT_CONNECTION hConnection,
IN PT_INPUT_BIR *pTemplate,
OUT PT_LONG *plSlotNr
)
Now I need to do the wrapper for the above C function in C#.
How should I marshal the PT_INPUT_BIR* structure and how should I unmarshal it after return of this function?
Please help me to solve this.
/********************** FOR MORE DETAIL ABOUT THIS QUESTION**************************/
C struct and function are defined in above. pls refer there.
C# Struct :
For C# struct declaration i have maintatined two struct for the one C struct. bcz one is for setting the values and another one id for passing to c function.
C# app struct:
[StructLayout(LayoutKind.Sequential)]//for app
public struct FPINPUTBIR
{
public byte byForm;
public InputBIRType InputBIR;
}
[StructLayout(LayoutKind.Sequential)] // here when i use explicit it throws exception so i removed it.
public struct InputBIRType
{
// [FieldOffset(0)]
public FPBIR pBIR;
//[FieldOffset(0)]
public int lSlotNr;
//[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public byte[] abyReserved;
}
C# wrapper struct:
[StructLayout(LayoutKind.Sequential)]
public struct FP_INPUTBIR
{
public byte byForm;
public IntPtr mIPBIR;
}
[StructLayout(LayoutKind.Explicit, Size = 20, CharSet = CharSet.Ansi)]
public struct Input_BIRType
{
[FieldOffset(0)]
public IntPtr mBIR;
[FieldOffset(0)]
public int lSlotNr;
//[FieldOffset(8)]
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
//public byte[] abyReserved;
}
finally i will copy the value from the C# app struct to wrapper struct before the call the C fun()
2a) C# App Side Code is :
//here mAppMemory is already known value
FPINPUTBIR lfipdata = new FPINPUTBIR();
FPDATA lfpdata = new FPDATA();
lfipdata.byForm = (byte)eFPVerifyBy.FULLBIR_INPUT;
lfipdata.InputBIR = new InputBIRType();
lfipdata.InputBIR.abyReserved = new byte[20];
lfipdata.InputBIR.pBIR.Data = new byte[mAppMemory[listBox2.SelectedIndex].Header.Length];
Array.Copy(mAppMemory[listBox2.SelectedIndex].Data, lfipdata.InputBIR.pBIR.Data, mAppMemory[listBox2.SelectedIndex].Header.Length);
lfipdata.InputBIR.pBIR.Header = mAppMemory[listBox2.SelectedIndex].Header;
Verify(ref lfipdata); //calling from C# APP side to C# wrapper
C# wrapper side:
public int Verify(ref FPINPUTBIR apStoredTemplate )
{
// i passed the args (apStoredTemplate ) but throws exception struct mismatch with C struct.
//here i don't know what should i do.
CDLL.StoreFinger(..,ref apStoredTemplate,.. ); //pls refer the C function above
}
Questions:
Do i really need two C# structures for this.
what should i do inside the C# wrapper function. please remeber i have two C# struct with diff members.
Thanks.
You just need a little extension on what you used in the previous question for PT_BIR. There we marshalled that variable length struct as byte[]. You can use the same code to generate the byte array, and I won't revisit that.
Next you need the union. That is:
[StructLayout(LayoutKind.Explicit, Size = 20)]
public struct PT_INPUT_BIR_UNION
{
[FieldOffset(0)]
public IntPtr pBIR;
[FieldOffset(0)]
public int lSlotNr; // I'm guessing what PT_LONG is
}
No need to declare the reserved part of the union. The size takes care of that.
Then PT_INPUT_BIR is
[StructLayout(LayoutKind.Sequential)]
public struct PT_INPUT_BIR
{
Byte byForm;
PT_INPUT_BIR_UNION InputBirUnion;
}
Then you need to use GCHandle to pin the PT_BIR byte array. Let's keep to the same naming as used at that question, and assume that the PT_BIR is held in a byte[] variable named data.
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
try
{
PT_INPUT_BIR inputBir;
inputBir.byForm := ...;
inputBir.InputBirUnion.pBIR = handle.AddrOfPinnedObject();
// now call StoreFinger passing ref inputBir
}
finally
{
handle.Free();
}
When you declare StoreFinger the PT_BIR* parameter should be declared as ref PT_BIR.

ORCA Api - C# p/invoke issues

I'm trying to work with the Powerbuilder ORCA Api from C#, and I'm having some issues with marshaling. Here is the C header file for the ORCA Api, I'm trying to implement the PBORCA_SccGetConnectProperties function, which requires the pborca_scc struct.
Here are my definitions:
[DllImport(OrcaModule, CharSet = CharSet.Auto)]
internal static extern int PBORCA_SccGetConnectProperties(IntPtr ORCASession,
[MarshalAs(UnmanagedType.LPWStr)] string Workspace,
ref OrcaSccInfo SCCInfo);
public delegate int TextOutDelegate(
[MarshalAs(UnmanagedType.LPWStr)] string data,
int userData);
public delegate void BuildProjectDelegate(
OrcaBuildError BuildError,
IntPtr userData);
[StructLayout(LayoutKind.Sequential)]
public sealed class OrcaSccInfo
{
internal IntPtr Wnd = IntPtr.Zero;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
internal string ProviderName = null;
internal int Capabilities = 0;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
internal string UserID = null;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 301)]
internal string Project = null;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 301)]
internal string LocalProjPath = null;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 301)]
internal string AuxPath = null;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 301)]
internal string LogFile = null;
internal TextOutDelegate SccMsgHandler = null;
internal BuildProjectDelegate OrcaMsgHandler = null;
internal int lCommentLen = 0;
internal int lAppend = 0;
internal IntPtr CommBlk = IntPtr.Zero;
internal int DeleteTempFiles = 0;
}
When calling the function like so:
ret = Interop.PBORCA_SccGetConnectProperties(session.Handle,
#"D:\PB11-test\test.pbw",
ref scc);
I receive a FatalExecutionEngineError with a message that it could be due to marshaling errors. I've gone over this again and again and can't seem to find where I'm making a mistake with how I'm marshaling the function, the struct, or the delegates.
For the delegate definition, that second parameter should be a uint rather than an int.
public delegate int TextOutDelegate([MarshalAs(UnmanagedType.LPWStr)] string data, uint userData);
The first parameter in the second delegate could possibly be a ref or a pointer.
public delegate void BuildProjectDelegate(ref OrcaBuildError BuildError, IntPtr userData);
And in the class definition you could try [MarshalAs(UnmanagedType.FunctionPtr)] on the delegate members, and in any methods that take delegates.
Is the size of the OrcaSccInfo class coming out the same size in C# as you'd expect it to be in the C++ code? When I put it into Linqpad and get the size I get 2584 for Auto charset, 1320 with Ansi charset and 1316 when I set Pack=1 on 64-bit and 1300 on 32-bit.
Have you tried replacing the strings with StringBuilders, particularly in the Dllimport declaration itself?
And is it possible that your delegates are being disposed of before they're being used?
Passing a delegate to unmanaged code doesn't create a reference that the garbage collector can see so unless you hold on to a reference to the delegate in your own code it'll be disposed of earlier than you expect.

PInvoke Marshalling Struct and BYTE* as parameter result the Bad Pointer

I had spent 1 day to find out why this problem happen, but the result is still failed.
When I debug in the Native DLL, it show the Bad Pointer for the second parameter. Need the expert in here to advice what is missing in my step that produce this error.
Native Struct
typedef struct
{
BYTE bcdTicketMainType;
BYTE bcdTicketSubType;
BYTE bcdValidityStartDate[4]; // YYYYMMDD
BYTE bcdValidityEndDate[4]; // YYYYMMDD
BYTE bcdPhysicalExpiryDate[4]; // YYYYMMDD
BYTE bFareZone;
SHORT sDepositAmount; // NEW ARGUMENT
LONG lBalance; // NEW ARGUMENT
BYTE bcdStationIDOrigin[2];
BYTE bcdStationIDDestination[2];
BYTE bcdPaymentType;
CHAR strPaymentMediaID[20];
CHAR strAgentID[8];
BYTE bcdShiftID;
} T_TK_KTMB_CSC_SALE_INFO;
Struct in C#
public struct T_TK_KTMB_CSC_SALE_INFO
{
public byte bcdTicketMainType; // 1
public byte bcdTicketSubType; // 1
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] bcdValidityStartDate; // YYYYMMDD
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] bcdValidityEndDate; // YYYYMMDD
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] bcdPhysicalExpiryDate; // YYYYMMDD
public byte bFareZone;
public short sDepositAmount; // NEW ARGUMENT
public long lBalance; // NEW ARGUMENT
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] bcdStationIDOrigin;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] bcdStationIDDestination;
public byte bcdPaymentType;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] szPaymentMediaID;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 8)]
public char[] szAgentID;
public byte bcdShiftID;
}
Native Function
int KTMBBiz_CSCSale( T_TK_KTMB_CSC_SALE_INFO CSCSaleInfo, BYTE*
pbTranxData );
Function in C#
[DllImport("KTMBBizRule.dll")]
public static extern int KTMBBiz_CSTSale([In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = >127)] byte[] pbTranxData, T_TK_KTMB_CST_SALE_INFO CSTSaleInfo);
Function called in C#
private void btnCscSale_Click(object sender, EventArgs e)
{
T_TK_KTMB_CSC_SALE_INFO cscSale = new T_TK_KTMB_CSC_SALE_INFO();
byte[] trxData = new byte[2];
BizRule.KTMBBiz_CSCSale(cscSale, trxData);
}
Error founded in Native C++
the SizeParamIndex attribute means that the n-th function parameter (zero-based, counting from the left) contains the actual size of the array.
your function has only 2 parameters, not 127.
what is more, you swapped the pbTranxData and CSTSaleInfo parameters in C++ and C#.
you can use SizeParamIndex to let the C++ function know the size of pbTranxData array:
C++
int KTMBBiz_CSCSale(T_TK_KTMB_CSC_SALE_INFO CSCSaleInfo,
BYTE* pbTranxData, INT iLength);
C#
[DllImport("KTMBBizRule.dll")]
public static extern int KTMBBiz_CSTSale(
T_TK_KTMB_CST_SALE_INFO CSTSaleInfo,
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=2)]
byte[] pbTranxData,
int iLength);
now call it with KTMBBiz_CSTSale(cscSale, trxData, trxData.Length);
you should also add [StructLayout(LayoutKind.Sequential)] before your C# structure.
instead of public char[] szPaymentMediaID; you can say string szPaymentMediaID.
By default strings are marshalled as char*, you can change it to UnmanagedType.ByValTStr with given SizeConst.

C# Custom PrintDialog PInvoke DevMode Question

According to the following page:
http://msdn.microsoft.com/en-us/library/ms646964(VS.85).aspx
underneath the first graphic, "If the user clicks the OK button, PrintDlg returns TRUE and the PRINTDLG structure to return informmation about the user's selection".
In this case, my custom print dialog is nearly working, but I'm trying to extract the information about printer name, orientation, etc... My understanding is that in order to retrieve the printer name, I need to examine the hDevMode value from the PRINTDLG structure to see the printer name. Is there a function that will allow me to extract that info?
My code is like (where pdlg is my defined instance of the PRINTDLG structure):
bool f = false;
try
{
f = PrintDlg(ref pdlg);
DEVMODE dm = pdlg.hDevMode;
int k = 0;
} catch (Exception ex)
{
// hopefully it doesn't fail
}
If someone has any pearlsof wisdom out there, I would sure appreciate any tips.
The following shows how to extract the printer name and driver. The key is to do a GlobalLock on hDevNames, Marshal.PtrToStructure it into the CLR version of the struct, and then access its content. Remember to GlobalUnlock when done.
You could do something similar with hDevMode, which will get you information about the printer metrics and setup. You can find a C# declaration of the DEVMODE struct here.
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication3 {
class Program {
// Win32 struct declarations
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
[System.Runtime.InteropServices.ComVisible(false)]
internal class PRINTDLG {
public Int32 lStructSize;
public IntPtr hwndOwner;
public IntPtr hDevMode;
public IntPtr hDevNames;
public IntPtr hDC = IntPtr.Zero;
public Int32 Flags;
public Int16 FromPage = 0;
public Int16 ToPage = 0;
public Int16 MinPage = 0;
public Int16 MaxPage = 0;
public Int16 Copies = 0;
public IntPtr hInstance = IntPtr.Zero;
public IntPtr lCustData = IntPtr.Zero;
public IntPtr lpfnPrintHook;
public IntPtr lpfnSetupHook = IntPtr.Zero;
public IntPtr lpPrintTemplateName = IntPtr.Zero;
public IntPtr lpSetupTemplateName = IntPtr.Zero;
public IntPtr hPrintTemplate = IntPtr.Zero;
public IntPtr hSetupTemplate = IntPtr.Zero;
}
[StructLayout(LayoutKind.Sequential)]
public class DEVNAMES {
public short wDriverOffset;
public short wDeviceOffset;
public short wOutputOffset;
public short wDefault;
}
// import PrintDlg, GlobalLock and GlobalUnlock
[DllImport("comdlg32.dll", CharSet = CharSet.Auto)]
private static extern bool PrintDlg([In, Out] PRINTDLG lppd);
[DllImport("kernel32.dll")]
private static extern IntPtr GlobalLock(IntPtr hMem);
[DllImport("kernel32.dll")]
private static extern bool GlobalUnlock(IntPtr hMem);
static void Main(string[] args) {
// show the printer dialog box
PRINTDLG pd = new PRINTDLG();
pd.lStructSize = Marshal.SizeOf(pd);
PrintDlg(pd);
// here's the meat -- extract the printer information
// out of pd.hDevNames...
DEVNAMES devNames = new DEVNAMES();
// lock hDevNames into memory and get a pointer to it
IntPtr pDevNames = GlobalLock(pd.hDevNames);
// marshal into a DEVNAME struct
Marshal.PtrToStructure(pDevNames, devNames);
// pull out the device and driver strings; hopefully not much of
// that in DEVMODE
string sDevice = Marshal.PtrToStringUni((IntPtr) (
pDevNames.ToInt32() +
devNames.wDeviceOffset * Marshal.SystemDefaultCharSize));
string sDriver = Marshal.PtrToStringUni((IntPtr) (
pDevNames.ToInt32() +
devNames.wDriverOffset * Marshal.SystemDefaultCharSize));
string sOutput = Marshal.PtrToStringUni((IntPtr) (
pDevNames.ToInt32() +
devNames.wOutputOffset * Marshal.SystemDefaultCharSize));
// done -- release the global memory handle
GlobalUnlock(pd.hDevNames);
}
}
}