I am trying to build a COM Library in C++, using a C# project for testing. Some methods need to return strings to the caller. On calling these methods from C# I get this: "Access violation reading at location ..."
This is the C++ code from my testproject (apart from all the stuff generated by VS 2010 ATL)
//COMTest.idl
[id(1)] HRESULT Test([out,retval] BSTR* ret);
//Program2.h
STDMETHOD(Test)(BSTR* ret);
//Program2.cpp
STDMETHODIMP CProgram2::Test(BSTR* ret)
{
BSTR tmp = (BSTR)CoTaskMemAlloc(sizeof(wchar_t) * 2);
tmp[0] = L'H';
tmp[1] = L'\0';
*ret = (BSTR)tmp;
return S_OK;
}
In C# I just referenced the DLL from the COM-Tab, turned "Embed Interop Types" off, because it caused errors, and ran this:
static void Main(string[] args)
{
COMTestLib.Program2Class instance = new COMTestLib.Program2Class();
string tmp = instance.Test(); //Where the error occurs
Console.WriteLine(tmp); //This is not reached
Console.Read();
}
The error occurs after leaving the Test-Method. I debugged the C++ code from within my C# project and the values are placed in the correct locations. I do not get the error if I try to return 0 (gives null in C#), even if I still allocate memory like in the example.
I can not make sense of the address, which the access violation complains about. It is neither the address I am allocating nor any other address used in the method. What also seems weird to me is that the CoTaskMemAlloc-Function always returns addresses with the first byte set to zero (0x00XXXXXX), but that might just be a COM thing.
I ran out of ideas and I cant find much information on this (except for basic COM tutorials) anywhere. Can anyone help?
BSTRs require extra memory (to keep track of the string len) so must use SysAllocString() function to allocate BSTRs (or use one of the "smart" BSTR classes).
So your original code should read like:
//Program2.cpp
STDMETHODIMP CProgram2::Test(BSTR* ret)
{
*ret = SysAllocString(L"H");
return S_OK;
}
A good reading about BSTRs: http://blogs.msdn.com/b/ericlippert/archive/2003/09/12/52976.aspx
Check that your COM project and test project are both STA. Check the bitness too. What if you replace BSTR by LPSTR ?
Related
I want to pass a string back and forth between C++ (.so) and C# on Linux. However, I can't really find an example of using PInvoke on Linux (most search results of PInvoke are on Windows). Thus, what is the best way to do this on Linux?
I have tried the plain char* solution, but it doesn't work. I also don't know if it is safe to do it this way, since it is marshaling between un-managed and managed code.
C++
//NativeSO
extern "C" const char* IO_String(const char *message)
{
// I know it is "const char*", but I just want
// to express what I want to achieve with this function
message = "I changed a C# string";
return message;
}
C#
[DllImport("NativeSO")] //let's assume the file name & path are correct
static extern string IO_String(string message);
...
public void test()
{
string target = IO_String("hi");
bool isTrue = (target == "I changed a C# string");
print(isTrue); //expect to be true
}
I'm expecting the print(isTrue) to be true in C#, but it is not working. The above code's main purpose is to demonstrate the goals that I'm trying to achieve.
In my Visual Studio extension, I'm going to read the text that is in the navigation bar. Therefore I listen to window created events and from the IVsCodeWindow object I get the IVsDropdownBar to get the current selection in the dropdown bar. This works fine. Then I'm using the following code snippet to extract the text of the current selection:
string text;
barClient.GetEntryText(MembersDropdown, curSelection, out text);
if (hr == VSConstants.S_OK)
{
Debug.WriteLine("Text: " + text);
} else {
Debug.WriteLine("No text found!");
}
However, this does not work. My extension crashes with an unhandled exception in the second line of the code snippet. I read the documentation and could find the following note:
The text buffer returned in ppszText is typically created by the
IVsDropdownBarClient object and the buffer must persist for the life
of the IVsDropdownBarClient object. If you are implementing this
interface in managed code and you need to have the string disposed of
by the caller, implement the IVsCoTaskMemFreeMyStrings interface on
the IVsDropdownBarClient interface.
I assume that this is part of my problem, but I can't really understand what I have to change in my code to get it working. Any hints?
I'm pretty sure now that the Visual Studio SDK Interop DLLs have the wrong marshalling information for IVsDropDownbarClient.GetEntryText and that there's no way to call that method using that interface.
The best workaround I've found so far is:
Use the tlbimp tool to generate an alternate Interop DLL for textmgr. (You can safely ignore the dozens of warnings including the one about GetTextEntry.)
tlbimp "c:\Program Files (x86)\Microsoft Visual Studio 11.0\VSSDK\VisualStudioIntegration\Common\Inc\textmgr.tlb"
(Optional) If you're using source control, you'll probably want to copy the resulting file (TextManagerInternal.dll) to a subdirectory of your extension project and check it in as an external dependency.
In your Visual Studio extension project, add a reference to the file (TextManagerInternal.dll).
Add the following method that should properly handle the string marshalling.
static public string HackHackGetEntryText(IVsDropdownBarClient client, int iCombo, int iIndex)
{
TextManagerInternal.IVsDropdownBarClient hackHackClient = (TextManagerInternal.IVsDropdownBarClient) client;
string szText = null;
IntPtr ppszText = IntPtr.Zero;
try
{
ppszText = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(IntPtr)));
if(ppszText == IntPtr.Zero)
throw new Exception("Unable to allocate memory for IVsDropDownBarClient.GetTextEntry string marshalling.");
hackHackClient.GetEntryText(iCombo, iIndex, ppszText);
IntPtr pszText = Marshal.ReadIntPtr(ppszText);
szText = Marshal.PtrToStringUni(pszText);
}
finally
{
if(ppszText != IntPtr.Zero)
Marshal.FreeCoTaskMem(ppszText);
}
return szText;
}
}
I am using some matlab functions in c#. When I convert a function to dll, I have two dll. One is Entropy.dll and other one is EntropyNative.dll.
If I use the Entropy.dll I must install the matlab compiler runtime (MCR) to the target computer because it needs MWArray class and it only works with MCR. However I do not want to install MCR to target computer so I have tried to use the EntropyNative.dll because it takes Object instead MWArray as parameter. Thus I do not need to install MCR to each computer. However when I use the native dll, MWMCR:EvaluateFunction error. occurs. That means out of memory exception. File size and functions are same but why this error occurs when I use the native dll?
This (Entropy.dll) one works and error not occurs.
MWNumericArray arr1 = null;
Entropy ent = new Entropy(); //Matlab function.
while(true)
{
double[] arraySegment = new double[window];
arr1=arraySegment;
MWArray function1Result = ent.function1(arr1);
MWArray function2Result = ent.function2(arr1);
arr1=null;
}
If I use EntropyNative.dll out of memory error occurs
Entropy ent = new Entropy();
while(true)
{
double[] arraySegment = new double[window];
double function1Result = ent.function1(arraySegment);
double function2Result = ent.function2(arraySegment);
}
I want to use Native dll so how can I eliminate this error? I tried to force GarbageCollector but it does not help me. Also I have tried ent.WaitForFiguresToDie() and ent.Dispose().
ent.WaitForFiguresToDie():
This method will cause a MATLAB figure window to behave as a modal
dialog box. The method will not return until all the figure windows
associated with this component have been closed.
I have a C# WinForms .NET 3.5 application that I need to call a COM DLL written in Visual FoxPro 7. I have added the COM object to the C# project fine, and can
view the objects and its members in the object browser fine.
However when I attempt to call any methods or access any properties from the COM object I get the following exception thrown :
System.AccessViolationException was unhandled
Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Source=Interop.emscosting
StackTrace:
at emscosting.ComBaseClass.ShellExecute(String tcItem, String tcAction)
...
Below is some example C# code I am using :
emscosting.ComBaseClass com = new emscosting.ComBaseClass();
com.ShellExecute(file, "TRUE"); <--- The exception is thrown here
Also when I try and access any of the public properties of the COM object in the debugger watch window I get the following appear in the Value column :
'com.DatabaseUserName' threw an exception of type 'System.AccessViolationException'
I am already using this COM object without any issues from other VFP applications, Office applications, and in Javascript/Classic ASP.
Can someone help me resolve this issue please ?
I do have the original VFP source code for the COM still and below is a snippet of the code where I am declaring the public properties, however I am trying to
avoid rewriting the COM DLL if possible!
#If BUILD_AS_COM_OBJECT
Define Class emsCosting As Combase OlePublic
#Else
Define Class emsCosting As Combase
#Endif
CostingsPath = COSTINGS_PATH
Dimension CostingsPath_COMATTRIB[5]
CostingsPath_COMATTRIB[1] = COMATTRIB_NONE
CostingsPath_COMATTRIB[2] = "Contains the path of where the costings get saved" && HelpString
CostingsPath_COMATTRIB[3] = "CostingsPath" && Capitalisation
CostingsPath_COMATTRIB[4] = "String" && Property Type
CostingsPath_COMATTRIB[5] = 0 && Number of parameters
CostingsMaster = COSTINGS_MASTER
Dimension CostingsMaster_COMATTRIB[5]
CostingsMaster_COMATTRIB[1] = COMATTRIB_NONE
CostingsMaster_COMATTRIB[2] = "Contains the filename of the costings master (usually costings.xls)" && HelpString
CostingsMaster_COMATTRIB[3] = "CostingsMaster" && Capitalisation
CostingsMaster_COMATTRIB[4] = "String" && Property Type
CostingsMaster_COMATTRIB[5] = 0 && Number of parameters
Function SetCostingsPath(tcPath As String) As VOID HelpString "This is a test function"
CostingsPath = tcPath
EndFunc
Function TestFunctionNoParam() AS String HelpString "This is a helpstring"
Return "\\TEST\ContractReviewCostings\"
EndFunc
......
NOTE: Both the two test functions defined above fail with the exception System.AccessViolationException
You have to compile your .Net Application for x86, if you have a x64 system.
When you compile it with "Any CPU" or "x64", you can not access the x32 FoxPro-DLL.
I have created a C# assembly that does 3DES encryption/encryption and tested it. I now need to decrypt the data on a remote machine for an install. .NET is not guaranteed to be present when my native process runs, so I need to decrypt it using Win32 C++ methods. This is for a commercial applicaiton, so third party libraries are going to need to flexible with their licensing. I would prefer a simple example to get me started. Most of the examples I have found so far require importing session keys. I'm not using those. I am encrypting on machineA with .NET 2.0, and passing over to machineB where I will retrive the key and decrypt with native Win32 API's. Can anyone point me in the right direction with some examples?
I know I need to start with CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFY_CONTEXT).
However, the next step appears to be import key and it looks like it requires (http://support.microsoft.com/kb/228786). Is this correct, or am I making this too difficult. I have a basic understanding of encryption. Thanks in advance!
Take a look to the following code:
#define TRIPLEDES_KEYSIZE 24
#define TRIPLEDES_BLOCKSIZE 8
...
BYTE key[TRIPLEDES_KEYSIZE] = { ... };
...
HCRYPTKEY hKey;
typedef struct
{
BLOBHEADER hdr;
DWORD cbKeySize;
BYTE rgbKeyData [TRIPLEDES_KEYSIZE];
} KEYBLOB;
KEYBLOB keyBlob;
memset(&keyBlob, 0, sizeof(keyBlob));
keyBlob.cbKeySize = TRIPLEDES_KEYSIZE;
keyBlob.hdr.bType = PLAINTEXTKEYBLOB;
keyBlob.hdr.bVersion = CUR_BLOB_VERSION;
keyBlob.hdr.aiKeyAlg = CALG_3DES;
memcpy(keyBlob.rgbKeyData, key, TRIPLEDES_KEYSIZE);
BOOL res = CryptImportKey(hCryptProv, (const BYTE*)&keyBlob, sizeof(keyBlob), 0, 0, &hKey);
if (res)
{
res = CryptSetKeyParam(hKey, KP_MODE, CRYPT_MODE_ECB, 0);
Please note you can use CRYPT_MODE_ECB or CRYPT_MODE_CBC in the call to the function CryptSetKeyParam with KP_MODE option depending on what you want to do. You can set an IV by for example the following code
res = CryptSetKeyParam(hKey, KP_IV, iv, 0);
which makes only sense in a CRYPT_MODE_CBC like mode.
Please note there is also a different 3DES mode (CALG_3DES_112) working with only 112 Bit key (i.e. with two normal DES keys). You have to modify the code if you want to use this mode.
Edit:
You should write some classes in C++ to manage all things of the CryptoApi. It will save you a lot of headache.