I'm writing a windows service and I need to pass some parameters into the service, does anyone know how I would go about this?
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
SERVICE_TABLE_ENTRY svcTable[2] = {0};
svcTable[0].lpServiceName = (LPWSTR)svcName.c_str();
svcTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)TRPC::ServiceMain;
std::wstring* str = new std::wstring(TEXT("Hello World"));
// I want to pass say, str in argv[0];
StartServiceCtrlDispatcher(svcTable);
}
void TRPC::ServiceMain(DWORD argc, LPWSTR* argv)
{
Access argv here...
}
ServiceMain is a static definition within the class so as to conform with __stdcall, but what I want to do is populate agrv with my OWN set of allocated strings before StartServiceCtrlDispatcher() is called, so that I can use the parameters from within the method. Any ideas?
The Service Control Manager calls ServiceMain() from within StartServiceCtrlDispatcher() when starting the service. The only way to pass parameters to ServiceMain() itself are to:
Put the parameters in the SCM UI (the "Services" applet in the Control Panel) when starting the service by clicking on the "Start" button in the service's properties dialog. These parameters are not persistent, though. One you close the UI, they are discarded.
In a call to the StartService() function in your own app code.
The alternatives are to either:
Put command-line parameters in the service registration, then the service can use GetCommandLine() when needed.
Put the values somewhere - Registry, Database, INI/XML file, etc - that the service can access when needed.
Related
I am trying to learn how to work with raw Win32 API's and am following the tutorial here, but cannot, for the life of me, figure out how to pass int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) function signature to work. I do understand the int WINAPI isn't needed...but how do I get all of those parameters to pass to WinAPI calls? Especially hInstance and nCmdShow?
My Goal
Get hInstance and nShowCmd from
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {}
into a Rust program, perhaps something like:
fn main(/* args, such as hInstance, nShowCmd here /*) {
}
or, the more likely way:
fn main() {
std::env::/* hopefully something like args() /*;
}
What I've tried
I've tried getting the args, but this just passes the command-line arguments that I used to spawn the program, just as args[0] being the name of the program, which is the expected behavior. Also, calling args_os() gives the same result.
I've also tried to set the windows subsystem, but the previous behavior is the same, not the desired behavior...
#![windows_subsystem = "windows"]
I am able to get the hInstance handle by manually calling GetModuleHandle() and passing in a null pointer, but have no idea how to get the nShowCmd manually.
Important Note
I am using the windows crate, which is what I would like to use.
Any help to this eluding mystery would be much appreciated!
P.S. My window does open, and everything works fine, as expected, including working with FFI, and all the crazies involved there, lol. But I just would like to understand how this is done. One can get by without the nShowCmd, but I would really like to be able to understand how this is done in rust. I also cannot overwrite the fn main() function signature, so not sure how to go about it.
WinMain is the user-provided entry point of a Windows application. The raw application entry point as seen by the OS is far simpler:
DWORD CALLBACK RawEntryPoint(void);
It is now up to language support libraries to recover the startup information and call into the user-provided entry point (see WinMain is just the conventional name for the Win32 process entry point for details):
GetModuleHandle(NULL) for hInstance
hPrevInstance is always NULL in 32-bit and 64-bit Windows
GetCommandLine for the unparsed command line passed to the program
GetStartupInfo for lots of state information, including the wShowWindow that corresponds to nCmdShow
If you have Visual Studio installed you can have a look inside exe_common.inl to see how the C and C++ support libraries go about this.
With Rust things are unfortunately more complex. Even though the compiler and linker repurpose MSVC's CRT implementation responsible for extracting the information that would be passed into WinMain, I'm not aware of a way to get a hold of this from Rust.
You're going to have to recover that information manually. Getting to the nCmdShow parameter is a bit more involved, so let's illustrate that here:
// build.rs
// Using windows-rs 0.17.2; version 0.10.0 and later should be just fine
fn main() {
windows::build!(Windows::Win32::System::Threading::GetStartupInfoW,)
}
// src/main.rs
mod bindings {
windows::include_bindings!();
}
use bindings::Windows::Win32::System::Threading::{GetStartupInfoW, STARTUPINFOW};
fn main() {
let mut si = STARTUPINFOW {
cb: std::mem::size_of::<STARTUPINFOW>() as u32,
..Default::default()
};
unsafe { GetStartupInfoW(&mut si) };
let cmd_show = si.wShowWindow as i32;
println!("nCmdShow: {:?}", cmd_show);
}
With that you now have access to a value that corresponds to the nCmdShow parameter passed into WinMain when compiling for C or C++ (roughly, anyway). Ideally you would need to see whether dwFlags contains the STARTF_USESHOWWINDOW bit, and fabricate a reasonable default when it doesn't.
That said, I'm not even sure what purpose the nCmdShow argument passed into WinMain serves. As explained under ShowWindow, using that value doesn't have any effect when it is populated from caller-provided information.
Update 2021-10-28
Starting with version 0.22.1 the windows crate comes with pre-built bindings, making it a lot easier to consume the Windows API. The following implements the same program using the pre-built bindings in place of compile-time code generation.
Cargo.toml
[package]
name = "startup_info"
version = "0.0.0"
edition = "2021"
[dependencies.windows]
version = "0.22.1"
features = ["Win32_Foundation", "Win32_System_Threading"]
main.rs
use windows::Win32::System::Threading::{GetStartupInfoW, STARTUPINFOW};
fn main() {
let mut si = STARTUPINFOW {
cb: std::mem::size_of::<STARTUPINFOW>() as u32,
..Default::default()
};
unsafe { GetStartupInfoW(&mut si) };
let cmd_show = si.wShowWindow as i32;
println!("nCmdShow: {:?}", cmd_show);
}
There is this function in Kernel32: GetStartupInfo() that in windows-rs seems to be mapped to bindings::Windows::Win32::System::Threading::GetStartupInfoW.
This function fills a STARTUPINFOW structure that has, between a lot of useful fields, WORD wShowWindow that has the same value as the last argument in WinMain().
The funny thing about WinMain is that there is nothing magic in it, it is just the function that the real entry point function, WinMainCRTStartup calls from the CRT initialization code. You can get an idea of how it does its thing by looking at the equivalent Wine source code. There you can see that your idea of calling GetModuleHandle(NULL) to get the hInstance is the right one.
Ordinarily XInput controllers are identified simply using an index corresponding to the player number of the controller. Is there a way to obtain more information about a controller with a specific index, such as its vendor ID, product ID, or device name?
Even better would be a identifier that corresponds uniquely and consistently to just that controller so that it can be distinguished from all other XInput devices regardless of its index, including another controller that's an identical model (i.e. same product and vendor ID), similar to the instance GUID available using DirectInput.
Can this be accomplished using XInput or another Microsoft API? I'm also open to using undocumented functions if need be.
There are a few undocumented functions inside the XInput1_4.dll. You can get the Vendor ID and Product ID like this:
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <Xinput.h>
#include <stdio.h>
struct XINPUT_CAPABILITIES_EX
{
XINPUT_CAPABILITIES Capabilities;
WORD vendorId;
WORD productId;
WORD revisionId;
DWORD a4; //unknown
};
typedef DWORD(_stdcall* _XInputGetCapabilitiesEx)(DWORD a1, DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES_EX* pCapabilities);
_XInputGetCapabilitiesEx XInputGetCapabilitiesEx;
void main()
{
HMODULE moduleHandle = LoadLibrary(TEXT("XInput1_4.dll"));
XInputGetCapabilitiesEx = (_XInputGetCapabilitiesEx)GetProcAddress(moduleHandle, (char*)108);
for (int i = 0; i < 4; ++i)
{
printf("Gamepad %d ", i);
XINPUT_CAPABILITIES_EX capsEx;
if (XInputGetCapabilitiesEx(1, i, 0, &capsEx) == ERROR_SUCCESS)
{
printf("connected, vid = 0x%04X pid = 0x%04X\n", (int)capsEx.vendorId, (int)capsEx.productId);
}
else
{
printf("not connected\n");
}
}
}
What XInput internally does is open a device, then call DeviceIoControl on it every time it reads the joypad. (control code 0x8000e00c)
You need to hook these functions imported by "XInput1_4.dll":
CreateFileW from "api-ms-win-core-file-l1-1-0.dll"
DuplicateHandle from "api-ms-win-core-handle-l1-1-0.dll"
CloseHandle from "api-ms-win-core-handle-l1-1-0.dll"
DeviceIoControl from "api-ms-win-core-io-l1-1-0.dll"
Using the hooks for CreateFileW, DuplicateHandle and CloseHandle, you can keep track of what filename is associated with a handle.
Then when you see a call to DeviceIoControl with control code 0x8000e00c, you will know what filename is being read.
The first time you call XInputGetState, it will open multiple devices, and call DeviceIoControl multiple times, regardless of what player number you have asked for. You are only interested in the last filename seen by DeviceIoControl before XInputGetState returns. And if XInputGetState indicates the controller is not plugged in, disregard the filename you have collected for that controller number.
Examples of filenames I have seen on my own computer:
\\?\hid#{00001124-0000-1000-8000-00805f9b34fb}&vid_045e&pid_02e0&ig_00#8&7074921&2&0000#{ec87f1e3-c13b-4100-b5f7-8b84d54260cb}
\\?\usb#vid_045e&pid_028e#1&1a590e2c&1&01#{ec87f1e3-c13b-4100-b5f7-8b84d54260cb}
edit:
One more hook is required as well.
CoCreateInstance from "api-ms-win-core-com-l1-1-0.dll", to hook creating the undocumented IDeviceBroker COM object. If it can successfully create an IDeviceBroker COM object, it will use that instead of the call to CreateFileW. Parameters will be: CLSID_DeviceBroker = {acc56a05-e277-4b1e-a43e-7a73e3cd6e6c}, IID_IDeviceBroker = {8604b268-34a6-4b1a-a59f-cdbd8379fd98}. The method OpenDeviceFromInterfacePath will be called instead of CreateFileW. Alternatively, you can make creating the IDeviceBroker object simply fail, and it will proceed to use CreateFileW as usual.
In my app, I catch all event via SetWindowsHookEx and when a user clicks on a button I retrieve an hwnd that I guess is the handle of the Tbutton.
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
DWORD lPrivate;
} MSG, *P
Now how can I from this hwnd retrieve the button name (or better the Delphi object representing the button?).
Maybe I can also retrieve the component via the POINT pt; ?
You can use FindControl, which will retrieve the object instance if the window is created by a control that belongs to the same instance of the VCL that calls the function. Since Name is published in TComponent, you can access the property regardless of the actual class type.
Every windowed VCL control has its object instance address stored in the API window's property list, along with properties containing information of module address, process id, thread id. This makes it possible for the VCL to backtrack a control from the window it created.
Why QApp constructor fails with WinMain parameters?
int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPTSTR _lpCmdLine, int _nShowCmd) {
QApplication app(_nShowCmd, & _lpCmdLine);
And here it fail with exception:
Exception at adress 0x0F3621DC (Qt5Guid.dll) in updater_app.exe: 0xC0000005
Whats wrong? how to fix it?
UPD:
And it works in such way:
int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPTSTR _lpCmdLine, int _nShowCmd) {
int nShowCmd(0);
QApplication app(nShowCmd, & _lpCmdLine);
_lpCmdLine is 10 and _nShowCmd is empty string - so it doesn't match. Why?
The Qt application QApplication main object supposed to be created in the standard main function:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// more app objects initialization
return app.exec();
}
and you do:
int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPTSTR _lpCmdLine, int _nShowCmd)
{
QApplication app(nShowCmd, & _lpCmdLine);
return app.exec();
}
Which implies that &_lpCdLine being equivalent to argv but:
LPTSTR _lpCmdLine which is an equivalent to char* and you take an address of it so &_lpCmdLine matches char** when _lpCmdLine is pointing to a contiguous buffer of characters and not an array of character strings as argv.
It makes sense to consider how main() function is implemented on Windows. There is a good discussion: WINMAIN and main() in C++ (Extended) Mind that the C runtime must be initialized before main() function runs and that may depend on compiler/linker. And also find an example of Qt application main function.
I guess that the case when you make your code not to crash by introducingn nShowCmd == 0 makes QApplication object not to read the command line which prevents an incorrect access via random content interpreted as addresses in _lpCmdLine. But that is still wrong and an incomplete intitialization of QApplication object.
The authors fails to see the window and sees the console instead and that relates to incomplete code of main function not launching any window. Also, QML application main.cpp might help.
Since this problem becomes relevant again with Qt6, here is a very simple solution for VisualStudio with using WinMain as entry point:
QApplication app(__argc, __argv);
__argc and __argv are populated by the microsoft compiler
(found this suggestion here: https://codingmisadventures.wordpress.com/2009/03/10/retrieving-command-line-parameters-from-winmain-in-win32/)
Short explanation of the problem: using main() with VC requires to use Subsystem:Console, but this always opens a console window, which you wanna avoid usually. You cannot get rid of this console, except with some very dirty hacks. So you need to switch to Subsystem:Windows to have a "silent" application launch, but this requires to use WinMain() as application entry point. Up to Qt5 there was an own implementation of WinMain() in qtmain.lib, which you simply linked in, and which forwards to the main()-function, but this has been kicked out in Qt6. The problem exists afaik only for MSVC users, because with mingw you can disable the console window and continue using main(), as shown in the Qt-examples.
I wrote some code to watch for window title changes. It works fine with different windows in my Windows 7. I use SetWinEventHook like that:
SetWinEventHook(EVENT_OBJECT_NAMECHANGE,
EVENT_OBJECT_NAMECHANGE,
0,
WinEventCallback,
processId,
threadId,
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS | WINEVENT_SKIPOWNTHREAD);
Callback:
void CALLBACK WinEventCallback(HWINEVENTHOOK hWinEventHook,
DWORD dwEvent,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD dwEventThread,
DWORD dwmsEventTime)
{
qDebug("Window %p", hwnd);
...
GetWindowText(hwnd, ...);
}
For one specific window I see the debug message "Window 0x0" all the time, e.g. I get the window handle set to zero in the callback. In this case GetWindowText fails. All other windows work fine. The question is why? I don't see anything extraordinary in Spy++:
Not all events generated may be associated with a window, especially for something as generic as a name change. The hook documentation specifically states that NULL windows are possible, so simply ignore them if your hook logic is window-oriented. If you are seeing a window change its title but you are getting a NULL window in your callback, then either it is not a real window, or there was an issue marshaling the window to your callback, or something like that.
The problem comes for the WinEventCallback's signature you are using.
Fix it by using this one: WinEventCallback(IntPtr hWinEventHook, uint iEvent, IntPtr hWnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)