I am trying to use the Hikvision SDK https://www.hikvision.com/en/support/download/sdk/
My current goal is to open the door (trigger an output) with the intercom outdoor station.
I managed to do the login (NET_DVR_LoginV40) and display the outdoor station's camera feed.
My next step would be to open the door. For this I need to call the NET_DVR_RemoteControl function passing in among others the struct of NET_DVR_Control_GateWay.
Now when doing this it does not work it returns error 17 which is apparently:
Parameter error. Input or output parameters in the SDK API is NULL, or
the value or format of the parameters does not match with the
requirement.
So it is practically 100% that something is iffy in my C# code. But I have no idea what it is and as far as I know this is quite impossible to pinpoint easily if you are not an expert at this (I am not)
The dll import for the function:
[DllImport(#"..\bin\HCNetSDK.dll")]
public static extern bool NET_DVR_RemoteControl(int lUserID, uint dwCommand, IntPtr lpInBuffer, uint dwInBufferSize);
The struct for the parameter of the previous function:
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct NET_DVR_Control_GateWay
{
public uint dwSize;
public uint dwGatewayIndex;
public byte byCommand;
public byte byLockType;
public UInt16 wLockID;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 32, ArraySubType = UnmanagedType.I1)]
public byte[] byControlSrc;
public byte byControlType;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.I1)]
public byte[] byRes3;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 16, ArraySubType = UnmanagedType.I1)]
public byte[] byPassword;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 108, ArraySubType = UnmanagedType.I1)]
public byte[] byRes2;
public void Init()
{
byRes3 = new byte[64];
byRes2 = new byte[108];
}
}
My method for the door opening:
private void button_Door1_Click(object sender, EventArgs e)
{
CHCNetSDK.NET_DVR_Control_GateWay gateWay = new CHCNetSDK.NET_DVR_Control_GateWay();
gateWay.Init();
gateWay.dwSize = (uint)Marshal.SizeOf(gateWay);
gateWay.dwGatewayIndex = 1;
gateWay.byCommand =1; //opening command
gateWay.byLockType = 0 ; //this is a normal lock not a smart lock
gateWay.wLockID = 0; //this is 0 because I want to use the door station's output
gateWay.byControlSrc = new byte[] {123} ; // this needs to be something, but doesn't matter what
gateWay.byControlType = 1 ; //this needs to be 1 or 2 but does not matter which
//gateWay.byPassword = ; this is not needed because the LockType is 0
IntPtr ptrStruData = Marshal.AllocHGlobal((int)gateWay.dwSize);
var dd = CHCNetSDK.NET_DVR_RemoteControl(lUserID, 16009, ptrStruData, gateWay.dwSize);
MessageBox.Show(dd.ToString() + CHCNetSDK.NET_DVR_GetLastError().ToString() + "\n" + gateWay.dwSize.ToString() + "\n" + "ptrStruData:" + ptrStruData.ToString());
}
According to the documentation the function looks like this
And the struct is defined as such
So according to my knowledge I have done the definition and import correctly.
I would appreciate if someone could set me in the right direction as I have never worked with C#, c++ interoperation before and at this point I have no idea how to go forward, how could I debug, how could I determine the problem in my code.
I have tried contacting the manufacturer about this issue, but they cannot help directly with my code, and from their perspective everything is working as it should as I get back the error that it is me who is the cause for the issue.
Your help is much appreciated!
You allocate some memory with Marshal.AllocHGlobal, you do not copy your gateWay structure into that memory, you pass the pointer to the allocated memory to NET_DVR_RemoteControl so that it sees random garbage, and then you do not free the allocated memory so that it leaks.
You could have fixed it by copying gateWay into the allocated memory and then freeing it after the call.
Instead, however, it is better to let the marshaller do its job:
[DllImport(HIKVISION_LIBRARY, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = false)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool NET_DVR_RemoteControl(int lUserID, int dwCommand, [In] ref NET_DVR_CONTROL_GATEWAY lpInBuffer, int dwInBufferSize);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct NET_DVR_CONTROL_GATEWAY
{
public int dwSize;
public int dwGatewayIndex;
public byte byCommand;
public byte byLockType;
public short wLockID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAME_LEN)]
public string byControlSrc;
public byte byControlType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] byRes3;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = PASSWD_LEN)]
public string byPassword;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 108)]
public byte[] byRes2;
}
var gateWay = new NET_DVR_CONTROL_GATEWAY()
{
dwSize = Marshal.SizeOf<NET_DVR_CONTROL_GATEWAY>(),
dwGatewayIndex = 1,
byCommand = 1,
byLockType = 0,
wLockID = 0,
byControlType = 1
};
var dd = NET_DVR_RemoteControl(lUserID, 16009, ref gateWay, gateWay.dwSize);
Related
I have a C++ DLL with an exported function:
__declspec (dllexport) int AllocateBuf(DataBufferIn *in);
This function allocate buffer array and assign value for test
__declspec (dllexport) int AllocateBuf(DataBufferIn *in){
for (int i = 0 ; i < 4; i++){
in->data_r[i] = (double*)VirtualAlloc(
NULL, // System selects address
1024 * sizeof(double), // Size of allocation
MEM_RESERVE | MEM_COMMIT, // Allocate reserved pages
PAGE_READWRITE); // Protection = no access
for (int j = 0; j < 128; i++){
(in->data_r[i])[j] = j;//Assign value for test
}
}
}
The struct DataBufferIn is like this
struct DataBufferIn
{
double* data_r[4];
double* data_g[4];
};
The following is my code, i wanna marshal this struct in c#, but I get a exception (An unhandled exception of type 'System.Runtime.InteropServices.SafeArrayTypeMismatchException' occurred in C_Sharp_Sample.exe). I get stuck in this for much times, does anyone know how to solve this issue? Thanks :)
public struct DataBufferIn
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public IntPtr[] data_r;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public IntPtr[] data_g;
};
[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int AllocateBuf(ref DataBufferIn _data_buf_in);
private void button_allocate_buf_Click(object sender, EventArgs e)
{
DataBufferIn data_buf_in = new DataBufferIn();
AllocateBuf(ref data_buf_in);//<---Function call fail...
double[] doubleArrayRed = new double[1024];
Marshal.Copy(data_buf_in.lum_data_r[0], doubleArrayRed, 0, 1024);
.....
}
According to Charlieface suggestion,
Add [StructLayout(LayoutKind.Sequential)] could solve this issue.
[StructLayout(LayoutKind.Sequential)]
public struct DataBufferIn
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public IntPtr[] data_r;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public IntPtr[] data_g;
};
I want to use a c++ dll in c#. I'm using [DllImport] to call the method. I'm having trouble passing struct to a method.
I have a C struct:
typedef struct
{
DWORD TopPoint;
DWORD EndPoint;
WORD dwCount;
MYFUNC_NUMERIC11 *pGetData;
} MYFUNC_BUFFERNORMAL;
MYFUNC_NYMERIC11 is another struct.
typedef struct
{
BYTE Sign; // Sign ("±")
BYTE Integer[3]; // 3-digit integer (no zero suppression)
BYTE Period; // Decimal point (".")
BYTE Decimal[6]; // 6-digit decimal number
} MYFUNC_NUMERIC11;
I have written a C# struct to mimic this.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public unsafe struct MYFUNC_BUFFERNORMAL
{
public uint TopPoint;
public uint EndPoint;
public ushort Count;
public IntPtr pGetData;
}
A pointer to the struct is an argument in a method. C# function is:
[DllImport("MYFUNC_DLL.dll", EntryPoint = "MYFUNC_GetData", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, ThrowOnUnmappableChar = true)]
public static extern int MYFUNC_GetData(IntPtr myfuncHandle, UInt32 dwIO, ref IntPtr pBufferNormal, Byte bccFlg);
This is the method in C:
MYFUNC_STATUS MYFUNC_GetData(MYFUNC_HANDLE myfuncHandle, DWORD dwOut, MYFUNC_BUFFERNORMAL *pBufferNormal , BYTE bccFlg)
The return type is cast to an enum, which has an interpretation. The struct parameter is invalid. I've tried to allocate memory using Marshal.AllocHGlobal(...), but the parameter is still invalid, i.e. there is no error during compilation but the value returned is incorrect.
I've spent quite a few hours on this, still unable to figure out what to do. A lot of similar questions exist already, like here: How do I convert c struct from dll to C# or here: How to pass C# array to C++ and return it back to C# with additional items?, but I, somehow, still haven't figured out a way.
Something like this should work, at least with one element in the array (is it an array?). For an array, you will have to allocate sizeof * count of elements and marshal (StructureToPtr) each element at its offset.
var num = new MYFUNC_NUMERIC11();
num.Integer = new byte[] { 1, 2, 3 };
num.Decimal = new byte[] { 4, 5, 6, 7, 8, 9 };
num.Sign = 10;
num.Period = 11;
var buffer = new MYFUNC_BUFFERNORMAL();
buffer.Count = 1234;
buffer.EndPoint = 5678;
buffer.TopPoint = 9;
buffer.pGetData = Marshal.AllocCoTaskMem(Marshal.SizeOf(num));
try
{
Marshal.StructureToPtr(num, buffer.pGetData, false);
MYFUNC_GetData(Whatever, 0, ref buffer, 0);
}
finally
{
Marshal.FreeCoTaskMem(buffer.pGetData);
}
With these definitions.
[StructLayout(LayoutKind.Sequential)]
public struct MYFUNC_BUFFERNORMAL
{
public uint TopPoint;
public uint EndPoint;
public ushort Count;
public IntPtr pGetData;
}
[StructLayout(LayoutKind.Sequential)]
public struct MYFUNC_NUMERIC11
{
public byte Sign;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] Integer;
public byte Period;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] Decimal;
}
// check calling convention
[DllImport(#"MYFUNC_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern MYFUNC_STATUS MYFUNC_GetData(IntPtr myfuncHandle, uint dwIO, ref MYFUNC_BUFFERNORMAL pBufferNormal, byte bccFlg);
I am working on WIN CE platform, developing windows forms in C#.Net.
The DeviceIoControl API is working fine with the parameters (mentioned below) in c++ console application.
PNIC_STATISTICS structure in nuiouser.h
global declarations :
TCHAR PCI1_NAME[] = _T("PCI\\ManiXX1");
TCHAR *AUB_NAME = NULL;
AUB_NAME = PCI1_NAME;
pNICStat = (PNIC_STATISTICS)malloc(sizeof(NIC_STATISTICS)) ;
pNICStat->ptcDeviceName = AUB_NAME ; //wchar_t* ptcDeviceName
DeviceIoControl( hUB94Port, //void*
IOCTL_NDISUIO_NIC_STATISTICS,
pNICStat, //PNIC_STATISTICS
0,
pNICStat, //PNIC_STATISTICS
sizeof(NIC_STATISTICS),
&dwReturnedBytes,
NULL
);
<==============================================================================>
But I'm getting problems in implementing the same with C#.Net CF for WIN-CE7
My WIN-CE Code is as follows:
Modified Structure in C#:
[StructLayout(LayoutKind.Sequential,CharSet = CharSet.Unicode)]
public struct __NIC_STAT
{
ulong Size; // Of this structure.
public Char[] ptcDeviceName; // The device name to be queried..
ulong DeviceState; // DEVICE_STATE_XXX above
ulong DeviceState; // DEVICE_STATE_XXX above
ulong MediaType; // NdisMediumXXX
ulong MediaState; // MEDIA_STATE_XXX above
ulong PhysicalMediaType;
ulong LinkSpeed; // In 100bits/s. 10Mb/s = 100000
UInt64 PacketsSent;
UInt64 PacketsReceived;
ulong InitTime; // In milliseconds
ulong ConnectTime; // In seconds
UInt64 BytesSent; // 0 - Unknown (or not supported)
UInt64 BytesReceived; // 0 - Unknown (or not supported)
UInt64 DirectedBytesReceived;
UInt64 DirectedPacketsReceived;
ulong PacketsReceiveErrors;
ulong PacketsSendErrors;
ulong ResetCount;
ulong MediaSenseConnectCount;
ulong MediaSenseDisconnectCount;
} ;
From this Structure I am just filling ptcDeviceName and trying to send.
__NIC_STAT NIC_STAT = new __NIC_STAT();
Char[] toBytes = {'P','C','I','\\','M','a','n','i','X','X','1','\0'}
NIC_STAT.ptcDeviceName = toBytes; //public Char[] ptcDeviceName; in structure
// __NIC_STAT this is the same structure as
//in nuiouser.h
int sz = Marshal.SizeOf(NIC_STAT.GetType());//sometimes Getting exception here
intptr ptr = Marshal.AllocHGlobal(sz);
Marshal.StructureToPtr((__NIC_STAT)NIC_STAT, ptr, false);
unsafe
{
DeviceIoControl(hFileHandle,
IOCTL_NDISUIO_NIC_STATISTICS,
ref ptr,
0,
ref ptr,
sz,
ref dwReturnedBytes,
0);
}//unsafe
It's corresponding prototype
[DllImport("coredll.dll", CharSet = CharSet.Auto, SetLastError = true)]
unsafe public static extern bool DeviceIoControl(
int hDevice,
int dwIoControlCode,
ref intptr InBuffer,
int nInBufferSize,
ref intptr OutBuffer,
int nOutputBufferSize,
ref int pBytesReturned,
int pOverlapped
);
In Win-CE DeviceIoControl() is getting failed, with exception and not displaying any error codes. and sometimes getting error code as 87 (INVALID PARAMETERS).
I feel ptcDeviceName is creating the problem or may be because of allocating memory for pointer (ptr) ?
In Console application we are sending ptcDeviceName as Wchar_t* but in WIN-CE so I used
public Char[] ptcDeviceName;
Can anybody tell me where I am doing wrong.?
You have a couple problems going on here.
First is that you seem to think a ulong is 32-bits in C#, but it's not. It'64 bits, so your struct is totally mapped wrong.
Second, I'm sure you need to be setting the struct Size member before passing it to the call.
Third, that ptcDeviceName member is a pointer to a wide character string. That means that in the struct itself it's 4 bytes. I'd likely make it an IntPtr. You then need to separately allocate the string, pin it, and put the pointer to it into that member slot. Since `StringToHGlobal doesn't exist in the CF, it would look something like this:
public struct __NIC_STAT
{
public uint Size;
public IntPtr ptcDeviceName;
public uint DeviceState;
public uint DeviceState;
public uint MediaType;
public uint MediaState;
public uint PhysicalMediaType;
public uint LinkSpeed;
public ulong PacketsSent;
public ulong PacketsReceived;
public uint InitTime;
public uint ConnectTime;
public ulong BytesSent;
public ulong BytesReceived;
public ulong DirectedBytesReceived;
public ulong DirectedPacketsReceived;
public uint PacketsReceiveErrors;
public uint PacketsSendErrors;
public uint ResetCount;
public uint MediaSenseConnectCount;
public uint MediaSenseDisconnectCount;
};
....
var myStruct = new __NIC_STAT();
myStruct.Size = (15 * 4) + (6 * 8);
var name = "PCI\\Manixx1\0";
var nameBytes = Encoding.Unicode.GetBytes(name);
myStruct.ptcDeviceName = Marshal.AllocHGlobal(nameBytes.Length);
try
{
Marshal.Copy(nameBytes, 0, myStruct.ptcDeviceName, nameBytes.Length);
// make the IOCTL call, a-la
NativeMethods.DeviceIoControl(...., ref myStruct, ....);
}
finally
{
Marshal.FreeHGlobal(myStruct.ptcDeviceName);
}
I cannot seem to pass the right arguments correctly. I get
"Invalid Name Parsing buffer size" error
Lotus notes function in dname.h
STATUS LNPUBLIC DNParse(
DWORD Flags,
const char far *TemplateName,
const char far *InName,
DN_COMPONENTS far *Comp,
WORD CompSize);
structure below
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DN_COMPONENTS
{
public int Flags;
[MarshalAs(UnmanagedType.LPWStr)]
public string C;
public short OLength;
[MarshalAs(UnmanagedType.LPWStr)]
public string O;
[MarshalAs(UnmanagedType.LPWStr)]
public string CN;
};
c#
Below is what I have tried
Status sts = 0;
StringBuilder szServer = new StringBuilder(names.MAXUSERNAME);
string notUsedString = null;
DWORD notUsed = 0;
dname.DN_COMPONENTS xdDC = new DN_COMPONENTS();
sts = nnotesDLL.SECKFMGetUserName(szServer);
//IntPtr structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(dname.DN_COMPONENTS)));
//UInt16 num = Convert.ToUInt16(Marshal.SizeOf(structPtr));
//WORD num = Convert.ToUInt16(Marshal.SizeOf(structPtr));
int num = Marshal.SizeOf(typeof(DN_COMPONENTS));
IntPtr structPtr = Marshal.AllocCoTaskMem(num);
sts = nnotesDLL.DNParse(notUsed, notUsedString, szServer, structPtr, (UInt32)num);
this.xdDC = (dname.DN_COMPONENTS)Marshal.PtrToStructure(structPtr, typeof(dname.DN_COMPONENTS));
xdDC.CN = Decode(Marshal.ReadIntPtr(structPtr), ushort.MaxValue);
///CN=SomeFirstName SomeLastName/OU=Corp/O=test
I am looking for "SomeFirstName SomeLastName"
[DllImport("nnotes.DLL", CallingConvention = CallingConvention.StdCall)]
public unsafe static extern Status DNParse(DWORD notUsed, string notUsedString, StringBuilder InName, IntPtr structPtr, UInt32 structPtrSizeOf);
I have tried all variations by ref to all, ref to struct, changed to string, int, uint Nothing!!!
Help...
What I am looking for is CN=SomeFirstName SomeLastName/OU=Corp/O=test
The problems that I can see:
You did not declare all the fields in the struct.
It is unlikely that the struct is packed. Remove the Pack setting.
You need to declare the pointers in the struct as IntPtr. The marshaller cannot marshal them from native to managed. Declare them as IntPtr and use Marshal.PtrToStringAnsi to convert.
I am trying to read records from a Btrieve (v6.15) database by using btrieve API from C# code via P/Invoke.
I have managed to read records, however last character of strings are cropped while reading. If I increase the string size in my data struct then the string is read properly but this time the next variable is not read correctly.
What might be wrong here?
Btrieve function declaration:
[DllImport("WBTRV32.dll", CharSet = CharSet.Ansi)]
static extern short BTRCALL(ushort operation,
[MarshalAs(UnmanagedType.LPArray, SizeConst = 128)] byte[] posBlk,
[MarshalAs(UnmanagedType.Struct, SizeConst = 255)]
ref RecordBuffer databuffer,
ref int dataLength,
[MarshalAs(UnmanagedType.LPArray, SizeConst = 255)] char[] keyBffer,
ushort keyLength, ushort keyNum);
My structure definition:
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct RecordBuffer
{
public short docType;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string docDescPlural;
public short sorting;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string docDescSingle;
public short copyOtherThanSrc;
public double defaultNotebookNo;
}
These are the sizes for the columns, from Btreive database manager:
signed int, 2 bytes
string, 15 bytes
signed int, 2 bytes
string, 15 bytes
signed int, 2 bytes
float, 8 bytes
The code:
private void PopulateAllRecords(string fileName)
{
byte[] positionBlock = new byte[128];
char[] fileNameArray = fileName.ToCharArray();
// Open file
RecordBuffer dataBuffer = new RecordBuffer();
int bufferLength = System.Runtime.InteropServices.Marshal.SizeOf(dataBuffer);
BReturnCodes status = (BReturnCodes) BTRCALL(
BOPEN, positionBlock, ref dataBuffer, ref bufferLength, fileNameArray, 0, 0);
if (status == BReturnCodes.NO_ERROR)
{
// Get first record
dataBuffer = new RecordBuffer();
status = (BReturnCodes) BTRCALL(
BGETFIRST, positionBlock, ref dataBuffer, ref bufferLength, fileNameArray, 0, 0);
if (status == BReturnCodes.NO_ERROR)
{
AddListViewItem(dataBuffer);
}
// Get subsequent records
while (status == BReturnCodes.NO_ERROR) // BReturnCodes.END_OF_FILE or an error will occur
{
dataBuffer = new RecordBuffer();
status = (BReturnCodes)BTRCALL(
BGETNEXT, positionBlock, ref dataBuffer, ref bufferLength, fileNameArray, 0, 0);
if (status == BReturnCodes.NO_ERROR)
{
AddListViewItem(dataBuffer);
}
}
}
else
{
MessageBox.Show("Error occured while opening file: " + status.ToString());
}
}
private void AddListViewItem(RecordBuffer buffer)
{
ListViewItem item = new ListViewItem(buffer.docType.ToString());
item.SubItems.Add(buffer.docDescPlural);
item.SubItems.Add(buffer.sorting.ToString());
item.SubItems.Add(buffer.docDescSingle);
item.SubItems.Add(buffer.copyOtherThanSrc.ToString());
item.SubItems.Add(buffer.defaultNotebookNo.ToString());
listView.Items.Add(item);
}
Edit: Changing the string to char array (thanks to weismat's answer) resolved the problem. Happy!
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct RecordBuffer
{
public short docType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 15)]
public char[] docDescPlural;
public short sorting;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 15)]
public char[] docDescSingle;
public short copyOtherThanSrc;
public double defaultNotebookNo;
}
I would asume that the issue comes from the \0 which is expected when using a C# string with p/invoke.
I have no experience with Btrieve, but it is pretty likely there is no \0.
Could you post the original c structure? Otherwise you need to use a byte array with a fixed length of 15 and convert it into a string afterwards.
Furthermore I would use the sizeof operator to compare the size of the structure on both ends.