How to use the WinRM C++ API in a simple example - c++

why is my code failing to run a simple executable using WinRM's C++ API?
//main.cpp
int main()
{
ShellClient *shellClient = new ShellClient();
//Set up the shell client here and connect to the localhost.
//This seems to be working fine because I'm handling every
//possible error code, and none of them are being triggered
PCWSTR commandLine = L"\"MyExampleExecutable.exe\"";
isOk = shellClient->RunCommand(commandLine);
if (!isOk)
return 1;
return 0;
}
//ShellClient.cpp
bool ShellClient::RunCommand(PCWSTR command)
{
WSMAN_SHELL_ASYNC createCommandAsync;
ZeroMemory(&createCommandAsync, sizeof(createCommandAsync));
createCommandAsync.operationContext = this;
createCommandAsync.completionFunction = (WSMAN_SHELL_COMPLETION_FUNCTION)CommandCreatedCallback;
WSManRunShellCommand(shellHandle, 0, command, NULL, NULL, &createCommandAsync, &commandHandle);
if (commandHandle == NULL)//It is *always* NULL
{
std::cout << "command handle null" << std::endl;
system("pause");
return false;
}
return true;
}
One possible clue is that my C++ code thinks the shell gets created fine, but in the Event Viewer for my machine, there is this:
WSMan operation CreateShell failed, error code 2150859250
At the time of writing, this lovely error code gives precisely zero results when put into Google, making it rather difficult to know what it means.
Background and common solutions which I have already checked
As documented here and explaned in this video by the same author, most WinRM issues boil down to either connection or authentication problems. In my case, if I deliberately enter incorrect user credentials, I get an authentication error, so I know that my program is connecting and authenticating fine when the correct username and password are supplied. Also:
From the command line, I can connect to my local machine and pretend it's a remote server, for example the following command works fine:
winrs -r:http://localhost:5985 -u:COMPUTERNAME\Jeremy "dir"
winrm quickconfig shows the service is working (which we already know otherwise the winrs command wouldn't work)
winrm get winrm/config shows TrustedHosts = localhost, AllowUnencrypted = true, and all authentication methods are set to true
Following this advice, I have set the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\LocalAccountTokenFilterPolicy = 1
Working in Windows 10
Thank you in advance!

I wasn't aware of this so can't comment on whether it's common knowledge but Microsoft have a nifty error lookup tool where you can enter an error code (after converting it from a normal number to hexadecimal) and it tells you what it means.
In this case, 2150859250 (803381F2 in hex) corresponds to:
ERROR_WINRS_IDLETIMEOUT_OUTOFBOUNDS wsmerror.h
# The WS-Management service cannot process the request. The
# requested IdleTimeout is outside the allowed range.
When setting up the WinRM shell, I was doing the following:
WSMAN_SHELL_STARTUP_INFO startupInfo;
ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.idleTimeoutMs = 1000;//not large enough!
startupInfo.workingDirectory = L"C:\\";
//other parameters of startupInfo set here
WSManCreateShell(session, 0, shellUri, &startupInfo, NULL, NULL, &createShellAsync, &shellHandle);
Changing idleTimeoutMs from 1000 to a much larger number like 100000 solved the error and my program now works fine.
Since the official docs for this parameter say anything between 0 and 0xFFFFFFFF are valid, it remains a mystery why a value of 1000 is throwing this error. I leave this for somebody more knowledgable than myself to answer, on the off chance that they come across this question.

Related

Setting useUnsafeHeaderParsing for C++ WinHttp

I'm trying to reach a web page on an embedded device.
I'm using WinHttp on Win32.
When trying to read response I get error
ERROR_WINHTTP_INVALID_SERVER_RESPONSE
12152
The server response cannot be parsed.
But when I captured with WireShark I can see that response is coming.
So to test I wrote a simple C# program.
GetResponse was throwing exception
The server committed a protocol violation. Section=ResponseHeader
Detail=CR must be followed by LF
So according to below solution I set useUnsafeHeaderParsing to true. And it worked fine.
HttpWebRequestError: The server committed a protocol violation. Section=ResponseHeader Detail=CR must be followed by LF
Since I can't use C# I need to find a way to set useUnsafeHeaderParsing to true for WinHttp with win32 C++
Many thanks
I've briefly looked into the option flags of WinHttpSetOption and found the following entry:
WINHTTP_OPTION_UNSAFE_HEADER_BLOCKING
This option is reserved for internal use and should not be called.
Since the option looks linke an on/off switch I would try to do the following:
BOOL bResult;
BOOL bOption = FALSE;
bResult = WinHttpSetOption(hInternet,
WINHTTP_OPTION_UNSAFE_HEADER_BLOCKING,
&bOption,
sizeof(bOption));
if (bResult == FALSE)
{
/* handle error with GetLastError() */
}
Well but as MSDN says it's reserved for internal use and therefore the function may change in the future (or has already changed in the past). But it's worth a try... Good Luck!
Looks like the name of the option must have changed since then: with the current SDK it's WINHTTP_OPTION_UNSAFE_HEADER_PARSING. Also, I verified (by examining the Assembly code directly) that:
the option must be DWORD-sized
the value of the option doesn't matter, as long as it's nonzero
you can only enable unsafe parsing; trying to disable (by setting the option value to zero) causes an error to be returned
Obviously, since this undocumented, it's subject to change.

WinHttpDetectAutoProxyConfigUrl always fails with error code 12180 (ERROR_WINHTTP_AUTODETECTION_FAILED)

I'm trying get the proxy settings for my computer automatically.
I've set up a local server and I've uploaded a .pac file (which I can access from my browser) and I've set the link to it in the Internet Explorer connection settings, in the "address" field and checked "Use automatic configuration script".
My code is the following:
int main()
{
LPWSTR str = NULL;
if (!WinHttpDetectAutoProxyConfigUrl(WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A, &str))
{
printf("%d\n", GetLastError());
}
if(str)
GlobalFree(str);
return 0;
}
The function always fails and GetLastError returns 12180 (ERROR_WINHTTP_AUTODETECTION_FAILED)
What am I doing wrong?
From https://developer.appcelerator.com/question/120622/errorwinhttpautodetectionfailed:
This error message is not necessarily a problem and can be ignored if you are using direct connection. you get this error if you are having direct connection. to check that and get more info, you can use the following commands:
cd windows\system32
netsh winhttp help
— answered 4 years ago by Nick G
From your comment I gather that this was indeed the reason you were getting this error, so I'm posting it as an answer.

C++ and ADODB : Com error 0x800a0e78

I have a C++ library that calls a stored procedure in MSSQL database using ADODB. Everything works fine on development database. But on test database, I am getting Com error 0x800a0e78: This option is not allowed if an object is closed.
The code looks like
ADODB::_CommandPtr cmd = NULL;
ADODB::_RecordsetPtr rs = NULL;
CHECKHR(cmd.CreateInstance( __uuidof( ADODB::Command) ));
cmd->ActiveConnection = m_connexion;
cmd->CommandText = "my_stored_procedure";
cmd->CommandType = ADODB::adCmdStoredProc;
CHECKHR(cmd->Parameters->Refresh());
//input param
cmd->Parameters->GetItem(paramIndex)->put_Value(paramValue);
// output param
cmd->Parameters->GetItem(paramIndex)->PutDirection(ADODB::adParamOutput);
rs = cmd->Execute( NULL, NULL, ADODB::adCmdStoredProc);
while(! rs->ADO_EOF ) { ...
rs->ADO_EOF is where it crashes the program if I use the test database.
note: The stored procedure is same in both the databases and returns same data.
There is one more flow where another SP is called. It works well with the test database. The problem appears only with this particular SP.
I tend to think that it is not a code issue because it works with development database. But I can consistently reproduce the problem with test database.
Please suggest of next actions I should take to resolve this issue
UPDATE
Due to some miracle this C++ exception has gone away after 1 day. But it is so very slow that the execution almost always times out. I do not know how to justify this behavior
UPDATE: 2013-07-18
After so much time, the error has appeared again. This time with development DB with the same SP
[Com error 0x800a0e78 : Operation is not allowed when the object is closed.
on the same line
while(! rs->ADO_EOF ) {
in rs I can see a memory address pointing to ADODB recordset object. But rs->ADO_EOF is generating the said error

Interop exception with VC++ and MySQL C++ Connector

I am helping a friend with his bachelor thesis project. It is a program that calculates bending moments for various materials. One of the extra requirements that the client desires is database functionality to store and retrieve various pieces of data being used in the program.
The program is a forms application written in managed C++. I jumped on board to help with writing the database functionality. I am using MySQL Server 5.5 and the MySQL Connector/C++ to bridge the program and the database. Everything has been going pretty well and all the functionality we need works just fine, but only in debug. As soon as we put the program into release mode there is undefined behavior occurring at runtime. Below is the function that is used to open a connection to the database:
try
{
m_driver = get_driver_instance();
m_conn = m_driver->connect(m_dbHost, m_dbUser, m_dbPwd);
m_conn->setSchema(m_dbSchema);
}
catch(sql::SQLException &e)
{
int a = e.getErrorCode();
MessageBoxA(NULL, e.what(), "DB Error", MB_OK);
}
The values passed into the connect function are all std::string. In debug mode the connection is made with no issues. In release mode an exception is caught after the connect function is called, and displays the message "Unknown MySQL Server Host '####' (0)" where the #### is always some garbage text. I also notice that in the output window another exception is being thrown, this one is the type System.Runtime.InteropServices.SEHException.
I have been doing some research and have seen numerous cases of this exception on many forums (and here on stack exchange) but no one seems to be having this issue with the MySQL connector. My assumption is that the memory is being corrupted because the program is mixed mode, with the main program code being written in Managed C++ and my database helper code being in native C++ (as required by the connector).
Is there something I can change in my code to try and fix this issue to that the strings aren't being corrupted at run time. I have tried many different hacks to try and solve the problem but nothing has worked.
Thanks,
Tom
Update: I am now seeing this error in debug mode. I added code to retrieve values from the database and populate some text boxes on the form. The code is as follows:
// Populate the form with material details
String^ selectedMaterial = (String^)(comboBox1->SelectedItem);
string selectedMaterial_ = "";
MarshalString(selectedMaterial, selectedMaterial_);
sql::ResultSet* results = dbHelper.GetData("matname", selectedMaterial_);
if (results->rowsCount() == 1)
{
// Outdim
string outdim_ = "";
outdim_ = results->getString("outdim");
String^ outdim = gcnew String(outdim_.c_str());
textBox1->Text = outdim;
}
else
{
// !!!! Duplicate materials in list
}
When it tries to read outdim from the result set the SEHException is thrown, and the only other piece of information given is that it was thrown in an external component.
Update 2: I ran Application Verifier against the debug executable and then launched the program from VS2010. However the form window never loads so somewhere along the line the program must be getting halted. Strangely there is absolutely no information in the log files in Application Verifier. I also tried with the release version and I didnt get any useful information from that either.

First call to Windows Performance Counters (PDH) sometimes fails

I'm having a problem where sometimes my code will function correctly, but other times it will fail.
This is the first bit of PDH related code that I run:
const std::wstring pidWildcardPath = L"\\Process(*)\\ID Process";
DWORD bufferSize = 0;
LPTSTR paths = NULL;
PDH_STATUS status = PdhExpandCounterPath(
pidWildcardPath.c_str(),
paths,
&bufferSize);
checkPDHStatus(status, PDH_MORE_DATA, L"Expected request for more data.");
The result of the PdhExpandCounterPath function call is 0x800007D0 (PDH_CSTATUS_NO_MACHINE). The checkPDHStatus function is a simple function that I wrote that asserts that the status is equal to the second parameter. In this case, I expect the result to be PDH_MORE_DATA because paths is NULL and bufferSize is 0. The goal of this call is to determine the size of the buffer I must allocate to store all of the results for a subsequent call to PdhExpandCounterPath. This is described in the PDH documentation under the Remarks section.
The list of PDH error codes describes PDH_MORE_DATA as "Unable to connect to the specified computer, or the computer is offline." As you can see by the performance counter path in the code above, I am not even trying to connect to a different computer than my own.
It is interesting the way that this code fails. Sometimes it works fine and then other times, it will fail on multiple back-to-back executions of my application. I have #include <pdh.h> in my header file and I have a section in my property sheet for this DLL that looks like this:
<Tool
Name="VCLinkerTool"
AdditionalDependencies="pdh.lib"
/>
I'm not sure if it matters, but this program is built by Visual Studio 2005 and run on Windows XP. Am I doing something incorrectly?
I'm a co-worker of Dave's and have discovered the following during my investigation:
the code above runs fine when run from a logged-in interactive session
the code runs fine when initiated as a Scheduled Task AND the user is logged in at the time the scheduled task is fired off
the code FAILS only when run as a Scheduled Task AND the user is NOT logged in at the time the task starts
the code continues to fail if the user logs in after the failing task has started but while it is still running (because it is looping "endlessly" until it gets a PDH_MORE_DATA status back).
In the failing instances, the following environment variables have not been established/set for the program: APPDATA, HOMEDRIVE and HOMEPATH ... I don't think this is a problem. However, the failing program also lacks the SeCreateGlobalPrivilege from its token; the passing programs all have this privilege in the token and PERFMON shows it as "Default Enabled". The other difference is that failing program has the NT_AUTH\BATCH user group in the token, while the passing program has NT_AUTH\INTERACTIVE instead ... all other user groups and privileges are the same for both cases. I think the global privilege is coming from the interactive login, but don't know if it has any bearing on PDH operation.
I cannot find anything in the Performance Counter/PDH documentation that talks about needing any special permissions or privileges for this functionality to succeed. Is the global privilege required to use Performance Counters ?
Or is there some other context/environment difference between running Scheduled Tasks (as a specific user) when that user is/isn't logged in at the time the task starts, that would account for the PDH call succeeding/failing respectively ?
Try this format, indicating the local computer:
const std::wstring pidWildcardPath = L"\\.\Process(*)\ID Process";