Qt shell32.dll ShellExecuteA strange behavior. How Can I display a file? - qlibrary

I have some code defined as follows:
typedef long (*ShellExecFunct)(long, const char*, const char*, long, long, long);
void some_funct()
{
QLibrary shell32(QString("shell32.dll"));
ShellExecFunct viewfile = (ShellExecFunct) shell32.resolve("ShellExecuteA");
if(viewfile)
{
int res = viewfile(NULL, "open", "c:\\eula.1028.txt", NULL, NULL, 5);
}
}
This code launches the file as I want it to, but blows up immediately. The new window stays open.
I have another version like so:
typedef long (*ShellExecFunct)(long, const char*, const char*, long, long, long);
void some_funct()
{
QLibrary shell32(QString("shell32.dll"));
ShellExecFunct viewfile = (ShellExecFunct) shell32.resolve("ShellExecuteA");
if(viewfile)
{
int res = viewfile(NULL, "open", "c:\\eula.1028.txt", NULL, NULL, 5);
QMessageBox b;
b.setText(QString::number(res,10));
}
}
This code does not blow up. Notice I don't even need to call b.exec(). However, if I do call b.exec(), the value 42 is displayed.
Can someone clue me in as to what is going wrong here and what I can do to fix it?
Thanks.
Edit:
For posterity, the premise here is wrong. Qt provides the exact functionality I need without platform-specific code. Please see accepted answer.

Why not just use Qt for the whole thing like this?
QDesktopServices::openUrl(QUrl("file:///c:/eula.1028.txt", QUrl::TolerantMode));
As for why you get a crash, it could be a calling convention issue. This is just a guess, but you may want to try adding WINAPI to the function pointers type. But like I said, I would just use QDesktopServices and not deal with these things directly.
Something like this:
typedef HINSTANCE WINAPI (*ShellExecutePtr)(HWND,LPCSTR,LPCSTR,LPCSTR,LPCSTR,INT);

Related

how to fix wrong GCC ARM startup code pointer to initialized and zero variables?

[skip to UPDATE2 and save some time :-)]
I use ARM Cortex-M4, with CMSIS 5-5.7.0 and FreeRTOS, compiling using GCC for ARM (10_2021.10)
My variables are not initialized as they should.
My startup code is pretty simple, the entry point is the reset handler (CMSIS declared startup_ARMCM4.s as deprecated and recommend using the C code startup code so this is what I do).
Here is my code:
__attribute__((__noreturn__)) void Reset_Handler(void)
{
DataInit();
SystemInit(); /* CMSIS System Initialization */
main();
}
static void DataInit(void)
{
typedef struct {
uint32_t const* src;
uint32_t* dest;
uint32_t wlen;
} __copy_table_t;
typedef struct {
uint32_t* dest;
uint32_t wlen;
} __zero_table_t;
extern const __copy_table_t __copy_table_start__;
extern const __copy_table_t __copy_table_end__;
extern const __zero_table_t __zero_table_start__;
extern const __zero_table_t __zero_table_end__;
for (__copy_table_t const* pTable = &__copy_table_start__; pTable < &__copy_table_end__; ++pTable) {
for(uint32_t i=0u; i<pTable->wlen; ++i) {
pTable->dest[i] = pTable->src[i];
}
}
for (__zero_table_t const* pTable = &__zero_table_start__; pTable < &__zero_table_end__; ++pTable) {
for(uint32_t i=0u; i<pTable->wlen; ++i) {
pTable->dest[i] = 0u;
}
}
}
__copy_table_start__, __copy_table_end__ etc. have the wrong values an so no data is copied to the appropriate place in RAM.
I tried adding __libc_init_array() before DataInit(), as suggested in this answer, and remove the nostartfiles flag from the linker, but at some point __libc_init_array() jumps to an illegal address and I get a HardFault interrupt.
Is there a different method to fix it? maybe one where I can use the nostartfiles flag?
UPDATE:
Looking at the memory, where __copy_table_start__ is located, I see the data there is valid (even without the use of __libc_init_array()). It seems that pTable doesn't get the correct value.
I tried using __data_start__, __data_end__, __bss_start__, __bss_end__ and __etext instead of the above variables, in the linker file it is said they can be used in code without definition, but they cannot (maybe that's a clue?). In any case they didn't work either.
UPDATE2:
found the actual problem
all struct members get the same value (modifying one changes all others), it happens with every struct. I have no idea how this is possible. In other words the value of __copy_table_start__.src is, for example, 0x14651234, __copy_table_start__.dest is 0x00100000, and __copy_table_start__.wlen is 0x0365. When looking at pTable all members are 0x14651234.

What is the "address" being returned from GetProcAddress()?

I'm a little bit confused with GetProcAddress().
Quoting the Win32 docs:
If the function succeeds, the return value is the address of the exported function or variable.
I know that for functions, GetProcAddress() returns a function pointer that calls the desired function. However, it isn't clear what GetProcAddress() returns in something like this:
DLL code
typedef struct {
uint64_t foo;
double bar;
char* baz;
} MyStruct;
__declspec(dllexport) MyStruct* my_struct_ptr;
__declspec(dllexport) long long special_global;
Application code
void* my_struct_ptr = GetProcAddress(my_dll_handle, "my_struct_ptr");
void* special_global = GetProcAddress(my_dll_handle, "special_global");
What would my_struct_ptr and special_global in the application point to?
EDIT: Does my_struct_ptr point to the DLL's my_struct_ptr or the struct that the DLL's my_struct_ptr points to?

How to get args from WinMain or wWinMain in Rust?

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.

Why _makepath_s does not return on error?

According to https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/makepath-s-wmakepath-s?view=vs-2019#generic-text-routine-mappings _makepath_s should return an error code on failure.
On my system this is not true, it gives an assert (Expression: (L"Buffer is too small" && 0)) and then terminates the program. In release it simply terminates the program.
The only way I found to keep my program running is to set an empty invalid parameter handler:
void myInvalidParameterHandler(const wchar_t* expression,
const wchar_t* function,
const wchar_t* file,
unsigned int line,
uintptr_t pReserved)
{
}
_set_invalid_parameter_handler(myInvalidParameterHandler);
But this is not recommended. The docs say that this handler should abort the program.
And I also think that this modifies all secure function which is not what exactly what I want.
So is there a better way to get _makepath_s behave like the docs say?
TIA Michael
VS2017, MFC
Edit
A small sample:
char Path[_MAX_PATH];
char drive[_MAX_DRIVE] = "C:";
char dir[_MAX_DIR] = "Thisisaverylongdirname01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
char fname[_MAX_FNAME] = "Thisisaverylongfilename012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
char ext[_MAX_EXT] = "txt";
_makepath(Path, drive, dir, fname, ext);

How to make String to const wchar_t* conversion function work under Windows and Linux

I work on a project written for MSVCC / Windows, that I have to port to GCC / Linux. The Project has its own String Class, which stores its Data in a QString from Qt. For conversion to wchar_t* there was originally this method (for Windows):
const wchar_t* String::c_str() const
{
if (length() > 0)
{
return (const wchar_t*)QString::unicode();
}
else
{
return &s_nullString;
}
}
Because unicode() returns a QChar (which is 16 Bit long), this worked under Windows as wchar_t is 16 Bit there, but now with GCC wchar_t is 32 Bit long, so that doesn't work anymore. I've tried to solve that using this:
const wchar_t* String::c_str() const
{
if ( isEmpty() )
{
return &s_nullString;
}
else
{
return toStdWString().c_str();
}
}
The problem with this is, that the object doesn't live anymore when this function returns, so this doesn't work eiter.
I think the only way to solve this issue is to either:
Don't use String::c_str() and call .toStdString().c_str() directly
Make GCC treat wchar_t as 16 bit type
Possibility one would mean several hours of needless work to me and I don't know if possiblity 2 is even possible. My question is, how do I solve this issue best?
I'd appreciate any useful suggestion. Thank you.
In my opinion, there are 2 ways :
convert QString to wchar_t* when needed
Let QString to store wchar_t* and return QString::unicode directly
These two functions can convert a QString to std::string and std::wstring
QString::toStdWString
QString::toStdString
To build QString as ucs4 :
#define QT_QSTRING_UCS_4
#include "qstring.h"
This can be used in qt3(qstring.h). I can't find the source of qt4.

Resources