I want to read the Device Descriptor of a USB Serial port (not just the VID/PID -- mainly so I can get the bcdDevice, iProduct, and iManufacturer) so I can know if the USB serial port is capable of what I want it to do (we have a couple of modems in the field, and one of them is much more reliable at half-duplex).
I don't want to use libusbdotnet because I don't want my users to have to figure out ahead of time what to filter for to get the ports I'm interested in.
I see how to do it with C; looking on the USBView application, we're going to need to use the SetupAPI to enumerate the controllers (SetupDiEnumDeviceInfo), then the hubs, and then query the hubs with IOCTL to get what is on each port.
I think I have to do this this way because I can't figure out a mapping from COM13 -> \.\USB#ROOT_HUB30#4&2a9d917a&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8}.. is there a better/easier way to get the filename I need for the ioctl, rather than go through them all and filter them?
I've also tried making the usbview application into a C DLL and tried to make a wrapper around that, but I can't get my homegrown DLL to compile properly...
I tried adding this to the enum.c from usbview just as a starting point
typedef struct
{
PSTR Name;
PSTR path;
int port;
} USBDevice;
USBDevice USBDevices[255] = { 0 };
int ndevices;
extern __declspec(dllexport) int GetDevices(PSTR filter)
{
//Enumerate it all and filter for "COM%"
//Store the data in the USBDevice array.
}
extern __declspec(dllexport) PSTR DeviceName(int i)
{
return USBDevices[i].Name;
}
extern __declspec(dllexport) PSTR DevicePath(int i)
{
return USBDevices[i].path;
}
extern __declspec(dllexport) void FreeList(void)
{
char* foo;
int i;
for (i = 0; i<ndevices; i++)
{
free(USBDevices[i].Name);
free(USBDevices[i].path);
}
ndevices = 0;
}
extern __declspec(dllexport) PUSB_COMMON_DESCRIPTOR GetDevuceInfo(PSTR path, int port)
{
//I didn't want to figure out how to marshal this.
}
extern __declspec(dllexport) PSTR GetDevuceInfoValue(PSTR path,int port, int selector)
{
//Open Hub, send IOCTL to it, get data, filter that, return the interesting bit.
}
But I can't load the resulting DLL into my C# project (it crashes when run - even before the window appears). Looking at the DLL, it appears that it is garbage (walking the DLL causes the walker to crash). so... I'm not sure what I'm doing wrong there, so I was going to translate the enum.c into C# pinvoke-style and roll with that, but it seems like a lot of work, especially if there's a simpler way to get the COM14 -> \.\USB#ROOT_HUB30#4&2a9d917a&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8} [Port1] link...
So... Is there a lot of work ahead of me, or is there a simpler way to get the linkage (I've tried from the registry, but it doesn't seem to have the mapping).
It looks like that is the approved way (EnumerateDevices, CM_GetParent, Munge it to a valid path, then ask the Hub to get the data). I've made a C program to do the dirty work and it works like a charm. I think I'll wrap it in a DLL and post it to CodeProject.
Warning though... even if you do a DeviceIOControl, windows does not re-query your device. It only queries your device at Plug-In.
But at least you can find the "Manufacturer String" so you can tell your USB port from all the similar ones that are plugged in. Then again, you can get that from SetupAPI, so... I don't know what it really buys you.
Related
I have a third-party Windows app that supports a C plugin/driver (see spec below) to be a passive data receiver once initialized. Now I found a C# package that does the data gathering and Pushing. The only missing piece here is a bridge between the C dll and the C# dll.
The data flow is like this: When the app is launched, it loads and calls the C dll which contains several exported functions including an init function. In this init function, I like to establish a call to the C# to setup some network connection and prepare for incoming data. Once that done, according to the driver spec, the C# dll will gather data and stream it to the receiving driver. To accommodate this, I have two thoughts (you may come up with more):
1) to wrap the C# with C++/Cli and call the expose C#-like methods from the driver. Declare an object with gcroot, then instantiate it with gcnew then call the method(s). Tried that and I got stackoverflowexception. I am new to this mix-mode programming and can't figure out why that happened.
2) to wrap the C dll in some way (like using C++/Cli to import the C functions then interact with the C# data streamer) to be callable from C#. What is the best way to do this?
I have done some reading and it seems C++/Cli is the easy route but I am open to other not so complicated options as well. What are the project settings I have to add/modify to make it work should I choose C++/Cli or any way you suggest?
As I am new to tackle this kind of problem, any samples or related links are helpful. So I appreciate if you could demonstrate how things work one way or the other.
Here is piece of the skeletal C# dll referenced (other methods are omitted):
public class Client
{
public Client()
{
//reset();
}
public void test()
{
Console.WriteLine("test");
}
/// <summary>
/// Connect connects the Client to the server.
/// Address string has the form host:port.
/// </summary>
public void Connect(string address)
{
Disconnect();
// validate address
int sep = address.IndexOf(':');
if (sep < 0)
{
throw new ArgumentException("Invalid network address");
}
// set host and port
host = address.Substring(0, sep);
toPort(ref port, address.Substring(sep + 1));
// connect
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(host, port);
rw.Set(socket, CLIENT_DEFAULT_BUFFER_SIZE);
}
/// <summary>
/// Disconnect disconnects the Client from the server.
/// </summary>
public void Disconnect()
{
backlog.Clear();
try
{
if (Connected)
{
write("close");
}
}
catch (Exception e)
{
}
//reset();
//rw.Close();
}
}
Here is the skeletal spec of the C dll:
//APIs
#ifdef __cplusplus
extern "C"{
#endif
#define DLL __declspec(dllexport)
////////////////////////////////////////////////////////////////////////////
// Initialization: do some prep for receiving data from C#
// params:
// hWnd handle
// Msg message
// nWorkMode work mode
// return:
// 1 true
// -1 false
DLL int Init(HWND hWnd, UINT Msg, int nWorkMode);
// Quitting and closing
// Param:
// hWnd same handle as Init
// return:
// 1 true
// -1 fals
DLL int Quit(HWND hWnd);
#ifdef __cplusplus
}
#endif
To call C functions in an external DLL, you can use C++/CLI as you've already mentioned, or use P/Invoke (Platform Invoke).
With P/Invoke, you define a static extern method in your C# assembly then decorate it with appropriate attributes. After that, you can call the method as per any other C# method, and the .NET Framework plumbing will handle the loading of the DLL and marshalling the method parameters back and forth.
For example:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SystemParametersInfo(uint uiAction, uint uiParam, uint pvParam, uint fWinIni);
The method is declared with the static and extern keywords, and decorated with the DllImport attribute which identifies the DLL that contains the C function. That's generally the minimum you will need.
The hardest part is determining which C types map to which .NET types for the method parameters and return type: this is a bit of a black art! If you visit pinvoke.net you can see how the Windows APIs have been translated, which may help. Searching the web for "pinvoke" should also turn up a number of useful resources.
At the moment, I can get a list of running processes with a main window using System.Diagnostics.Process.GetProcesses() and executing a simple LINQ query.
Then, I can import user32.dll and the SetWindowPos function and I manipulate other processes' window parameters.
Ok, it works. Now I'd like to select a window of a process, let's say calc.exe, by clicking it. In other words, I'd like to obtain a Process object (and then the MainWindowHandle) with a hook that catches the process name when I click on its window.
How can I achieve this?
I don't know how this is done in C#, but you have also tagged this question WinAPI so I can help there. In WinAPI, it can be done like so:
#include <stdio.h>
#include <Windows.h>
#include <Psapi.h>
#pragma comment(lib, "Psapi.lib")
int main(void)
{
/* Hacky loop for proof of concept */
while(TRUE) {
Sleep(100);
if(GetAsyncKeyState(VK_F12)) {
break;
}
if(GetAsyncKeyState(VK_LBUTTON)) {
HWND hwndPt;
POINT pt;
if(!GetCursorPos(&pt)) {
wprintf(L"GetCursorPos failed with %d\n", GetLastError());
break;
}
if((hwndPt = WindowFromPoint(pt)) != NULL) {
DWORD dwPID;
HANDLE hProcess;
GetWindowThreadProcessId(hwndPt, &dwPID);
hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwPID);
if(hProcess == NULL) {
wprintf(L"OpenProcess failed with error: %d\n", GetLastError());
} else {
wchar_t lpFileName[MAX_PATH];
DWORD dwSize = _countof(lpFileName);
QueryFullProcessImageName(hProcess, 0, lpFileName, &dwSize);
wprintf(L"%s\n", lpFileName);
CloseHandle(hProcess);
}
}
}
}
return EXIT_SUCCESS;
}
Example result:
In this case, I am simply polling to get the mouse click. A more proper way would be to use some sort of windows hook.
As Mike Kwan said, you'll be better of writing a hook though both approaches have their own drawbacks, but bjarneds already did a good work on this. Have a look # DotNET Object Spy. It's written in C# and will serve for your needs and more.
You should also note that using hooks are becoming redundant by the day. Depending on what you want to do, other winapis like GetForegroundWindow might serve better.
I'm exploring the idea of building a bridge between a DLL plugin for a 3rd party app and a C# app. I'm writing both the plugin DLL and the C# application. The plugin will be loaded into the 3rd party app and then I want to use call the plugin from C# to indirectly get data from the 3rd party app.
I am able to successfully call an exported function from the DLL from C#. For example:
C++ DLL:
extern "C" __declspec(dllexport) char * HelloFromDll()
{
char *result;
result = "Hello from my DLL";
return result;
}
C#:
using System.Runtime.InteropServices;
[DllImport(#"MyDll.dll")]
private static extern string HelloFromDll();
I can then call this DLL function from C# and display the string in the UI. However, as soon as I create an export function that calls a function from my 3rd party app, I get an AccessViolationException. For example,
extern "C" __declspec(dllexport) char * GetData()
{
char *result;
result = 3rdPartyLibrary::SomeFunction();
return result;
}
Through some testing, the error seems to occur as soon as I make a call to a 3rd party function. How can I fix this?
This function is very difficult to use in a C program as well. Returning strings from functions is a poorly supported scenario. There's a memory management problem, it isn't clear who owns the string. In most cases the caller is expected to take ownership of the string and free it after using it. That's not going to work out well for your function, the program will crash since you returned a string literal.
The .NET pinvoke marshaller needs to solve this problem as well. With the extra problem that it cannot use the allocator that's used by the C code. It is going to call CoTaskMemFree (the COM allocator). That causes an undiagnosable memory leak on XP, a crash on Vista and Win7.
Just don't write C code like this. Always let the caller pass the buffer for the string. Now there's no guessing who owns the memory. Like this:
extern "C" __declspec(dllexport) void HelloFromDll(char* buffer, int bufferSize)
{
strcpy_s(result, bufferSize, "Hello from my DLL");
}
With your C# code like this:
[DllImport("foo.dll", CharSet = CharSet.Ansi)]
private static extern void HelloFromDll(StringBuilder buffer, int bufferSize);
...
var sb = new StringBuilder(666);
HelloFromDll(sb, sb.Capacity);
string result = sb.ToString();
From your question it seems that this is the scenario:
ProcessA (3rd party App) --> loads X.DLL --> initializes the plugin --> does other stuff.
ProcessB (Your C# App) --> loads X.DLL --> calls GetData();
Does X.DLL loaded in ProcessA have any mechanism to talk to X.DLL loaded in ProcessB?
if not then this approach is flawed. Your code probbably crashes because "3rdPartyLibrary" class hasn't been initialised in your C# app as it is completely different copy of the DLL.
For you to extract this data you need a query interface defined by X.DLL which can talk across processes, maybe sockets?
Then ProcessB talks to this interface and extracts the data. if using sockets, then your X.DLL would implement both server and client code, where your GetData() would use this mechanism (maybe sockets) and query the data and return it.
So : X.DLL in ProcessA should act like a server.
And: X.DLL (or write a Y.DLL) in ProcessB should act like a client and get this information from ProcessA.
btw, if the query is only needed to be done once, just hard code this is in X.DLL and dump to disk, and then explore at your convinience :-)
Generally, a returned char* needs to be returned as an IntPtr:
[DllImport(#"MyDll.dll")]
private static IntPtr HelloFromDll();
Then, you'll need to convert that IntPtr into a string:
string retVal=Marshal.PtrToStringAnsi(HelloFromDll());
Strings are a bit difficult in P/Invoke. My general rule of thumb is:
Input char* parameter = c# string
Return char * = IntPtr (use PtrToStringAnsi)
Output char* parameter = c# StringBuilder - and be sure to pre-allocate it large enough
before (ie = new StringBuilder(size)) calling the function.
I need a way to determine internet availability programmatically.
At now i use Ping to constantly ping some internet site.
But it seems Windows 7 though determines internet availability in some other way.
If computer is online there is earth icon on the network interface icon in the System Tray.
The question is: is there any standard Win32 way to check online status, win event or something, and if so, how to use it from C#?
I believe something like this would work although your question appears to be a duplicate:
using System;
using System.Runtime;
using System.Runtime.InteropServices;
public class InternetCS
{
//Creating the extern function...
[DllImport("wininet.dll")]
private extern static bool InternetGetConnectedState( out int Description, int ReservedValue );
//Creating a function that uses the API function...
public static bool IsConnectedToInternet( )
{
int Desc ;
return InternetGetConnectedState( out Desc, 0 ) ;
}
}
forund here:
check whether Internet connection is available with C#
'Connected to the Internet' doesn't have any actual meaning except in the case of a modem. The beat way to test whether any resource is available is to use it. You have to cope with failures at that point anyway, no need to code everything twice.
I have a service that runs and I'd like to receive notification when:
a) the network is connected.
b) when a user logs in to the machine.
How can I do this? (C# .NET 2.0)
//using Microsoft.Win32;
//using System.Net.NetworkInformation;
public class SessionChanges
{
public SessionChanges()
{
NetworkChange.NetworkAvailabilityChanged +=
new NetworkAvailabilityChangedEventHandler(NetworkChange_NetworkAvailabilityChanged);
SystemEvents.SessionSwitch += new SessionSwitchEventHandler(SystemEvents_SessionSwitch);
}
void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
{
if (e.Reason == SessionSwitchReason.SessionLogon)
{
//user logged in
}
}
void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
{
if (e.IsAvailable)
{
//a network is available
}
}
}
To be notified when any user logs in to the machine, call WTSRegisterSessionNotification, passing NOTIFY_FOR_ALL_SESSIONS, and listen for the WM_WTSSESSION_CHANGE message in the message loop.
In the message, cast the wParam to the Microsoft.Win32.SessionSwitchReason enum to find out what happened, and pass the lParam to WTSQuerySessionInformation to find the username.
[DllImport("Wtsapi32.dll", CharSet=.CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool WTSRegisterSessionNotification(IntPtr hWnd, int dwFlags);
//Pass this value in dwFlags
public const int NOTIFY_FOR_ALL_SESSIONS = 0x1;
//Listen for this message
public const int WM_WTSSESSION_CHANGE = 0x02B1;
//Call this method before exiting your program
[DllImport("Wtsapi32.dll", CharSet=.CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool WTSUnRegisterSessionNotification(IntPtr hWnd);
To find out when the network is connected, add a handler for the NetworkAvailabilityChanged in the System.Net.NetworkInformation.NetworkChange class.
One simple way to find out when a user logs in is to make a separate exe and put it into common startup.
It would be executed whenever any user logs in, and could then communicate with your service and give the username.
If you want your service to interact with the user's desktop, this is (I believe) the only way to do it. If you don't however, this might not be a good idea.
Remember that it is possible for multiple users to be logged in at once, especially through Remote Desktop or Terminal Services (On Windows Server)
You can't do b) with .NET 2.0. For XP and before, you need to create a GINA replacement, which is an unmanaged DLL which exports a specified set of functions.
In Vista, there is a different COM-based model which you have to use which you might be able to use .NET 2.0 for, but I wouldn't be surprised if you can't.
What I would do is have your GINA replacement (or the equivalent on Vista) send a signal of some kind to your service.
For a) these two links should be able to help:
Network Awareness in Windows XP
http://msdn.microsoft.com/en-us/library/ms700657(VS.85).aspx
Network Awareness on Windows Vista
http://msdn.microsoft.com/en-us/library/ms697388(VS.85).aspx
You could write a WinLogin Notification package. However, I am not sure if it is possible to do in C#. (I have no reason to believe it isn't, but I have only ever done it in C++ and unmanaged code.)
Here is a CodeProject link
I don't know how to listen for when the network connects.
SENS can do this.
http://msdn.microsoft.com/en-us/magazine/cc301850.aspx
Check for both ISensNetwork and ISensLogon.
Adapt this for C# and you're done.