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.
Related
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.
I've been working on an application that requires monitoring thread specific mouse activity (WH_MOUSE) on another process and encountered something very curious.
After finding out that this is not possible via exclusively managed code if I don't want to use WH_MOUSE_LL and that I'd need a native DLL export to inject itself in the target process, I set out and created it in C++ according to what scattered documentation I could find on the subject and then tried using it to hook into Notepad.
Although according to GetLastWin32Error the injection succeeded, I was not getting notified of mouse events. After nearly giving up and going for the low level global hook option, I re-read the "Remarks" section of this article which made me suspect that the problem may be because of the "bitness" of my code vs notepad:
A 32-bit DLL cannot be injected into a 64-bit process, and a 64-bit
DLL cannot be injected into a 32-bit process. If an application
requires the use of hooks in other processes, it is required that a
32-bit application call SetWindowsHookEx to inject a 32-bit DLL into
32-bit processes, and a 64-bit application call SetWindowsHookEx to
inject a 64-bit DLL into 64-bit processes.
However, both my native DLL and managed application were compiled as x64, and I was trying to hook into the 64-bit version of notepad, so it should've worked fine, but I took a shot in the dark anyway and went into the SysWOW64 folder and opened the 32-bit Notepad from there, tried hooking in again and this time the hook worked beautifully!
Curiously, I then recompiled both my native DLL and managed app as x86 and tested it it against the 32-bit Notepad and it didn't work, but it worked on my normal 64-bit Notepad!
How am I possibly seem to be able to inject a 32-bit DLL into a 64-bit process and vice versa!
Although my original problem has been solved and I can continue with my app's development, the curiosity as to why I'm observing this strange inverse behavior from SetWindowsHookEx is driving me insane, so I really hope someone will be able to shed some light on this.
I know this a lot of talk and no code, but the code for even a sample app is rather large and comes in both managed and unmanaged flavors, however I'll promptly post any piece of the code you think might be relevant.
I've also created a sample app so you can test this behavior yourself. It's a simple WinForms app that tries to hook into Notepad and displays its mouse events:
http://saebamini.com/HookTest.zip
It contains both an x86 version and an x64 version. On my machine (I'm on a 64-bit Windows 7), the x86 version only works with 64-bit Notepad, and the x64 version only works with 32-bit Notepad (from SysWOW64).
UPDATE - Relevant Bits of Code:
C# call to the unmanaged library:
public SetCallback(HookTypes type)
{
_type = type;
_processHandler = new HookProcessedHandler(InternalHookCallback);
SetCallBackResults result = SetUserHookCallback(_processHandler, _type);
if (result != SetCallBackResults.Success)
{
this.Dispose();
GenerateCallBackException(type, result);
}
}
public void InstallHook()
{
Process[] bsProcesses = Process.GetProcessesByName("notepad");
if(bsProcesses.Length == 0)
{
throw new ArgumentException("No open Notepad instance found.");
}
ProcessThread tmp = GetUIThread(bsProcesses[0]);
if (!InitializeHook(_type, tmp.Id))
{
throw new ManagedHooksException("Hook initialization failed.");
}
_isHooked = true;
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern int GetWindowThreadProcessId(IntPtr hWnd, IntPtr procid);
// 64-bit version
[DllImport("SystemHookCore64.dll", EntryPoint = "InitializeHook", SetLastError = true,
CharSet = CharSet.Unicode, ExactSpelling = true,
CallingConvention = CallingConvention.Cdecl)]
private static extern bool InitializeHook(HookTypes hookType, int threadID);
[DllImport("SystemHookCore64.dll", EntryPoint = "SetUserHookCallback", SetLastError = true,
CharSet = CharSet.Unicode, ExactSpelling = true,
CallingConvention = CallingConvention.Cdecl)]
private static extern SetCallBackResults SetUserHookCallback(HookProcessedHandler hookCallback, HookTypes hookType);
C++:
HookProc UserMouseHookCallback = NULL;
HHOOK hookMouse = NULL;
HINSTANCE g_appInstance = NULL;
MessageFilter mouseFilter;
bool InitializeHook(UINT hookID, int threadID)
{
if (g_appInstance == NULL)
{
return false;
}
if (hookID == WH_MOUSE)
{
if (UserMouseHookCallback == NULL)
{
return false;
}
hookMouse = SetWindowsHookEx(hookID, (HOOKPROC)InternalMouseHookCallback, g_appInstance, threadID);
return hookMouse != NULL;
}
}
int SetUserHookCallback(HookProc userProc, UINT hookID)
{
if (userProc == NULL)
{
return HookCoreErrors::SetCallBack::ARGUMENT_ERROR;
}
if (hookID == WH_MOUSE)
{
if (UserMouseHookCallback != NULL)
{
return HookCoreErrors::SetCallBack::ALREADY_SET;
}
UserMouseHookCallback = userProc;
mouseFilter.Clear();
return HookCoreErrors::SetCallBack::SUCCESS;
}
return HookCoreErrors::SetCallBack::NOT_IMPLEMENTED;
}
int FilterMessage(UINT hookID, int message)
{
if (hookID == WH_MOUSE)
{
if(mouseFilter.AddMessage(message))
{
return HookCoreErrors::FilterMessage::SUCCESS;
}
else
{
return HookCoreErrors::FilterMessage::FAILED;
}
}
return HookCoreErrors::FilterMessage::NOT_IMPLEMENTED;
}
static LRESULT CALLBACK InternalMouseHookCallback(int code, WPARAM wparam, LPARAM lparam)
{
if (code < 0)
{
return CallNextHookEx(hookMouse, code, wparam, lparam);
}
if (UserMouseHookCallback != NULL && !mouseFilter.IsFiltered((int)wparam))
{
UserMouseHookCallback(code, wparam, lparam);
}
return CallNextHookEx(hookMouse, code, wparam, lparam);
}
My best guess about your problem :
The Windows hook system is able to hook both 32-bit and 64-bit application, from any bitness. The thing is, as you pointed, you can't inject a DLL into an application with the wrong bitness. To make this work, Windows will normally inject the DLL if it can, but if it can't, it will setup a callback that use the hooking application message loop. Since the message loop is handled by the OS, it is used to make a call from different bitness.
In your case, the only thing that work is the message loop way. And there is a good reason for that : your 64-to-64 and 32-to-32 calls have no chance to succeed, because the hook is in the injected DLL, that is, in a different process than your application.
Nothing happens in your case because your UserMouseHookCallback stay to NULL. Indeed, the call to SetUserHookCallback() is done in the application DLL instance, but UserMouseHookCallback is unchanged in the target DLL instance. Once injected, the DLL is in a different process, and should be considered as such. You have to find another way to call back the application (maybe post a message, like in the 32-to-64 case, and/or make use of shared sections).
To test this, put something like MessageBox() in InternalMouseHookCallback(). The box should appear even in 64-to-64 and 32-to-32.
A company produces a device with an ethernet connector. I have one of their devices and they gave me a .lib file and some .h files so I can connect and communicate with their device. Works fine, I'm glad.
Then the company produced a new version of their device. I also have this newer device and again they gave me some files so I can connect and communicate with it: this time a .lib file, a .dll file and some .h files.
So much for the background. Now, the problem:
I wrote a simple test application in MS Visual C++ 6.0 to see what happens, when I try to connect to the new device with the old interface: the connect() method provided (using the old .lib file) immediately returns 0 (Could not connect). Makes sense, I agree!
But, when I wrap that .lib file in a .dll file and try the same scenario again, I don't get an immediate result, it takes 42 seconds until I get the 0 result!
The wrapper works fine when I try to connect to the older device, no problem at all.
This is the first project where I have to wrap up a .lib file in a .dll file (I did that so I can use it in C#), so I don't know too much about this topic. All I know is, that I'm passing along values from the original method calls to the wrapper ... How can there be such a delay? It must happen somewhere in the Wrapping, but I have no clue. Networking shouldn't be the problem, because without wrapper I get the result immediately. Maybe something about Threading or DLLs?
I was hoping someone could enlighten me here ...
I didn't post any specific code, because I wouldn't know what is relevant here. If you need more information, please let me know!
EDIT:
In C# I have a class MyDLLImport:
class MyDLLImport
{
[DllImport("Old_Interface_MSVC++GeneratedDLL.dll")]
public static extern int Connect();
[DllImport("Old_Interface_MSVC++GeneratedDLL.dll")]
public static extern int Disconnect();
...
}
In another C# class I access these methods directly, like:
MyDLLImport.Connect();
The .lib and .h files I wrapped up in the Old_Interface_MSVC++GeneratedDLL.dll file like this:
Old_Interface_MSVC++GeneratedDLL.cpp:
#include "stdafx.h"
#include "Old_Interface_MSVC++GeneratedDLL_Class.h"
#include "Old_Interface_MSVC++GeneratedDLL.h"
#include <string.h>
#include <iostream>
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
ALibClass MyDLL::client;
int MyDLL::Connect()
{
int ret;
ret = MyDLL::client.Connect();
if(!ret)
{
std::cerr << "Could not connect" << std::endl;
return false;
}
return true;
}
void MyDLL::Disconnect()
{
MyDLL::client.Disconnect();
}
...
/////////////////////////////////
// exported functions
int Connect()
{
return MyDLL::Connect();
}
void Disconnect()
{
MyDLL::Disconnect();
}
Then, there is the header file for the previous .cpp file, Old_Interface_MSVC++GeneratedDLL.h:
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
// added by myself to prevent export warnings
#pragma warning( disable: 4251 )
/////////////////////////////////////////////
//exported general methods
extern "C" MYDLL_API int Connect();
extern "C" MYDLL_API void Disconnect();
...
And finally the class header file Old_Interface_MSVC++GeneratedDLL_Class.h:
#include "lib_header_file.h"
class MyDLL
{
private:
static ALibClass client;
public:
static int Connect();
static void Disconnect();
...
}
I hope I have added all relevant code parts. If you need more information, let me know!
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 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.