|
I do see a bug.
SHELLEXECUTEINFO sei = { sizeof(sei) };
You should zero that struct out. Then set the cbSize member. Not sure if this is causing your problem though.
I'm on my TV right now so reviewing on my couch. But don't see any other issues.
|
|
|
|
|
Thanks for the tip!
Am also currently looking into a way to use the ControlService utility via CreateProcess to install the service, instead of calling CreateService directly... But not sure if I can pull that off correctly...
|
|
|
|
|
I just noticed in your original post:
Rick R. 2023 wrote:
Am using VS2022 using toolset 1.41_XP
Do you get the same behavior if you compile for Windows 11?
|
|
|
|
|
Starting to suspect the toolset, too.
Have to fix a bunch of linker problems caused by my old and pretty messy project settings, in order to test with a newer version... might take a while.
|
|
|
|
|
Well,
Alot of members check the forums everyday. Sometimes it just helps to have a few other eyes look at the issue.
Rick R. 2023 wrote: Have to fix a bunch of linker problems
XP to Win11 is a big jump, I can imagine.
|
|
|
|
|
how to create a fingerprint sensor code with c++ from scratch
|
|
|
|
|
Probably you mean 'how to create from scratch a C++ application interfacing a fingerprint sensor module (directly handling the fingerprint sensor would be far more difficult, I suppose).
If I got you then you should carefully read the documentation of the module and implement yourself the appropriate communication code.
You may also have a look at existing libraries source code (e.g. Arduino).
"In testa che avete, Signor di Ceprano?"
-- Rigoletto
|
|
|
|
|
Hi there, I can't understand why my char type array isn't displayed with printf using string specifier %s. Here is my code.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define SIZE 10
void findbinary(int number, char result[], int index);
int main (void)
{
int someNumber = 233;
char result[SIZE];
int index = 0;
size_t i;
findbinary(someNumber, result,0);
printf("Decimal %d in binary = %c ",someNumber);
for(i = 0; result[i] != '\0'; i++)
printf("%s",result[i]);
}
void findbinary(int number, char result[], int index)
{
if(number == 0){
return;
}
result[index] = number % 2;
findbinary(number / 2, result, index + 1);
}
It displayed properly if I use %d specifier, but... this is char array..?
Thank you.
modified 20-Sep-23 11:07am.
|
|
|
|
|
In:
printf("%s",result[i]) result[i] is a char not a string. It should be printed with '%c' not '%s'.
Also in:
printf("Decimal %d in binary = %c ",someNumber); you have 2 format specifiers (%d and %c) and only one argument. All hell can break loose when program accesses nonexistent argument.
Mircea
|
|
|
|
|
Your compiler will warn you about issues with printf formats not agreeing with the arguments provided. MS C seems to do this even without any additional warning flags. If you're using GCC (linux) or clang (Apple?) then you can add -Wall to the command line. The -Wall flag will generate warnings for the most often occurring code issues that are usually the cause of bugs. It's probably a good idea to add -Wextra to the command line, too.
Keep Calm and Carry On
|
|
|
|
|
Probably you meant something similar to
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define SIZE 10
void findbinary(int number, char result[], int index);
int main (void)
{
int someNumber = 233;
char result[SIZE];
findbinary(someNumber, result,0);
printf("Decimal %d in reversed binary %s\n", someNumber, result);
return 0;
}
void findbinary(int number, char result[], int index)
{
if ( index == SIZE)
exit(-1);
if(number == 0){
result[index] = '\0'; return;
}
result[index] = (number % 2) + '0'; findbinary(number / 2, result, index + 1);
}
Note you are representing the binary number 'reversed' (that is leftmost bit is the least significant).
"In testa che avete, Signor di Ceprano?"
-- Rigoletto
|
|
|
|
|
I was trying to get the following to work:
#include <iostream>
#include <string>
#include <array>
std::string exec(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&_pclose)> pipe(_popen(cmd, "r"), _pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}
void main() {
std::cout << exec("wmic bootconfig get description");
std::cout << exec("wmic diskdrive where DeviceID='\\.\PHYSICALDRIVE3' get model,serialnumber");
}
The first exec is running fine and giving the right return, but I'm having problems with the second exec, where I'm getting: ERROR: Description = Invalid query. In my search for a fix for that error, I found that because I'm using C++ I should be using native wmi queries, but unfortunately each result with code examples that I found were "like 3 pages" of code. Does anyone know of some more simple examples for wmi queries?
As for the code, I'm trying to get the model and serial number of the disk that the OS is installed on for some unique identification of app install and some other minor checks. And for this, I found that using wmic bootconfig get description , you can parse that result (Description\Device\Harddisk3\Partition1 , 3 is the index) and get the index of the disk the OS is installed on, then using that index and wmic diskdrive where DeviceID='\\.\PHYSICALDRIVE3' get model,serialnumber where you change the 3 from PHYSICALDRIVE3 with the index from last command, you get the model and serial number. The code above is just a test to see the commands working, later I was going to parse the result and update the second exec call, right now it's using the index for my PC that I know were the OS is installed.
And yes, I know that the user can simply install the OS on a different disk and so the model and/or serial number would be different, but having to go though all that is enough of a hindrance to actually affect my use case, as I only care when the app is launched, and not on how many machines is installed on, that is IF the user finds that I'm using this verification method in the first place.
Any help is appreciated!
|
|
|
|
|
I am not an expert in WMI, but the issue is not that of C++ but your use of the wmic commands. You should first check the actual syntax of the commands you are trying to use. Then run them in a command window to find out the actual correct usage. I tried them both and got the following:
C:\Users\rjmac\source\repos>wmic bootconfig get description
Description
\Device\Harddisk0\Partition1
C:\Users\rjmac\source\repos>wmic diskdrive where DeviceID=\\.\PHYSICALDRIVE0
Node - RJM-INSPIRON15
ERROR:
Description = Invalid query
So I tried this to find out what options are available with the diskdrive sub-command:
C:\Users\rjmac\source\repos>wmic diskdrive -?
DISKDRIVE - Physical disk drive management.
HINT: BNF for Alias usage.
(<alias> [WMIObject] | <alias> [<path where>] | [<alias>] <path where>) [<verb clause>].
USAGE:
DISKDRIVE ASSOC [<format specifier>]
DISKDRIVE CREATE <assign list>
DISKDRIVE DELETE
DISKDRIVE GET [<property list>] [<get switches>]
DISKDRIVE LIST [<list format>] [<list switches>]
The final one allows you to check the options for the command.
So the error message "Description = Invalid query" is telling you that the command is in error, and you need to find out what the correct format is.
|
|
|
|
|
Richard MacCutchan wrote: So the error message "Description = Invalid query" is telling you that the command is in error, and you need to find out what the correct format is.
I did make it work, for some reason I had to use a lot of \ in C++ (I thought you don't need to escape \ in C++, but maybe that is valid in some cases, or maybe I'm just wrong about it).
If used in CMD directly then you need \\\\.\\
I'm still looking for a native use for wmi as that is suggested when used with C++, but at least if I don't find one, you can use this now that is working.
std::cout << exec("wmic diskdrive where DeviceID='\\\\\\\\.\\\\PHYSICALDRIVE3' get model,serialnumber");
//Edit: I'm not looking anymore. I found out that in C++ you need a lot more code to make it work compared with .NET, so I guess you can stick with this, now that it is working.
modified 20-Sep-23 4:59am.
|
|
|
|
|
Yes you always need to escape backslashes in C/C++, as the backslash itself is the escape character. It's also interesting that you need to escape them when using the string in a command window. Glad you found the answer.
|
|
|
|
|
See Valentinor's reply to me below.
|
|
|
|
|
Based on the reply from Richard MacCutchan and Valentinor I ended up using CMD call in C++ and this is the final form of the code after parsing the returns as well:
#include <iostream>
#include <string>
#include <array>
std::string exec(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&_pclose)> pipe(_popen(cmd, "r"), _pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}
void replaceString(std::string& subject, const std::string& search,
const std::string& replace) {
size_t pos = 0;
while ((pos = subject.find(search, pos)) != std::string::npos) {
subject.replace(pos, search.length(), replace);
pos += replace.length();
}
}
void main() {
std::string index = exec("wmic bootconfig get description");
index = index.at(index.find_last_of("\\") - 1);
std::string modelSerialNumber = "wmic diskdrive where DeviceID='\\\\\\\\.\\\\PHYSICALDRIVE" + index + "' get model,serialnumber";
modelSerialNumber = exec(modelSerialNumber.c_str());
replaceString(modelSerialNumber, "Model", "");
replaceString(modelSerialNumber, "SerialNumber", "");
replaceString(modelSerialNumber, " ", "");
std::cout << modelSerialNumber;
}
The output will be: KINGSTONSA2000M8250GXXXX_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX_XXXX . For my use case I needed the model and the serial number united like that.
Thank you both for your help!
|
|
|
|
|
From a wmic prompt, the command:
diskdrive where DeviceID='\\\\.\\PHYSICALDRIVE0' produces a result that can be parsed. In C/C++, each of those backslashes needs to be escaped (with a backslash).
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"You can easily judge the character of a man by how he treats those who can do nothing for him." - James D. Miles
|
|
|
|
|
David Crow wrote: diskdrive where DeviceID='\\\\.\\PHYSICALDRIVE0'produces a result that can be parsed.
In C/C++, each of those backslashes needs to be escaped (with a backslash). The real problem is that people can say anything like that with at straight face: Sure, that which semantically '\\.\' is already escaped in one level, but certain conditions that are too difficult to explain means that every one of those six backslashes - semantically, they are only three - must be escaped to make the three of them into twelve escaped-escaped backslashes ..."
This is the kind of stuff that makes me sigh, "Sorry, workmates: We failed. This just doesn't work."
Even programmers have difficulties handling it (at least in counting the number of slashes when the debugger displays the string as \\\\\\\\.\\\\devicename). When we say 'OK with us!', the next step is that we expect non-IT users to accept something like that.
Really, it is like saying that 'In tttthis systtttem you have to repeatttt every tttt four ttttimes, because tttthatttt is tttthe way tttthe systtttem is builtttt!'
|
|
|
|
|
Well....
You can always hide it with a macro. Or several.
|
|
|
|
|
I am not a super-expert with macros (in my C# programming I have never used it). I do not immediately see how I can write a macro so that I can write file names in their 'true' form, without any escaping of backslashes (or for that sake, spaces, different quotes and other other characters requiring quoting in a *nix file system context.
If you can show me how a macro to achieve that might look like, I would be grateful.
|
|
|
|
|
The problem goes back to the first days of MS-DOS, when they chose the backslash for the path separator, rather than the forward slash used by UNIX. Microsoft could have addressed this when they created the first version of Windows, but obviously no one thought it important.
|
|
|
|
|
Richard MacCutchan wrote: The problem goes back to the first days of MS-DOS, when they chose the backslash for the path separator,
Apparently IBM is responsible for that decision. But there are other factors. The following is surprisingly detailed. Even comments at end are interesting. Goes sideways with charset discussion but that also is historically interesting.
Why Does Windows Really Use Backslash as Path Separator? | OS/2 Museum[^]
Richard MacCutchan wrote: Microsoft could have addressed this when they created the first version of Windows, but obviously no one thought it important.
Huh? There were quite a few versions of windows which ran on DOS. Not with it or instead of it. DOS provided the file system not Windows. DOS was directly accessible. So they would have needed to change DOS.
If they had changed it with Win 95 (perhaps the first possible) it would have made it incompatible with a lot of third party software that ran fine on Win 95.
|
|
|
|
|
I always was happy that DOS/Windows chose the backwards slash. Ideally, the separator should be a character that is never used in plain text so it could be used in a file name. Forward slash is often used in text, in several different ways: As an either/or. For fractions, 1/2. At least in Norwegian (but I believe it holds for a number of other languages as well) in commonly used abbreviations, A/S is an 'Ltd.' company. (Today some companies have dropped the slash, but several thousand A/S-es still have 'A/S' in their official name.)
The same goes for the full stop between the file name and the extension. Lunix handles this by not splitting into name.ext. DOS/Windows mostly determines or identifies the extension from context, but there are still cases of ambiguity where a non-overloaded character code was used as a separator.
The problem with both \ and . is significantly reduced in a GUI environment where you point and select, rather that typing the full path. A file name being edited in an entry file dedicated to a file name also handles spaces without requiring quoting\escaping.
Holding up *nix file naming rules as some sort of ideal, "Why would anyone ever dream of doing anything differently??", is in my eyes rather crazy. It satisfied engineers of the 1960s and 70s, by not everyday, non-technical users of the 2020s. Not by far!
|
|
|
|
|
|