libcurl error with 32bit computer when using CURLINFO_COOKIELIST - c#

Objective
What I am trying to do is to make a dll from C++(Visual Studio 2012) and call that from C# winform(Visual Studio 2012).
C++ dll is called "Helper.dll".
C# winform is called "WinFormTest.exe".
C++ dll simply connects to a certain web site, and return the html code with cookie string.
Here is my C# code.
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WinFormTest
{
public partial class Form1 : Form
{
[DllImport(#"Helper.dll", EntryPoint="CallTest", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string CallTest(string Url, StringBuilder Cookies);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetDllDirectory(string path);
enum PlatformTarget { x64, x86 }
PlatformTarget _platformTarget;
string _assPath;
public Form1()
{
InitializeComponent();
_platformTarget = IntPtr.Size == 8 ? PlatformTarget.x64 : PlatformTarget.x86;
_assPath = AppDomain.CurrentDomain.BaseDirectory;
_assPath = Path.Combine(_assPath, _platformTarget.ToString());
bool ok = SetDllDirectory(_assPath);
if (!ok)
{
throw new System.ComponentModel.Win32Exception();
}
}
private void button1_Click(object sender, EventArgs e)
{
StringBuilder _cookies = new StringBuilder();
string html = CallTest("http://www.google.com", _cookies);
}
}
}
There is no big issue with my C# code.
Here is C++ code.
PlusPlusClass.cpp
#include "stdafx.h"
#include "PlusPlusClass.h"
#include "Scraper.h"
#include <vector>
#include <boost/algorithm/string.hpp>
using namespace std;
extern "C" __declspec(dllexport) BSTR CallTest(const char* Url, char Cookies[])
{
std::string html;
try
{
CPlusPlusClass* cpc = new CPlusPlusClass();
std::map<std::string, std::string> cookies;
html = cpc->GetCookies(Url, cookies);
std::string cookieData;
for (std::map<std::string, std::string>::iterator iterator = cookies.begin(); iterator != cookies.end(); iterator++)
{
cookieData += iterator->first;
cookieData += "=";
cookieData += iterator->second;
cookieData += ";";
}
sprintf(Cookies, cookieData.c_str(), 0);
delete cpc;
}
catch (...)
{
}
return ::SysAllocString(CComBSTR(html.c_str()).Detach());
}
Scraper* class2;
CPlusPlusClass::CPlusPlusClass(void)
{
class2 = new Scraper();
}
CPlusPlusClass::~CPlusPlusClass(void)
{
delete class2; --> here, I get the error. The error just kills my C# application.
}
std::string CPlusPlusClass::GetCookies(const char* Url, std::map<std::string, std::string>& Cookies)
{
return class2->DoSomething(Url, Cookies);
}
Here is Scraper.cpp.
Scraper.cpp
#include "stdafx.h"
#include "Scraper.h"
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <curl/easy.h>
// split
#include <vector>
#include <boost/algorithm/string.hpp>
Scraper::Scraper(void)
{
}
Scraper::~Scraper(void)
{
}
std::string Scraper::DoSomething(const char* Url, std::map<std::string, std::string>& Cookies)
{
std::string _retValue;
curl_global_init( CURL_GLOBAL_ALL );
CURL* curl;
CURLcode res;
struct curl_slist* headerlist = NULL;
curl = curl_easy_init();
if (curl)
{
headerlist = curl_slist_append(headerlist, "Accept: */*");
headerlist = curl_slist_append(headerlist, "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
curl_easy_setopt(curl, CURLOPT_URL, Url);
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &_retValue);
res = curl_easy_perform(curl);
// receive cookies
struct curl_slist* cookies = NULL;
struct curl_slist* nc;
CURLcode ret = curl_easy_getinfo(curl, CURLINFO_COOKIELIST, &cookies);
nc = cookies;
while (nc)
{
std::vector<std::string> split_vector;
boost::split(split_vector, nc->data, boost::is_any_of("\t"));
Cookies.insert( std::pair<std::string, std::string>(split_vector[5], split_vector[6]) ); --> here is the problem, I think.
nc = nc->next;
}
// clean up
curl_slist_free_all(cookies);
curl_easy_cleanup(curl);
curl_slist_free_all(headerlist);
}
curl_global_cleanup();
return _retValue;
}
std::string Scraper::DoSomething2(const char* Url, std::map<std::string, std::string>& Cookies)
{
std::string temp = "some value is not good enough";
std::vector<std::string> split_vector;
boost::split(split_vector, temp, boost::is_any_of("\t"));
Cookies.insert( std::pair<std::string, std::string>(split_vector[0], split_vector[1]) );
return "this function is okay";
}
It's just simple curl example to connect to a certain web site.
But from C#, I have to retrieve cookies as well as html, so I am passing StringBuilder to C++.
So, C++ can write cookie name and value to that StringBuilder.
Actually this codes work great in 64bit.
But if I change it into 32bit(in C# winform property), C++ gives me error in CPlusPlusClass::~CPlusPlusClass(void).
When I try to delete class2 which is Scraper class, it just kills my C# application, so I cannot even trace the error.
I am specious about Scraper.cpp when getting the cookie from curl CURLINFO_COOKIELIST.
And write them to char array which was referenced from C# StringBuilder.
So, I wrote down DoSomething2 function in Scraper.cpp.
Now, it works even when I am deleting class in 32bit or 64bit.
Does anyone have any idea about this problem?

By somehow it was fixed.
I don't know very much about the mechanism x86 nor x64, but it seems that the basic capacity are different.
I edited to fix the size of StringBuilder when I first start from C#.
StringBuilder _cookies = new StringBuilder();
to
StringBuilder _cookies = new StringBuilder(1024);
And now, I don't see that error again in 32bit computers.
Happy coding!!

Related

Retrieve wchar_t* from C++ to C++/CLI to C#

I have the following C++ function from the MyCppLib.h file of a third party library:
int CppFunc(int Index, wchar_t* String, int StringLength);
where the maximum length of String is StringLength. String is returned by the function (out).
I am trying to wrap this in C++/CLI for use in C#. According to this page it looks like StringBuilder should be used to marshall wchar_t* back to C++/CLI.
So I believe the C++/CLI should look something like this:
#include "MyCppLib.h"
using namespace System;
using namespace System::Text;
namespace MyCppCliLib
{
int CppCliFunc(int Index, StringBuilder^ sB, int StringLength) {
return ::CppFunc(Index, ???, StringLength);
}
}
Now, how can I retrieve the wchar_t* from C++ back to StringBuilder^ ? I can't find a good example on the topic...
I believe the C# side would then looks like:
StringBuilder sB = new StringBuilder();
int x = CppCliFunc(2, sB, 4096);
EDITS:
Thanks to all the comments, I finally got something working, it looks like this (don't quote me on it, it's my first steps in C++/CLI):
#include "MyCppLib.h"
#include <string.h>
#include <stdlib.h>
using namespace System;
using namespace System::Text;
namespace MyCppCliLib
{
String^ CppCliFunc(int Index, int StringLength) {
String^ newString="";
wchar_t* wchar = (wchar_t*)calloc(StringLength, sizeof(wchar_t));
if (wchar != NULL) {
int errorInt = ::CppFunc(Index, wchar, StringLength);
newString = gcnew String(wchar);
if(errorInt != 0){
throw gcnew Exception("Error #" + errorInt.ToString());
}
}else{
throw gcnew Exception("Error wchar is null");
}
free(wchar);
return newString;
}
}

How could I convert this to c++

I've written c# code for a registry modifying application, but the problem is c# is easily reversible and obfuscators make the program look like malware. So my problem is that I can't find anything close to Registry.SetValue for c++. Any help is appreciated.
I've tried using a c# to c++ tool by tangible but it was bad and it didn't work as expected at all.
I've also tried RegSetValueEx but I think I used it incorrectly.
This is what I tried:
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <Windows.h>
using namespace std;
int main()
{
HKEY key;
if (RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows\\CurrentVersion"), &key) != ERROR_SUCCESS)
{
cout << "unable to open registry";
}
if (RegSetValueEx(key, TEXT("value_name"), 0, REG_SZ, (LPBYTE)"value_data", strlen("value_data")*sizeof(char)) != ERROR_SUCCESS)
{
RegCloseKey(key);
cout << "Unable to set registry value value_name";
}
else
{
cout << "value_name was set" << endl;
}
return 0;
}
Could someone explain what is value_name value_data and how to use it with an example as that is the main thing I'm confused with.
Your best bet is to use RegSetValueEx - all C++ wrappers for the registry are very thin wrappers around the simple C API.
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <Windows.h>
#include <tchar.h>
using namespace std;
int main()
{
HKEY key;
RegOpenKey(HKEY_CLASSES_ROOT, TEXT(""), &key);
LPCTSTR value = TEXT("");
LPCTSTR data = TEXT("");
LONG setRes = RegSetValueEx(key, value, 0, REG_SZ, (LPBYTE)data, _tcslen(data) * sizeof(TCHAR));
RegCloseKey(key);
return 0;
}
this solved it

RunTime Error in Unity when I'm using a function from a C++ Dll

I'm using a Dll created from a C++ file. When I either put the .dll and .lib files in my Unity-project folder or when I use the function that I need, Unity crashes and I can't open the project untile I remove the .dll or delete the function from the c# script.
This function works well on C++, both in Visual Studio and in Dev-C++
PS: Assets/alzBraccioCorretto.json is the file that I need to read
I've tried the same procedure for more simple dlls and it worked fine, so I don't know what I'm missing with this one.
In the Unity script I wrote
[DllImport("QuartaLibreria.dll", CharSet = CharSet.Unicode)]
static extern int LeggiFile(string nomeFile);
Text testo;
unsafe void Start()
{
testo = GetComponent<Text>();
int temp = 0;
temp = LeggiFile("Assets/alzBraccioCorretto.json");
testo.text = temp.ToString();
}
In the header of the library I have
#define QUARTALIBRERIA_API __declspec(dllexport)
//all the other headers and #include
int converti_int(string frame);//returns reads a line and returns an int
int leggiFile(string nomeFile);
extern "C" {
QUARTALIBRERIA_API int LeggiFile(wstring nomeFile);
}
In the cpp of the library I have
int leggiFile(string nomeFile) {
ifstream _stream(nomeFile.c_str());
int temp;
_stream >> temp >> temp >> temp >> temp >> temp;
return temp;
}
QUARTALIBRERIA_API int LeggiFile(wstring nomeFile){
std::string str = std::string(nomeFile.begin(), nomeFile.end());
int daRit = leggiFile(str);
return daRit;
}

Return value of SHLoadIndirectString is an errorcode

Hi I've been trying to get name of the metro app with the acquired from AppManifest.xml of the respective app. Came to know that SHLoadIndirectString could be used for this purpose. On checking its functionality manually, I couldn't get the result resource. The code snippet goes as below.
#include <iostream>
using namespace std;
#include <Shlwapi.h>
int main(){
LPWSTR output = L"";
LPWSTR input = L"#{Microsoft.BingMaps_2.1.3230.2048_x64__8wekyb3d8bbwe?ms-resource://Microsoft.BingMaps/resources/AppDisplayName}";
int result = SHLoadIndirectString(input, output, sizeof(output), NULL );
cout<<output;
return 0;
}
The return value "result" is always a negative value(changes if I am changing the input string respective to app). Please guide me on my mistake. Thanks.
Got the right answer.
#include <iostream>
using namespace std;
#include <Shlwapi.h>
int main()
{
PWSTR output = (PWSTR) malloc(sizeof(WCHAR)*256);
PCWSTR input = L"#{C:\\Program Files\\WindowsApps\\Microsoft.BingMaps_2.1.3230.2048_x64__8wekyb3d8bbwe\\resources.pri?ms-resource://Microsoft.BingMaps/Resources/AppShortDisplayName}";
int result = SHLoadIndirectString(input, output, 256, NULL );
cout<<output;
return 0;
}

Open a URL in a new browser process

I need to open a URL in a new browser process. I need to be notified when that browser process quits. The code I'm currently using is the following:
Process browser = new Process();
browser.EnableRaisingEvents = true;
browser.StartInfo.Arguments = url;
browser.StartInfo.FileName = "iexplore";
browser.Exited += new EventHandler(browser_Exited);
browser.Start();
Clearly, this won't due because the "FileName" is fixed to iexplore, not the user's default web browser. How do I figure out what the user's default web browser is?
I'm running on Vista->forward. Though XP would be nice to support if possible.
A bit more context: I've created a very small stand-alone web server that serves some files off a local disk. At the end of starting up the server I want to start the browser. Once the user is done and closes the browser I'd like to quit the web server. The above code works perfectly, other than using only IE.
Thanks in advance!
Ok. I now have working C# code to do what I want. This will return the "command line" you should run to load the current default browser:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Linq;
using System.Text;
namespace testDefaultBrowser
{
public enum ASSOCIATIONLEVEL
{
AL_MACHINE,
AL_EFFECTIVE,
AL_USER,
};
public enum ASSOCIATIONTYPE
{
AT_FILEEXTENSION,
AT_URLPROTOCOL,
AT_STARTMENUCLIENT,
AT_MIMETYPE,
};
[Guid("4e530b0a-e611-4c77-a3ac-9031d022281b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IApplicationAssociationRegistration
{
void QueryCurrentDefault([In, MarshalAs(UnmanagedType.LPWStr)] string pszQuery,
[In, MarshalAs(UnmanagedType.I4)] ASSOCIATIONTYPE atQueryType,
[In, MarshalAs(UnmanagedType.I4)] ASSOCIATIONLEVEL alQueryLevel,
[Out, MarshalAs(UnmanagedType.LPWStr)] out string ppszAssociation);
void QueryAppIsDefault(
[In, MarshalAs(UnmanagedType.LPWStr)] string pszQuery,
[In] ASSOCIATIONTYPE atQueryType,
[In] ASSOCIATIONLEVEL alQueryLevel,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName,
[Out] out bool pfDefault);
void QueryAppIsDefaultAll(
[In] ASSOCIATIONLEVEL alQueryLevel,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName,
[Out] out bool pfDefault);
void SetAppAsDefault(
[In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszSet,
[In] ASSOCIATIONTYPE atSetType);
void SetAppAsDefaultAll(
[In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName);
void ClearUserAssociations();
}
[ComImport, Guid("591209c7-767b-42b2-9fba-44ee4615f2c7")]//
class ApplicationAssociationRegistration
{
}
class Program
{
static void Main(string[] args)
{
IApplicationAssociationRegistration reg =
(IApplicationAssociationRegistration) new ApplicationAssociationRegistration();
string progID;
reg.QueryCurrentDefault(".txt",
ASSOCIATIONTYPE.AT_FILEEXTENSION,
ASSOCIATIONLEVEL.AL_EFFECTIVE,
out progID);
Console.WriteLine(progID);
reg.QueryCurrentDefault("http",
ASSOCIATIONTYPE.AT_URLPROTOCOL,
ASSOCIATIONLEVEL.AL_EFFECTIVE,
out progID);
Console.WriteLine(progID);
}
}
}
Whew! Thanks everyone for help in pushing me towards the right answer!
If you pass a path of the known file type to the (file) explorer application, it will 'do the right thing', e.g.
Process.Start("explorer.exe", #"\\path.to\filename.pdf");
and open the file in the PDF reader.
But if you try the same thing with a URL, e.g.
Process.Start("explorer.exe", #"http://www.stackoverflow.com/");
it fires up IE (which isn't the default browser on my machine).
I know doesn't answer the question, but I thought it was an interesting sidenote.
The way to determine the default browser is explained in this blog post:
http://ryanfarley.com/blog/archive/2004/05/16/649.aspx
From the blog post above:
private string getDefaultBrowser()
{
string browser = string.Empty;
RegistryKey key = null;
try
{
key = Registry.ClassesRoot.OpenSubKey(#"HTTP\shell\open\command", false);
//trim off quotes
browser = key.GetValue(null).ToString().ToLower().Replace("\"", "");
if (!browser.EndsWith("exe"))
{
//get rid of everything after the ".exe"
browser = browser.Substring(0, browser.LastIndexOf(".exe")+4);
}
}
finally
{
if (key != null) key.Close();
}
return browser;
}
Ok, I think I might have found it - IApplicationAssociationRegistration::QueryCurrentDefault [1]. According to the docs this is what is used by ShellExecute. I'll post code when I get it to work, but I'd be interested if others think this is the right thing to use (BTW, I'm Vista or greater for OS level).
[1]: http://msdn.microsoft.com/en-us/library/bb776336(VS.85).aspx QueryCurrentDefault
Ok. Been away on the conference circuit for a week, now getting back to this. I can do this with C++ now - and it even seems to behave properly! My attempts to translate this into C# (or .NET) have all failed however (Post On Question).
Here is the C++ code for others that stumble on this question:
#include "stdafx.h"
#include <iostream>
#include <shobjidl.h>
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
#include <atlbase.h>
#include <atlstr.h>
#include <AtlDef.h>
#include <AtlConv.h>
using namespace std;
using namespace ATL;
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr = CoInitialize(NULL);
if (!SUCCEEDED(hr)) {
cout << "Failed to init COM instance" << endl;
cout << hr << endl;
}
IApplicationAssociationRegistration *pAAR;
hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
NULL, CLSCTX_INPROC, __uuidof(IApplicationAssociationRegistration),
(void**) &pAAR);
if (!SUCCEEDED(hr))
{
cout << "Failed to create COM object" << endl;
cout << hr << endl;
return 0;
}
LPWSTR progID;
//wchar_t *ttype = ".txt";
hr = pAAR->QueryCurrentDefault (L".txt", AT_FILEEXTENSION, AL_EFFECTIVE, &progID);
if (!SUCCEEDED(hr)) {
cout << "Failed to query default for .txt" << endl;
cout << hr << endl;
}
CW2A myprogID (progID);
cout << "Result is: " << static_cast<const char*>(myprogID) << endl;
/// Now for http
hr = pAAR->QueryCurrentDefault (L"http", AT_URLPROTOCOL, AL_EFFECTIVE, &progID);
if (!SUCCEEDED(hr)) {
cout << "Failed to query default for http" << endl;
cout << hr << endl;
}
CW2A myprogID1 (progID);
cout << "Result is: " << static_cast<const char*>(myprogID1) << endl;
return 0;
}
I will post the C# code when I finally get it working!
I've written this code for a project once... it keeps in mind any additional parameters set for the default browser. It was originally created to open HTML documentation in a browser, for the simple reason I always set my default program for HTML to an editor rather than a browser, and it annoys me to no end to see some program open its HTML readme in my text editor. Obviously, it works perfectly for URLs too.
/// <summary>
/// Opens a local file or url in the default web browser.
/// </summary>
/// <param name="path">Path of the local file or url</param>
public static void openInDefaultBrowser(String pathOrUrl)
{
pathOrUrl = "\"" + pathOrUrl.Trim('"') + "\"";
RegistryKey defBrowserKey = Registry.ClassesRoot.OpenSubKey(#"http\shell\open\command");
if (defBrowserKey != null && defBrowserKey.ValueCount > 0 && defBrowserKey.GetValue("") != null)
{
String defBrowser = (String)defBrowserKey.GetValue("");
if (defBrowser.Contains("%1"))
{
defBrowser = defBrowser.Replace("%1", pathOrUrl);
}
else
{
defBrowser += " " + pathOrUrl;
}
String defBrowserProcess;
String defBrowserArgs;
if (defBrowser[0] == '"')
{
defBrowserProcess = defBrowser.Substring(0, defBrowser.Substring(1).IndexOf('"') + 2).Trim();
defBrowserArgs = defBrowser.Substring(defBrowser.Substring(1).IndexOf('"') + 2).TrimStart();
}
else
{
defBrowserProcess = defBrowser.Substring(0, defBrowser.IndexOf(" ")).Trim();
defBrowserArgs = defBrowser.Substring(defBrowser.IndexOf(" ")).Trim();
}
if (new FileInfo(defBrowserProcess.Trim('"')).Exists)
Process.Start(defBrowserProcess, defBrowserArgs);
}
}
Short answer, you can't.
If the default browser is, say, Firefox, and the user already has a Firefox instance running, it will just be opened in another window or tab of the same firefox.exe process, and even after they close your page, the process won't exit until they close every window and tab. In this case, you would receive notification of the process exiting as soon as you started it, due to the temporary firefox.exe proc that would marshal the URL to the current process. (Assuming that's how Firefox's single instance management works).

Categories