Quser.exe allows a client to see user sessions on a remote RDP server. For example,
C:\>quser /server:MyRDPserver
USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME
userA 3 Disc 1+20:03 08/07/2014 12:36
userB 4 Disc 1+22:28 08/07/2014 10:38
I would like to build this functionality into a C++ or C# program. Yes, I could just spawn quser.exe and parse the output, but is there an Win32 API or .Net framework class that can give me the same information? Specifically:
User Name
Connection State
Logon time
I've found that using WMI (Win32_LoggedOnUser) to find the same information is unreliable, as it often lists stale connections. I've also tried the psloggedon approach of enumerating subkeys of HKEY_USERS and looking for the Volatile Environment key, but this also suffers from the same problem.
I'm going to answer my own question.
First of all, you need to make sure that permissions are set correctly on the target machine. This entails setting HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\AllowRemoteRPC to 1. A powershell script to do this is:
# Get the service account credential
$cred = Get-Credential my_admin_account
$Target_Computers = #("Computer_1","Computer_2")
# open the remote registry
[long]$HIVE_HKLM = 2147483650
foreach($c in $Target_Computers)
{
$StdRegProv = Get-WmiObject -List -Namespace root\default -ComputerName $c -Credential $cred | where { $_.Name -eq "StdRegProv" }
$StdRegProv.SetDWORDValue($HIVE_HKLM, "SYSTEM\CurrentControlSet\Control\Terminal Server", "AllowRemoteRPC", 1)
}
As Xearinox said, for C++ you can use the WTSxxx functions in the Win32 API. Assuming your computers are not XP, here is some C++ code:
#include <string>
#include <iostream>
#include <iomanip>
#include <windows.h>
#include <WtsApi32.h>
using namespace std;
const unsigned num_connection_states = 10;
const wchar_t* connection_state_list[num_connection_states] = {
L"Active",
L"Connected",
L"ConnectQuery",
L"Shadow",
L"Disc",
L"Idle",
L"Listen",
L"Reset",
L"Down",
L"Init" };
int print_error(DWORD err)
{
// format the message
LPTSTR* ppBuffer = nullptr;
DWORD retval = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, err, 0, reinterpret_cast<LPTSTR>(ppBuffer), 0, nullptr);
// print out
wcerr << "Error: *ppBuffer" << endl;
return 1;
}
wstring format_time(const LARGE_INTEGER& time)
{
// convert to a local Win32 file time
FILETIME ft = { time.LowPart, time.HighPart };
FileTimeToLocalFileTime( &ft, &ft );
// convert to a system time
SYSTEMTIME st;
FileTimeToSystemTime( &ft, &st );
wchar_t local_date[255], local_time[255];
GetDateFormat( LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, local_date, sizeof(local_date)/sizeof(wchar_t) );
GetTimeFormat( LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, local_time, sizeof(local_time)/sizeof(wchar_t) );
wstring result = local_date;
result.append(L" ");
result.append(local_time);
return result;
}
const _int64 SECOND = 10000000;
const _int64 MINUTE = 60*SECOND;
const _int64 HOUR = 60*MINUTE;
const _int64 DAY = 24*HOUR;
wstring format_timespan(const LARGE_INTEGER& timespan)
{
// convert to a local Win32 file time
FILETIME ft = { timespan.LowPart, timespan.HighPart };
FileTimeToLocalFileTime( &ft, &ft );
// convert to a system time
SYSTEMTIME st;
FileTimeToSystemTime( &ft, &st );
wchar_t local_time[255];
int daydiff = floor(
GetTimeFormat( LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, local_time, sizeof(local_time)/sizeof(wchar_t) );
wstring result = local_date;
result.append(L" ");
result.append(local_time);
return result;
}
int wmain(int argc, wchar_t* argv[])
{
// check args
if(argc > 2)
{
wcout << "Usage: " << argv[0] << " [server_name]\n";
return 1;
}
// server name
bool current_server = true;
wstring server_name = L".";
if(argc == 2)
{
server_name = argv[1];
current_server = false;
}
// open the server
HANDLE hServer;
if(current_server)
hServer = WTS_CURRENT_SERVER_HANDLE;
else
hServer = WTSOpenServer(const_cast<LPWSTR>(server_name.c_str()));
// enumerate through the sessions
DWORD Count = 0;
WTS_SESSION_INFO* pSessionInfo = nullptr;
BOOL success = WTSEnumerateSessions(hServer, 0, 1, &pSessionInfo, &Count);
if(success == 0)
return false;
// write the headers
wcout << " " << left << setw(24) << "USERNAME";
wcout << setw(19) << "SESSIONNAME";
wcout << "ID ";
wcout << setw(9) << "STATE";
wcout << "IDLE TIME LOGON TIME";
// loop through each session
for(unsigned long s=0; s<Count; s++)
{
LPTSTR pBuffer = nullptr;
DWORD BytesReturned = 0;
wcout << "\n " << left;
// try getting all info at once
WTSINFO* info = nullptr;
success = WTSQuerySessionInformation(hServer, pSessionInfo[s].SessionId, WTSSessionInfo, reinterpret_cast<LPTSTR*>(&info), &BytesReturned);
bool have_wtsinfo = true;
if(!success)
{
// see why failed
DWORD err = GetLastError();
if(err == ERROR_NOT_SUPPORTED)
have_wtsinfo = false;
else
return print_error(err);
}
// print user name
wstring user_name;
if(have_wtsinfo)
user_name = info->UserName;
else
{
success = WTSQuerySessionInformation(hServer, pSessionInfo[s].SessionId, WTSUserName, &pBuffer, &BytesReturned);
if(!success)
continue;
user_name = pBuffer;
WTSFreeMemory(pBuffer);
}
wcout << setw(24) << user_name;
// print session name
wstring session_name;
if(have_wtsinfo)
session_name = info->WinStationName;
else
{
success = WTSQuerySessionInformation(hServer, pSessionInfo[s].SessionId, WTSWinStationName, &pBuffer, &BytesReturned);
if(!success)
continue;
session_name = pBuffer;
WTSFreeMemory(pBuffer);
}
wcout << setw(19) << session_name;
// print session ID
wcout << right << setw(2) << pSessionInfo[s].SessionId;
// print connection state
WTS_CONNECTSTATE_CLASS connect_state;
if(have_wtsinfo)
connect_state = info->State;
else
{
success = WTSQuerySessionInformation(hServer, pSessionInfo[s].SessionId, WTSConnectState, &pBuffer, &BytesReturned);
if(!success)
continue;
connect_state = *reinterpret_cast<WTS_CONNECTSTATE_CLASS*>(pBuffer);
WTSFreeMemory(pBuffer);
}
if(connect_state>=num_connection_states)
continue;
wcout << " " << left << setw(8) << connection_state_list[connect_state];
// get idle time
LARGE_INTEGER idle = info->CurrentTime;
idle.QuadPart -= info->LogonTime.QuadPart;
// print logon time - not supported
if(info->LogonTime.QuadPart!=0)
{
wcout << format_time(info->LogonTime);
}
// clean up
WTSFreeMemory(info);
}
// clean up
WTSFreeMemory(pSessionInfo);
if(!current_server)
WTSCloseServer(hServer);
}
For C#, the easiest way is to use the Cassia library, which is basically a C# wrapper around the same API functions.
You can call a Win32 API to create a process and pass the "quser /server:MyRDPserver" as parameters,I usually do like this:
PROCESS_INFORMATION process_info;
STARTUPINFOA startup_info;
string cmdline2;
char error_msg[1024];
memset(&process_info, 0, sizeof(process_info));
memset(&startup_info, 0, sizeof(startup_info));
startup_info.cb = sizeof(startup_info);
argc = argarray.size();
for(int i = 0; i < argc; i++) {
cmdline2 += argarray.at(i);
if(i != (argc - 1)) cmdline2 += " ";
}
string command = suCmdLineRemoveQuotations(argarray.at(0));
retval = CreateProcessA(command.c_str(), (LPSTR)cmdline2.c_str(), NULL, NULL, TRUE,
0, NULL, NULL, &startup_info, &process_info);
if (!retval) {
windows_error_string(error_msg, sizeof(error_msg));
error = error_msg;
return false;
}
WaitForSingleObject(process_info.hProcess, msecs);
if(GetExitCodeProcess(process_info.hProcess, &status)) {
// status maybe is STILL_ACTIVE, in that case, the process is killed
if(status == STILL_ACTIVE) {
TerminateProcess(process_info.hProcess, 1);
}
ecode = status;
}
return true;
when the process startup, you can redirect the output.If you use Qt,the problem become simple,you can use QProcess to implement.
Related
I've a NFC reader along with MIFARE Classic 1K card. I've a Visual C# winforms project. Right now I'm able to connect to the reader and detect the card and get it's UUID. The problem I'm facing is while writing and reading data. I searched a lot on internet, found some solution even tested the demo code provided with the SDK... nothing's working.
Let me describe the workflow and code I'm using for writing, authenticating a block, sending APDU and reading the block.
Following is the code for writing data to block 5.
String tmpStr = Text;
int indx;
if (authenticateBlock(Block))
{
ClearBuffers();
SendBuff[0] = 0xFF; // CLA
SendBuff[1] = 0xD6; // INS
SendBuff[2] = 0x00; // P1
SendBuff[3] = (byte)int.Parse(Block); // P2 : Starting Block No.
SendBuff[4] = (byte)int.Parse("16"); // P3 : Data length
SendBuff[5] = 0xFF;
SendBuff[6] = 0xFF;
SendBuff[7] = 0xFF;
SendBuff[8] = 0xFF;
SendBuff[9] = 0xFF;
SendBuff[10] = 0xFF;
for (indx = 0; indx <= (tmpStr).Length - 1; indx++)
{
SendBuff[indx + 5] = (byte)tmpStr[indx];
}
SendLen = SendBuff[4] + 5;
RecvLen = 0x02;
retCode = SendAPDUandDisplay(2);
if (retCode != Card.SCARD_S_SUCCESS)
{
MessageBox.Show("fail write");
}
else
{
MessageBox.Show("write success");
}
}
else
{
MessageBox.Show("FailAuthentication");
}
CloseCardConnection();
The function SendAPDUandDisplay is as below
private int SendAPDUandDisplay(int reqType)
{
int indx;
string tmpStr = "";
pioSendRequest.dwProtocol = Aprotocol;
pioSendRequest.cbPciLength = 8;
//Display Apdu In
for (indx = 0; indx <= SendLen - 1; indx++)
{
tmpStr = tmpStr + " " + string.Format("{0:X2}", SendBuff[indx]);
}
retCode = Card.SCardTransmit(hCard, ref pioSendRequest, ref SendBuff[0],
SendLen, ref pioSendRequest, ref RecvBuff[0], ref RecvLen);
if (retCode != Card.SCARD_S_SUCCESS)
{
return retCode;
}
else
{
try
{
tmpStr = "";
switch (reqType)
{
case 0:
for (indx = (RecvLen - 2); indx <= (RecvLen - 1); indx++)
{
tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
}
if ((tmpStr).Trim() != "90 00")
{
//MessageBox.Show("Return bytes are not acceptable.");
return -202;
}
break;
case 1:
for (indx = (RecvLen - 2); indx <= (RecvLen - 1); indx++)
{
tmpStr = tmpStr + string.Format("{0:X2}", RecvBuff[indx]);
}
if (tmpStr.Trim() != "90 00")
{
tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
}
else
{
tmpStr = "ATR : ";
for (indx = 0; indx <= (RecvLen - 3); indx++)
{
tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
}
}
break;
case 2:
for (indx = 0; indx <= (RecvLen - 1); indx++)
{
tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
}
break;
}
}
catch (IndexOutOfRangeException)
{
return -200;
}
}
return retCode;
}
Function authenticateBlock is as following
private bool authenticateBlock(String block)
{
ClearBuffers();
/*SendBuff[0] = 0xFF; // CLA
SendBuff[2] = 0x00; // P1: same for all source types
SendBuff[1] = 0x82; // INS: for stored key input
SendBuff[3] = 0x00; // P2 : Memory location; P2: for stored key input
SendBuff[4] = 0x05; // P3: for stored key input
SendBuff[5] = 0x01; // Byte 1: version number
SendBuff[6] = 0x00; // Byte 2
SendBuff[7] = (byte)int.Parse(block); // Byte 3: sectore no. for stored key input
SendBuff[8] = 0x60; // Byte 4 : Key A for stored key input
SendBuff[9] = (byte)int.Parse("1"); // Byte 5 : Session key for non-volatile memory
*/
SendBuff[0] = 0xD4;
SendBuff[1] = 0x4A;
SendBuff[2] = 0x01;
SendBuff[3] = 0x00;
SendBuff[4] = (byte) int.Parse(block);
SendBuff[5] = 0xFF;
SendBuff[6] = 0xFF;
SendBuff[7] = 0xFF;
SendBuff[8] = 0xFF;
SendBuff[9] = 0xFF;
SendBuff[10] = 0xFF;
/*SendLen = 0x0A;
RecvLen = 0x02;*/
SendLen = 4;
RecvLen = 255;
retCode = SendAPDUandDisplay(2);
if (retCode != Card.SCARD_S_SUCCESS)
{
//MessageBox.Show("FAIL Authentication!");
return false;
}
return true;
}
One strange thing to notice here is that whatever values I set in sendBuff this function always returns true value and the write data code "The very first code block" returns write success message
But after executing the write data code when I read that very block "5" in my case, there is nothing present there. My read block code returns an empty string and when I try to double check if data was written and my faulty code couldn't read I use an external software to verify that was the value added or not, that software also does not show the data that I wrote and got that write success message.
Ok following is the code I'm using to read block 5.
public string readBlock(String Block)
{
string tmpStr = "";
int indx;
if (authenticateBlock(Block))
{
ClearBuffers();
/*
SendBuff[0] = 0xFF; // CLA
SendBuff[1] = 0xB0;// INS
SendBuff[2] = 0x00;// P1
SendBuff[3] = (byte)int.Parse(Block);// P2 : Block No.
SendBuff[4] = (byte)int.Parse("16");// Le
*/
SendBuff[0] = 0xD4;
SendBuff[1] = 0x40;
SendBuff[2] = 0x01;
SendBuff[3] = 0x30;
SendBuff[4] = byte.Parse(Block.ToString(), System.Globalization.NumberStyles.HexNumber);
SendBuff[5] = 0xFF;
SendBuff[6] = 0xFF;
SendBuff[7] = 0xFF;
SendBuff[8] = 0xFF;
SendBuff[9] = 0xFF;
SendBuff[10] = 0xFF;
//SendLen = 5;
//RecvLen = SendBuff[4] + 2;
SendLen = 5;
RecvLen = 255;
retCode = SendAPDUandDisplay(2);
if (retCode == -200)
{
return "outofrangeexception";
}
if (retCode == -202)
{
return "BytesNotAcceptable";
}
if (retCode != Card.SCARD_S_SUCCESS)
{
return "FailRead";
}
// Display data in text format
for (indx = 0; indx <= RecvLen - 1; indx++)
{
tmpStr = tmpStr + Convert.ToChar(RecvBuff[indx]);
}
return (tmpStr);
}
else
{
return "FailAuthentication";
}
}
Please Note that the read block method is called after checking that is a reader connected connected, if so then I call the
readblock method and it returns an empty string
I've tried several values as you would see in comments but nothing seems to help, it's been 3 long days and I'm still stuck here.
Can someone please help me figure where I'm doing it wrong and what values should I send in order to authenticate the block?
Please do me a favour that if anyone gets to knows the problem in my code or want to correctify the values I'm setting in sendBuff[] then please quote them in C# code so I can use exactly the solution you want me to implement
Any sincere help would be highly regarded, thanks in advance.
I have only experimented with mifare 1k, using Arduino.
In this instance, after detecting the card, and retrieving the UUID, It needs to select the card before reading/writing. Are you doing this select card step?
As its a S50 1K Classic the 'bytemap' is different and the process cycle while ostensibly the same you need to check that its a S50 before continuing by getting the ATR/ATS and parsing it to retrieve the switch setting. With contact its ATR, contactless ATS but is technically the same thing. Under PCSC this is asking for the readerchangestate when asking the reader is a card present, done before sending an APDU. You can also get other settings at the same time.
For MFS50 you need to perform a 'S'elect then a 'L'ogin using the sector key then read that sectors first three of 4 blocks but ignore most of the first sector block - the keys are in the fourth block along with other control bytes. The 'UID' is returned on 'S'elect, success or fail on 'L'ogin, data or 'E'rror on reading the blocks in the sector. For 14443 sector 0 the first block is occupied by the 'manufacturing' data which depends on the construct can have 4, 7 or 12 bytes as the UID with from a data point of view embedded CRC and check bytes, so cannot be used as a UID - it is a data map. Lights, C, ultralights, EV1's have a different 'bytemap' and may or may not have 'L'ogins at card or sector. These are 14443 types, there is also a 15693 type which has just data in the sectors. Some Chinese 14443 have writable manufacturing blocks, but usually its a mix of static, settings and OTP (once bit set cannot unset, used for authentication and NFC verification of size!).
Classic 1K: ident UID: 66908648, requires sector key log in (A read, B
read/write)
Sector 0:
6690864838880400468f76594d100612
00000000000000000000000000000000
00000000000000000000000000000000
ffffffffffffff078069ffffffffffff
...
Sector 15:
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
ffffffffffffff0780bcffffffffffff
Mifare ultralight: UID 0489d802a44080, might require sector login but key held elsewhere.
Sector 0:
0489D8DD
02A44080
66480900
00010001
.
Sector 1:
00000000
00000000
00000000
000000EE
15693: UID D89A1F24500104E0
Sector 0:
50555955
Sector 1:
48485353
Sector 2:
59435300
Sector 3:
00000000
...
Sector 15:
00000000
So, get the ATR/ATS and work out what card you have, then deal with it accordingly. Oh, and use the belt, braces and a piece of string approach - after writing to a card read it again to compare the written to what is expected. 15693 require sector complete writes else nothing gets written in that sector.
Will that be Type 2 NFC/NDEF - there are other standards. Have cannibalized a Zebra printer to encode and print NTAG201's bullseyes on the fly.
I'm working on a project which required a dll file for another program written in c# to use (I'm not very familiar with the usage of C++/C#). After finishing the development(?) of the dll file, I started its testing process(run the function call from dll in C# for many times to ensure it works fine). I've asked the way to pass data from C++ to C# here, and the problem is, the program will stop without any error message(I've put try catch in my program) after calling it over 2 times.
I've seen a saying that if there's a memory corruption, the program will stop at a very normal line(e.g. std::cout << ...). I think my situation is similar to that saying...
And here's my code structure...
//dll.hpp
extern "C" LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat*& res, int& img_count, int& mat_type_size, double params[2]);
extern "C" LIB_API void clear_mem(cv::Mat* res);
//dll.cpp
LIB_API void inference(unsigned char* img_pointer, long data_len, cv::Mat*& res, int& img_count, int& mat_type_size, double params[2])
{
try
{
img_count = 8;
mat_type_size = sizeof(cv::Mat);
res = new cv::Mat[img_count];
cv::Mat& img1 = res[0];
cv::Mat& img2 = res[1];
cv::Mat& img3 = res[2];
cv::Mat& img4 = res[3];
cv::Mat& img5 = res[4];
cv::Mat& img6 = res[5];
cv::Mat& img7 = res[6];
cv::Mat& img8 = res[7];
//some process to update img1~img8
std::cout << "res->" << res << std::endl;
std::cout << "===== finish inference process ===== >> " << (std::clock() - t_inf1) / (double)CLOCKS_PER_SEC << " s" << std::endl;
}
catch (const std::runtime_error& re)
{
std::cerr << "*** Runtime error: " << re.what() << std::endl;
return;
}
catch (const std::exception& ex)
{
std::cerr << "*** Error occurred: " << ex.what() << std::endl;
return;
}
catch (...)
{
std::cerr << "*** Unknown failure occurred... Possible memory corruption" << std::endl;
return;
}
}
LIB_API void clear_mem(cv::Mat* res)
{
try
{
std::cout << ">> In 'clear_mem'...." << std::endl;
std::cout << "res in clear_mem->" << res << std::endl;
delete[] res;
std::cout << ">> finish deleting res...." << std::endl;
}
catch (const std::runtime_error& re)
{
std::cerr << "*** Runtime error: " << re.what() << std::endl;
return;
}
catch (const std::exception& ex)
{
std::cerr << "*** Error occurred: " << ex.what() << std::endl;
return;
}
catch (...)
{
std::cerr << "*** Unknown failure occurred... Possible memory corruption" << std::endl;
return;
}
}
//test.cs
namespace Test_Unet_Console
{
class Program
{
[DllImport(#"D:\Coco\Code\C_plus_plus\unet_cpp_dll\x64\Release\unet_cpp_dll.dll")]
private static extern void inference(byte[] img, long data_len, out IntPtr images, ref int img_count, out int mat_type_size,
[In, MarshalAs(UnmanagedType.LPArray, SizeConst = 3)]double[] param);
[DllImport(#"D:\Coco\Code\C_plus_plus\unet_cpp_dll\x64\Release\unet_cpp_dll.dll")]
private static extern void init_inference();
[DllImport(#"D:\Coco\Code\C_plus_plus\unet_cpp_dll\x64\Release\unet_cpp_dll.dll")]
private static extern void clear_mem(IntPtr images);
static void Cppdll_inf(Bitmap image, out List<Mat> output_pic, double[] param, out IntPtr imgPtrs)
{
Console.WriteLine("before fmt");
ImageFormat fmt = new ImageFormat(image.RawFormat.Guid);
Console.WriteLine("before imageCodecInfo");
var imageCodecInfo = ImageCodecInfo.GetImageEncoders().FirstOrDefault(codec => codec.FormatID == image.RawFormat.Guid);
//this is for situations, where the image is not read from disk, and is stored in the memory(e.g. image comes from a camera or snapshot)
if (imageCodecInfo == null)
{
fmt = ImageFormat.Jpeg;
Console.WriteLine("C# - imageCodecInfo is null");
}
Console.WriteLine("before MemoryStream");
using (MemoryStream ms = new MemoryStream())
{
try
{
Console.WriteLine("C# - before image.Save");
image.Save(ms, fmt);
Console.WriteLine("C# - finish image.Save");
byte[] image_byte_array = ms.ToArray();
Console.WriteLine("C# - finish reading pic");
int imgCount = 0;
inference(image_byte_array, ms.Length, out var imgPtrs, ref imgCount, out var matTypeSize, param);
output_pic = new List<Mat>();
for (int i = 0; i < imgCount; i++)
{
output_pic.Add(new Mat(IntPtr.Add(imgPtrs, i * matTypeSize)));
}
Console.WriteLine("finish getting value from imgPtrs.....");
}
catch (Exception ex)
{
throw ex;
}
}
}
// I've tried to add this in my code, but it will cause some error(I didn't record it...)
static void clear()
{
Console.WriteLine("start gc collect");
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("finish gc collect in clear");
}
static void show_result(List<Mat> pic_list)
{
for (int i = 0; i < pic_list.Count; i++)
{
Console.WriteLine(i);
Cv2.ImShow("test", pic_list[i]);
Cv2.WaitKey(500);
}
Cv2.DestroyAllWindows();
Console.WriteLine("finish showing pic");
}
static void Main()
{
Bitmap image1 = new Bitmap("C:\\Users\\Coco\\Desktop\\(3).png");
double[] param = { 0.7, 20 }; //{ VI_threshold, area_threshold }
Console.WriteLine(">>> Initializing from C# =======");
init_inference(); // >> initialization of models
Console.WriteLine(">>> Finish initializing from C# ======");
for (int i = 0; i < 3; i++)
{
Console.WriteLine(i);
List<Mat> result;
IntPtr imgPtrs;
Cppdll_inf(image1, out result, param, out imgPtrs); // inference of picture
Console.WriteLine(">>> Finish image1 C# ======");
show_result(result); // showing pic to check if result is correct
clear_mem(imgPtrs); // clean the memory send from dll(?
result.Clear();
Console.WriteLine(">>> Finish result.Clear() ======");
Console.WriteLine("================================");
}
}
}
}
I've tried not to return the result from dll to C#, it can run about 100 times(the maximum I've tested). So I think the problem might be the process when passing data from dll to C#...
By the way, here's my memory usage when running the program...
I've searched many info on the internet, but I still have no idea how to fix this problem, so any advise or help is really appreciated!
(Thanks in advance for finish reading my messy question description! )
It's been quite long not recording the answer of this question...
There are two ways to handle this(in my experience):
Adding a function with doing delete[] cv::Mat* in your cpp, call this function every time after you received a Mat* from dll
Using IDisposable Class in C#'s calling with DLL function
(Imagine like it kills itself after every run with this syntax)
(This is Chinese material, but I think it's pretty clear with its
example code)
I'm playing around in C++ with WinHTTP.
I made an API in C# which I'm trying to call from C++, for this I'm using WinHTTP. The API is hosted on a local server with SSL active, the root certificate is also trusted.
I made a sample application to go through the following in order:
WinHttpOpen with WINHTTP_FLAG_ASYNC
WinHttpConnect with INTERNET_DEFAULT_HTTPS_PORT
WinHttpOpenRequest with WINHTTP_FLAG_SECURE
WinHttpSetStatusCallback to set callback function
WinHttpSendRequest
WinHttpReceiveResponse
WinHttpQueryDataAvailable
WinHttpReadData
WinHttpCloseHandle on all open handles
Here is the full source:
#include <windows.h>
#include <winhttp.h>
#include <iostream>
#pragma comment(lib, "winhttp")
LPWSTR GetHeaders(HINTERNET hHttp)
{
LPVOID lpOutBuffer = NULL;
DWORD dwSize = 0;
retry:
if (!WinHttpQueryHeaders(hHttp, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, (LPVOID)lpOutBuffer, &dwSize, WINHTTP_NO_HEADER_INDEX))
{
if (GetLastError() == ERROR_WINHTTP_HEADER_NOT_FOUND)
{
std::cout << "ERROR_HTTP_HEADER_NOT_FOUND" << std::endl;
return NULL;
}
else
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
//std::cout << "ERROR_INSUFFICIENT_BUFFER" << std::endl;
lpOutBuffer = new wchar_t[dwSize];
goto retry;
}
else
{
std::cout << GetLastError() << std::endl;
if (lpOutBuffer)
{
delete[] lpOutBuffer;
}
return NULL;
}
}
}
return (LPWSTR)lpOutBuffer;
}
VOID CALLBACK HookCallback(
HINTERNET hInternet,
DWORD_PTR dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength)
{
std::cout << "Status: " << dwInternetStatus << std::endl;
if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR)
{
WINHTTP_ASYNC_RESULT *pAR = (WINHTTP_ASYNC_RESULT*)lpvStatusInformation;
std::cout << "Error: " << pAR->dwError << std::endl;
}
else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_READ_COMPLETE)
{
char *buffer = new char[dwStatusInformationLength+1];
memcpy(buffer, lpvStatusInformation, dwStatusInformationLength);
buffer[dwStatusInformationLength] = '\0';
std::cout << buffer << std::endl;
}
else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE)
{
BOOL result = WinHttpQueryDataAvailable(hInternet, NULL);
if (!result)
{
std::cout << GetLastError() << std::endl;
}
LPWSTR pwszHeaders = GetHeaders(hInternet);
char* headers = new char[wcslen(pwszHeaders) + 1];
wsprintfA(headers, "%S", pwszHeaders);
std::cout << headers << std::endl;
}
else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE)
{
DWORD *bytesAvailable = (DWORD *)lpvStatusInformation;
std::cout << *bytesAvailable << std::endl;
LPVOID buffer = (LPVOID)new char *[*bytesAvailable];
WinHttpReadData(hInternet, buffer, *bytesAvailable, NULL);
}
};
int main()
{
BOOL async = TRUE;
BOOL bResults = FALSE;
HINTERNET hSession = NULL,
hConnect = NULL,
hRequest = NULL;
// Use WinHttpOpen to obtain a session handle.
hSession = WinHttpOpen(L"WinHTTP/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
async ? WINHTTP_FLAG_ASYNC : 0);
// Specify an HTTP server.
if (hSession)
hConnect = WinHttpConnect(hSession, L"api.local", INTERNET_DEFAULT_HTTPS_PORT, 0);
// Create an HTTP Request handle.
if (hConnect)
hRequest = WinHttpOpenRequest(hConnect, L"GET",
L"/config",
NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
WINHTTP_FLAG_SECURE);
if (async) {
WinHttpSetStatusCallback(hRequest, (WINHTTP_STATUS_CALLBACK)&HookCallback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0);
}
// Send a Request.
if (hRequest)
bResults = WinHttpSendRequest(hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS,
0, WINHTTP_NO_REQUEST_DATA, 0,
0, 0);
// Report any errors.
if (!bResults)
std::cout << GetLastError() << std::endl;
bResults = WinHttpReceiveResponse(hRequest, NULL);
// Report any errors.
if (!bResults)
std::cout << GetLastError() << std::endl;
if (!async)
{
DWORD bytesAvailable;
bResults = WinHttpQueryDataAvailable(hRequest, &bytesAvailable);
// Report any errors.
if (!bResults)
std::cout << GetLastError() << std::endl;
std::cout << bytesAvailable << std::endl;
LPVOID buffer = (LPVOID)new char *[bytesAvailable];
bResults = WinHttpReadData(hRequest, buffer, bytesAvailable, NULL);
// Report any errors.
if (!bResults)
std::cout << GetLastError() << std::endl;
std::cout << (char *)buffer << std::endl;
// Close any open handles.
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);
}
while (true)
{
Sleep(1000);
}
}
Whenever I call WinHttpSendRequest, my API is hit and a response is sent back. However, right before it should call the callback with WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, it is triggered with an WINHTTP_CALLBACK_STATUS_REQUEST_ERROR pointing out a ERROR_INTERNET_OPERATION_CANCELLED. The error description says this usually occurs if the handle is closed prematurely. If I hook the CloseHandle with some assembly magic, I can see its indeed called right before the error.
Question: What might be potential causes that it's generating this error at this particular point in the chain? How can I get more information that this when my API seemingly returns its data just fine.
I would have to add that the API is a "proxy" to another API. As such it does it's own WebRequest internally for every request that comes in, then writes that result to the response. The weird thing is however, if I cache the result of the internal WebRequest and return that immediately the next time I try it, without ever initiating the WebRequest, the C++ proceeds as expected. As soon as I call the internal WebRequest, but still return the cached value (even though it's identical to what I'm requesting), C++ fails.
That said, I have excluded the possibility of timeouts being the issue, because the response happens with or without caching within 30ms.
Also, when I disable ASYNC, I get to read my response just fine regardless of what happens on the API side.
I'm really pulling my hairs over this!
I have two programs written in c++ and c#.I want to establish a two way communication using named-pipe between them. The C# client program can be connected to the named-pipe created by c++ server program.But nothing received in both ends.
Here is the c++ part (Server):
#include <iostream>
#include <windows.h>
#include <stdlib.h>
#define UNICODE
using namespace std;
HANDLE hnamedPipe = INVALID_HANDLE_VALUE;
BOOL Finished =false;
HANDLE hThread = NULL;
unsigned long __stdcall CS_RcvThr(void * pParam) ;
int main(int argc, char **argv)
{
hnamedPipe = CreateNamedPipe(
"\\\\.\\pipe\\vikeyP",
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE|
PIPE_READMODE_MESSAGE|
PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
1024,
1024,
NMPWAIT_USE_DEFAULT_WAIT,
NULL);
if(hnamedPipe == INVALID_HANDLE_VALUE)
{
cout << "Failed" << endl;
}
while (true)
{
cout<< "Waiting for client"<< endl;
if(!ConnectNamedPipe(hnamedPipe,NULL))
{
if(ERROR_PIPE_CONNECTED != GetLastError())
{
cout << "FAIL"<< endl;
}
}
else
{
cout<<"Connected!"<<endl;
hThread = CreateThread( NULL, 0, &CS_RcvThr, NULL, 0, NULL);
if(hThread) cout<<"read thread created"<<endl; else cout<<"cant crat rd thed\n";
break;
}
}
while(1)
{
cout<<"lst loop"<<endl;
//Send over the message
char chResponse[] = "hello\n";
DWORD cbResponse,cbWritten;
cbResponse = sizeof(chResponse);
if (!WriteFile(
hnamedPipe,
chResponse,
cbResponse,
&cbWritten,
NULL))
{
wprintf(L"failiure w/err 0x%08lx\n",GetLastError);
}
cout<<"Sent bytes :)" << endl;
Sleep(10);
}
}
unsigned long __stdcall CS_RcvThr(void * pParam) {
BOOL fSuccess;
char chBuf[100];
DWORD dwBytesToWrite = (DWORD)strlen(chBuf);
DWORD cbRead;
int i;
while (1)
{
fSuccess =ReadFile( hnamedPipe,chBuf,dwBytesToWrite,&cbRead, NULL);
if (fSuccess)
{
printf("C++ App: Received %d Bytes : ",cbRead);
for(i=0;i<cbRead;i++)
printf("%c",chBuf[i]);
printf("\n");
}
if (! fSuccess && GetLastError() != ERROR_MORE_DATA)
{
printf("Can't Read\n");
if(Finished)
break;
}
}
}
Here is the C# part (Client):
private Thread vikeyClientThread;
public void ThreadStartClient()
{
Console.WriteLine("Thread client started ID ={0} name = {1} " ,
Thread.CurrentThread.ManagedThreadId,Thread.CurrentThread.Name);
using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".", "vikeyP"))
{
// The connect function will indefinately wait for the pipe to become available
// If that is not acceptable specify a maximum waiting time (in ms)
Console.WriteLine("Connecting to ViKEY server...");
pipeStream.Connect();
Console.WriteLine("Connected :)");
//Write from client to server
StreamWriter sw = new StreamWriter(pipeStream);
while (true)
{
//Read server reply
StreamReader sr = new StreamReader(pipeStream);
string temp = "";
sw.WriteLine(System.DateTime.Now);
byte[] c = new byte[200];
temp = sr.ReadLine();
pipeStream.Read(c, 0, c.Length);
Console.WriteLine("RX =:{0}", Encoding.UTF8.GetString(c, 0, c.Length));
Thread.Sleep(500);
}
}
Console.WriteLine("Vikey pipe Closed");
Console.WriteLine("Thread with ID ={0} name = {1} is closed.",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.Name);
}
You've set the server side up as a message type vs byte type. If you want to read an arbitrary number of bytes, you'll need to use a byte type named pipe.
You're wrapping the stream in 2 objects one StreamReader and one StreamWriter. Don't do that, just use a Stream. You're trying to read by line, don't do that either. Instead send the number of bytes to read followed by the bytes. On the client side you'll read the byte count then create a buffer big enough then read. If it's text data you then would use an encoder (probably ASCII) to translate it back into a C# string.
Your while(true) should instead detect when the server has closed the pipe.
You should probably look into using an asynchronous named pipe.
Under Windows, performance counters have different names, depending on the operating system language. For example, on an English Windows version, there is the performance counter \Processor(_Total)\% Processor Time. The same counter is called \Prozessor(_Total)\Prozessorzeit (%) on a German Windows version.
Is there any way to retrieve the performance counter value in a language-independent way (using C++ or C#)? Or is there an alternative to get the processor load of the whole computer without performance counters?
Each PerfMon counter is given a unique (per machine) integer ID to identify the PerfMon counter (however in the case of the standard counters this id is guaranteed to stay the same).
The information linking PerfMon counters id's to both their US English name and their Localized name is stored in the registry in the following key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib
Once you have used the registry to obtain the PerfMon counter name (which you can embed in your app as a constant for standard counters) you can use the PdhLookupPerfNameByIndex function to look up the localized name for the given counter ID.
See Using PDH APIs correctly in a localized language (Microsoft KB) for more details.
You might also find Finding perfmon counter id via winreg (StackOverflow) somewhat relevant.
Add this
using System.Runtime.InteropServices;
using Microsoft.Win32;
In your class import the DLL(my classed is named Program)
[DllImport("pdh.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern UInt32 PdhLookupPerfIndexByName(string szMachineName, string szNameBuffer, ref uint pdwIndex);
Send the name of the counter you want in your OS language and it return the english NAME
public string GetEnglishName(string name)
{
string buffer2 = name;
UInt32 iRet2 = new UInt32();
iRet3 = PdhLookupPerfIndexByName(null, buffer2, ref iRet2);
//Console.WriteLine(iRet2.ToString());
RegistryKey pRegKey = Registry.LocalMachine;
pRegKey = pRegKey.OpenSubKey(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009");
string[] after;
after = (string[])pRegKey.GetValue("Counter");
string value = iRet2.ToString();
int pos = Array.IndexOf(after, value);
return after[pos + 1];
}
Here is how to use it
Program m = new Program();
string result = m.GetEnglishName("Mémoire");
Console.WriteLine(result);
There are the WinAPI functions, QueryHighPerformanceCounter and QueryHighPerformanceFrequency.
Have you tried using the Pdh helper functions and the PdhAddEnglishCounter function?
My version of Full Path localization using PdhGetCounterInfoW.
std::wstring GetLocalizedCounterFullPath(const std::wstring& englishCounterName)
{
std::wstring localizedCounterName;
PDH_HQUERY queryHandle = nullptr;
try
{
std::ostringstream errorText;
auto status = PdhOpenQueryW(nullptr, 0, &queryHandle);
if (status != ERROR_SUCCESS)
{
errorText << "PdhOpenQueryW failed with " << std::hex << status;
throw std::runtime_error(errorText.str());
}
PDH_HCOUNTER counterHandle;
status = PdhAddEnglishCounterW(queryHandle, englishCounterName.data(), 0, &counterHandle);
if (status != ERROR_SUCCESS)
{
errorText << "PdhAddEnglishCounterW failed with " << std::hex << status;
throw std::runtime_error(errorText.str());
}
DWORD bufferSize = 0;
std::vector<unsigned char> counterInfo;
do
{
if (bufferSize != 0)
{
counterInfo.resize(bufferSize);
}
status = PdhGetCounterInfoW(counterHandle,
TRUE,
&bufferSize,
reinterpret_cast<PPDH_COUNTER_INFO_W>(counterInfo.data()));
}
while(static_cast<unsigned>(status) == PDH_MORE_DATA);
if (status != ERROR_SUCCESS)
{
errorText << "PdhGetCounterInfoW failed with " << std::hex << status;
throw std::runtime_error(errorText.str());
}
localizedCounterName = reinterpret_cast<PPDH_COUNTER_INFO_W>(counterInfo.data())->szFullPath;
status = PdhCloseQuery(queryHandle);
if (status != ERROR_SUCCESS)
{
errorText << "PdhCloseQuery failed with " << std::hex << status;
throw std::runtime_error(errorText.str());
}
}
catch (const std::exception& e)
{
std::wcout << e.what() << "\n";
PdhCloseQuery(queryHandle);
}
return localizedCounterName;
}