How to get args from WinMain or wWinMain in Rust? - windows

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.

Related

optimization options and usage of windows dll calls

I am building a dll for windows, using a Makefile, using cl.exe. I am using VS2015.. this dll uses CNG (bcrypt) for encryption operations, and bcryptr is loaded dynamically using loadlibrary call.
When i build with /Od option to disable optimization, i have no issues with any functionality. but if i use any optimization option /O1, /O2, /Ox, i see the strangest thing happen.. once i retrieve the address for a bcrypt function, such as BCryptGetFipsAlgorithmMode, using GetProcAddress, and then i make the call to that function ptr, the call stack goes away. This results in exception when the calling function tries to return.. it looks almost like when one calls a callback doesn't have the CALLBACK prefix, but i dont see the connection..
That bcrypt function's prototype looks like this:
NTSTATUS WINAPI BCryptGetFipsAlgorithmMode( __out BOOLEAN *pfEnabled)
and WINAPI seems to be defined:
define WINAPI __stdcall
Is there something I am missing? what does optimization have to do with this?
Any help would be appreciated.. Thank You!
Heres the code:
NTSTATUS GetFipsAlgorithmMode(BOOLEAN *pfEnabled )
{
FARPROC pBCryptGetFipsAlgorithmMode = NULL;
NTSTATUS (*_BCryptGetFipsAlgorithmMode)( __out BOOLEAN *);
NTSTATUS status = SPGC_ERR_LIBRARY_ADDRESS_LOOKUP_FAILURE;
if(g_hBCRYPTDLL != NULL)
{
pBCryptGetFipsAlgorithmMode = GetProcAddress(g_hBCRYPTDLL, _T("BCryptGetFipsAlgorithmMode"));
if(pBCryptGetFipsAlgorithmMode != NULL)
{
_BCryptGetFipsAlgorithmMode = (NTSTATUS (*)( __out BOOLEAN *)) pBCryptGetFipsAlgorithmMode;
status = _BCryptGetFipsAlgorithmMode(pfEnabled);
}
}
return status;
}
step over the call to _BCryptGetFipsAlgorithmMode(), and the call stack basically gets cleared.

Why winmain parameters doesn't match to each other?

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.

LoadString with numeric idetifier

i have problem with this code:
int
WINAPI
Getdesc(IN WORD wcode,
OUT LPWSTR lpBuf)
{
WCHAR szDescription[256];
int res = LoadStringW(NULL,wcode,szDescription,256);
if(res == 0)
{
wcscpy(lpBuf, L"Undefined");
return 0;
}
else
{
wcscpy(lpBuf,szDescription);
return 0;
}
}
The function is placed in a DLL, and when i access it, it always returns "Undefined",
I think there is problem in my LoadString call, but i can't figure it out.
I'm new to windows programming, any help would be appreciated
The problem is that you are passing NULL as the HINSTANCE parameter. That means that you look for the resource in the executable host and not the DLL. You'll have to pass the module handle of the DLL. You are provided with that instance handle as the first parameter passed to your DllMain function.
If you are compiling with MSVC then you could use __ImageBase to obtain the module handle. Personally I would suggest that making a note of the value passed to DllMain is a cleaner approach. It avoids taking a dependency on one specific compiler.
Note also that you can call GetLastError in case LoadString fails to obtain more information about the reason for the error. It's quite possible that would have helped you identify the fault.

How can I get HINSTANCE from a DLL?

I have created a DLL in VC++ as Win32 project
DLLMAIN function is
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
Now I need HINSTANCE of the DLL , that need to be passed to Win32 functions.
Are HMODULE and HINSTANCE same?
How can I get HINSTANCE?
An excerpt from the book Windows Via C/C++ [1]
Note As it turns out, HMODULEs and HINSTANCEs are exactly the same thing. If the documentation for a function indicates that an HMODULE is required, you can pass an HINSTANCE and vice versa. There are two data types because in 16-bit Windows HMODULEs and HINSTANCEs identified different things
[1] Richter, Jeffery and Nasarre, Christophe, Windows Via C/C++, 5th ed, Redmond: Microsoft Press 2008, pp. 74
Microsoft linker specific
#include "windows.h"
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#pragma warning(disable: 4047)
HINSTANCE hInstance = (HINSTANCE)&__ImageBase;
#pragma warning(default: 4047)
I think that these are the same. If you want HINSTANCE of the running process (exe), you should use
GetModuleHandle(NULL);
Calling GetModuleHandle(NULL) from a dll will return the Hinstanc of the EXE that started the DLL; to get the Hinstance for the curently running dll try this tip:
http://www.dotnet247.com/247reference/msgs/13/65259.aspx
DllMain function as it's described in MSDN:
BOOL WINAPI DllMain(
__in HINSTANCE hinstDLL,
__in DWORD fdwReason,
__in LPVOID lpvReserved
);
http://msdn.microsoft.com/en-us/library/ms682583%28v=vs.85%29.aspx
Each DLL has at least a header file, say MyDll.h and a corresponding implementation file MyDll.cpp. Open the header file and add
extern HMODULE hDllModule;//or whatever name you like
Now open the MyDll.cpp file. There is a function DLLMAIN. Add before it HMODULE hDllModule; and insert hDllModuleDll = hModule; before return true;. Your code will look like this:
HMODULE hDllModuleDll;
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
hDllModuleDll = hModule;
return TRUE;
}
In functions like ::GetModuleFileNameW(hModule, PathFile, MAX_PATH); that require a HMODULE of the DLL you can pass the global variable hDllModule.
To complement the other answers, for the sake of completness.
The actual signature of DllMain has an HINSTANCE parameter, instead of a HMODULE parameter. The Visual Studio DLL template generates the signature with HMODULE since at least Visual Studio 2008 however, but I believe this to be a minor bug more than anything. VC6 generated the code with HANDLE (even though both HINSTANCE and HMODULE exist). The reason that doesn't cause problems is because HINSTANCE and HMODULE are now exactly the same thing. Unfortunately I was unable to find an ancient enough version of the MSDN documetnation that could have confirmed this.
So the answer is: You get your HINSTANCE as an argument to your DllMain.
Personally I sort of like the distinction between HMODULE and HINSTANCE because it appeals to me as being good code hygiene. It's a bit like using const. But then, a new question arises: Given your HINSTANCE, how do you get your HMODULE in the "hygienic" way?
The windowsx.h header defines GetInstanceModule, which is now a macro that just casts the HINSTANCE to HMODULE. It only exists for code compatibility, along with a bunch of very similar macros.

Is anyone familiar with the undocumented ObReferenceObjectByName windows kernel function?

I read a very fascinating article that was about programming drivers using the wdk, and one of the functions it used is called ObReferenceObjectByName. This function has given me lots of headaches. The first bad thing is that it's not documented by microsoft. The second thing, is that the language used in the article was C++, and I want to keep my code in plain ol' C. I know that most of the time this shouldn't be a problem, but I haven't - for the life of me - been able to figure out how to include this function.
The code in the article goes something like:
extern "C"{
#include <ntifs.h>
NTSYSAPI NTSTATUS NTAPI ObReferenceObjectByName(PUNICODE_STRING ObjectName,
ULONG Attributes,
PACCESS_STATE AccessState,
ACCESS_MASK DesiredAccess,
POBJECT_TYPE ObjectType,
KPROCESSOR_MODE AccessMode,
PVOID ParseContext OPTIONAL,
PVOID* Object);
}
I've been trying to replicate this for hours. I tried declaring it without the 'extern' keyword, I tried changing the calling convention, I tried changing the includes... I always end up with the error "unresolved external symbol...".
I'm absolutely stumped, so if anyone could offer some advice, I'd be grateful. Thanks.
You wouldn't be reading http://www.codeproject.com/KB/recipes/keystroke-hook.aspx and trying to create your own Keyboard Logger would you?
Anyways, instead of using this, call ZwCreateFile then ObReferenceObjectByHandle instead.
Here is a test C code compiled and built with no problems:
#include <ntddk.h>
NTSYSAPI NTSTATUS NTAPI ObReferenceObjectByName(
PUNICODE_STRING ObjectName,
ULONG Attributes,
PACCESS_STATE AccessState,
ACCESS_MASK DesiredAccess,
POBJECT_TYPE ObjectType,
KPROCESSOR_MODE AccessMode,
PVOID ParseContext OPTIONAL,
PVOID* Object
);
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
ObReferenceObjectByName(0, 0, 0, 0, 0, 0, 0, 0);
return STATUS_SUCCESS;
}
I don't know this API, but I can give you a trick that might help you diagnose the problem.
at a command prompt that has MSVC tools in the path
link /dump /exports ???.dll
where ???.dll is the dll were you expect this function to be. This will give you a complete list of exported symbol names and will tell you two things. 1) is the symbol there? and 2) is it being decorated the same as your attempted prototype.
For 32 bit kernel, you should expect this to be called _ObReferenceObjectByName#64,

Resources