I'm using an Intel Ivy Bridge CPU and want to use the RDRAND opcode (https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide) in C#.
How can I call this CPU instruction via C#? I've seen an example of executing assembly code from c# here: x86/x64 CPUID in C#
But I'm not sure how to use it for RDRAND. The code doesn't need to check whether the CPU executing the code supports the instruction or not.
I've seen this C++ example of executing assembly byte code coming from drng_samples of Intel:
int rdrand32_step (uint32_t *rand)
{
unsigned char ok;
/* rdrand edx */
asm volatile(".byte 0x0f,0xc7,0xf0; setc %1"
: "=a" (*rand), "=qm" (ok)
:
: "edx"
);
return ok;
}
How can the example of executing assembly code in C# be combined with the C++ code coming from the Intel drng sample code?
There are answers out on SO that will generate (unmanaged) assembly code at runtime for managed code to call back into. That's all very interesting, but I propose that you simply use C++/CLI for this purpose, because it was designed to simplify interop scenarios. Create a new Visual C++ CLR class library and give it a single rdrandwrapper.cpp:
#include <immintrin.h>
using namespace System;
namespace RdRandWrapper {
#pragma managed(push, off)
bool getRdRand(unsigned int* pv) {
const int max_rdrand_tries = 10;
for (int i = 0; i < max_rdrand_tries; ++i) {
if (_rdrand32_step(pv)) return true;
}
return false;
}
#pragma managed(pop)
public ref class RandomGeneratorError : Exception
{
public:
RandomGeneratorError() : Exception() {}
RandomGeneratorError(String^ message) : Exception(message) {}
};
public ref class RdRandom
{
public:
int Next() {
unsigned int v;
if (!getRdRand(&v)) {
throw gcnew RandomGeneratorError("Failed to get hardware RNG number.");
}
return v & 0x7fffffff;
}
};
}
This is a very bare-bones implementation that just tries to mimic Random.Next in getting a single non-negative random integer. Per the question, it does not attempt to verify that RDRAND is actually available on the CPU, but it does handle the case where the instruction is present but fails to work. (This "cannot happen" on current hardware unless it's broken, as detailed here.)
The resulting assembly is a mixed assembly that can be consumed by managed C# code. Make sure to compile your assembly as either x86 or x64, same as your unmanaged code (by default, projects are set to compile as "Any CPU", which will not work correctly since the unmanaged code has only one particular bitness).
using System;
using RdRandWrapper;
class Program {
static void Main(string[] args) {
var r = new RdRandom();
for (int i = 0; i != 10; ++i) {
Console.WriteLine(r.Next());
}
}
}
I make no claims as to performance, but it's probably not great. If you wanted to get many random values this way, you would probably want a Next(int[] values) overload to get many random values in one call, to reduce the overhead of interop.
Related
Getting a list of open windows in .Net Framework on Windows was relatively easy. How can I do the same in .Net Core/.Net 5 or later on macOS?
To clarify, I'm looking for a way to retrieve a list of all open windows owned by any running application/process. I don't have much experience of macOS development - I'm a Windows developer - but I've tried to use the NSApplication as suggested by this answer.
I created a .Net 6.0 Console application in VS2022 on macOS Monterey (12.2), added a reference to Xamarin.Mac and libxammac.dylib as described here - which describes doing this in Xamarin rather than .Net, but I don't see any other option to create a Console application. With the simple code:
static void Main(string[] args)
{
NSApplication.Init();
}
I get the output
Xamarin.Mac: dlopen error: dlsym(RTLD_DEFAULT, mono_get_runtime_build_info): symbol not found
I've no idea what this means. I'm not even sure this approach has any merit.
Does anyone know if it's possible to use NSApplication from a .Net Core/6.0 application, and if so whether NSApplication will give me the ability to read a system-wide list of open windows? If not, is there another way to accomplish this?
This is only for my own internal use, it doesn't need to be in any way portable or stable outside of my own environment.
In the link you refer to, there is an important note:
... as Xamarin.Mac.dll does not run under the .NET Core runtime, it only runs with the Mono runtime.
Because you try to run Xamarin.Mac.dll under .net-core, you get this dlopen error.
No System-wide List via NSApplication
The linked answer with NSApplication.shared.windows is incorrect if you want to read a system-wide list of open windows. It can only be used to determine all currently existing windows for the application from which the call is made, see Apple's documentation.
Alternative solution
Nevertheless, there are several ways to access the Window information in macOS. One of them could be a small unmanaged C-lib that gets the necessary information via CoreFoundation and CoreGraphics and returns it to C# via Platform Invoke (P/Invoke).
Native Code
Here is example code for a C-Lib that determines and returns the names of the window owners.
WindowsListLib.h
extern char const **windowList(void);
extern void freeWindowList(char const **list);
The interface of the library consists of only two functions. The first function called windowList returns a list with the names of the window owners. The last element of the list must be NULL so that you can detect where the list ends on the managed C# side. Since the memory for the string list is allocated dynamically, you must use the freeWindowList function to free the associated memory after processing.
WindowsListLib.c
#include "WindowListLib.h"
#include <CoreFoundation/CoreFoundation.h>
#include <CoreGraphics/CoreGraphics.h>
static void errorExit(char *msg) {
fprintf(stderr, "%s\n", msg);
exit(1);
}
static char *copyUTF8String(CFStringRef string) {
CFIndex length = CFStringGetLength(string);
CFIndex size = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
char *buf = malloc(size);
if(!buf) {
errorExit("malloc failed");
}
if(!CFStringGetCString(string, buf, size, kCFStringEncodingUTF8)) {
errorExit("copyUTF8String with utf8 encoding failed");
}
return buf;
}
char const **windowList(void) {
CFArrayRef cfWindowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
CFIndex count = CFArrayGetCount(cfWindowList);
char const **list = malloc(sizeof(char *) * (count + 1));
if(!list) {
errorExit("malloc failed");
}
list[count] = NULL;
for(CFIndex i = 0; i < count; i++) {
CFDictionaryRef windowInfo = CFArrayGetValueAtIndex(cfWindowList, i);
CFStringRef name = CFDictionaryGetValue(windowInfo, kCGWindowOwnerName);
if(name) {
list[i] = copyUTF8String(name);
} else {
list[i] = strdup("unknown");
}
}
CFRelease(cfWindowList);
return list;
}
void freeWindowList(char const **list) {
const char **ptr = list;
while(*ptr++) {
free((void *)*ptr);
}
free(list);
}
CGWindowListCopyWindowInfo is the actual function that gets the window information. It returns a list of dictionaries containing the details. From this we extract kCGWindowOwnerName. This CFStringRef is converted to a dynamically allocated UTF-8 string by the function copyUTF8String.
By convention, calls like CGWindowListCopyWindowInfo that contain the word copy (or create) must be released after use with CFRelease to avoid creating memory leaks.
C# Code
The whole thing can then be called on the C# side something like this:
using System.Runtime.InteropServices;
namespace WindowList
{
public static class Program
{
[DllImport("WindowListLib", EntryPoint = "windowList")]
private static extern IntPtr WindowList();
[DllImport("WindowListLib", EntryPoint = "freeWindowList")]
private static extern void FreeWindowList(IntPtr list);
private static List<string> GetWindows()
{
var nativeWindowList = WindowList();
var windows = new List<string>();
var nativeWindowPtr = nativeWindowList;
string? windowName;
do
{
var strPtr = Marshal.ReadIntPtr(nativeWindowPtr);
windowName = Marshal.PtrToStringUTF8(strPtr);
if (windowName == null) continue;
windows.Add(windowName);
nativeWindowPtr += Marshal.SizeOf(typeof(IntPtr));
} while (windowName != null);
FreeWindowList(nativeWindowList);
return windows;
}
static void Main()
{
foreach (var winName in GetWindows())
{
Console.WriteLine(winName);
}
}
}
}
The GetWindows method fetches the data via a native call to WindowList and converts the C strings to managed strings, then releases the native resources via a call to FreeWindowList.
This function returns only the owner names, such as Finder, Xcode, Safari, etc. If there are multiple windows, the owners will also be returned multiple times, etc. The exact logic of what should be determined will probably have to be changed according to your requirements. However, the code above should at least show a possible approach to how this can be done.
Screenshot
I'm trying to build a a simple C# application to test passing data between languages. I have a simple main in C# calling a function that I wish to pass to C++:
using System;
namespace CS_Console
{
class Program
{
static void Main(string[] args)
{
CPP_Sum(5, 2); //pseudo code
}
}
}
and then the function in a C++ project:
CPP_Sum(int x, int y)
{
return x+y;
}
The problem is, I don't even know where to start on how to pass these between each other.
This is being done via two projects, CS_Console and CPP_Console, in the same solution in Visual Studio.
It has already been mentioned by #steveo314. The easiest way to call a C++-based function from C# is using PInvoke. I assume you can create a DLL without any documentation but this looks like it will give you all the information you need to use your DLL from C#.
Basically:
Put your function as a C++ DLL; then
Use a DllImport attribute to create a function prototype in your C# code.
Make sure you export your C++ function definitions.
Daniel's answer above is also correct, but if you really want to dive into the guts of this, you may want to learn C++/CLI, as that's more or less C++ running on top of the .NET framework, with more precise interop, and depending on where/what your DLL is, communication both ways (P/Invoke is just calling C++, not calling back into .NET... usually).
C++/CLI allows this kind of thing:
public class Program
{
public void main(String [] args)
{
NativeWrapper wrapper = new NativeWrapper(); // C++/CLI ref class
wrapper.doStuff("hey there!");
}
}
In a C++/CLI DLL, in the .h file:
// This is C++/CLI
class NativeClass; // Forward declare
ref class NativeWrapper
{
public:
NativeWrapper(); // Constructor - MUST be in .cpp file, as size of NativeClass not known here
~NativeWrapper(); // Destructor - MUST be in .cpp file, same reason as above
void doStuff(System::String^ inputString);
private:
NativeClass* mp_impl;
};
In a C++/CLI .cpp file:
class NativeClass // True C++ class, could be defined elsewhere!
{
public:
void nativeDoStuff(const std::string& inputString)
{
std::cout << "Previous input was: '" << cachedResult << "'";
cachedResult = inputString;
}
private:
std::string cachedResult{};
};
NativeWrapper::NativeWrapper()
{
mp_impl = new NativeClass();
}
NativeWrapper::~NativeWrapper()
{
delete mp_impl;
mp_impl = nullptr;
}
// Utility function!
std::string _convertClrString(System::String^ instr)
{
marshal_context context;
std::string mystring{ context.marshal_as<const char*>(instr) };
return mystring;
}
void NativeWrapper::doStuff(System::String^ inputString)
{
auto native_string = _convertClrString(inputString);
mp_impl->nativeDoStuff(native_string);
}
It's a toy example (necessary includes removed), but hopefully that gets the idea across of what's possible with C++/CLI. The link I put near the top is the main start of the resources at Microsoft for doing this. Keep in mind what files are compiling as .NET, which aren't (you can do it per-file), and what that all means.
I am kinda stuck on not being able to dispose .NET 3.5 dlls from the process.
AppDomain support is off in unity, there is no way to unload a dll from the process using the .NET api, because the C# functions are not implemented.
Anyone could get me some hints on how / where should I start to remove the dll from the memory / process somehow, so I can re-load the dll whenever I want?
Alright, after this time I thought that this is some sort of heavy research, and there are no publications related to this. So here is what you have to do. First head over to https://github.com/mono/mono/branches/all and determine which mono version you are going to require. In my case I was doing this for an old game, and I needed the 2014 version of mono.
My project is discontinued so there is no point for me to keep this as a secret. The following examples will show you a way, but It probably won't be enough for you to get what you want on a newer mono version.
Using this way, you are able to unload every dll in a new AppDomain. You can extend It to create multiple appdomains, or unload only a specific dll. If It is working on a newer mono version then please let me know! Credits go to me, and 4g3v.
Once you have done that you are going to need exactly the same environment, and compilers for that version. In my case that was the Visual Studio 2010 compilers, and I didn't need to refactor most of the things.
You will need more than just following my instructions, you will have to play with mono, and get to know how the project works.
So hence as stated: C# API doesn't support AppDomains, but mono by default does. You just need to make some improvements, and extensions for It. Here is what you need to do:
Define two new functions in mono.def for example
mono_rb_create_domain and
mono_rb_unload_domain
The above two functions will be responsible to create, and dispose a domain.
Head over to: mono/metadata/object.c
Find function mono_runtime_unhandled_exception_policy_set and add (We will create the function later below):
mono_add_internal_call("YourDLLNameSpace.Icalls::mono_rb_load_plugin", ves_icall_mono_rb_load_plugin); //Last mono function unity calls before adding their own icalls (mono_runtime_unhandled_exception_policy_set). Adding them at runtime doesn't work, so this should be a pretty good place.
The above code will define a C# function that will be able to handle the loading of a custom DLL loaded in our own AppDomain. Make sure that your C# class, and function is public. Reminder: This should be somewhere in your unity project already. (For example Assembly-CSharp or whatever). It is crucial, because this will be handling the loading of your new dlls, and they will go to a new appdomain.
[MethodImpl(MethodImplOptions.InternalCall)]
public extern Assembly mono_rb_load_plugin(IntPtr data, uint dataLen);
Alright, we have to add some additional checks to avoid crashes in unity/unity_liveness.c Look for function mono_add_process_object and make it look like the following. This might differ in newer mono versions. =) What we have done here is basically ensuring that the received object has a VTable (which should be the class), and It isn't null.
static void mono_add_process_object (MonoObject* object, LivenessState* state)
{
gboolean has_references = 0;
MonoClass* klass; // Define the class
if (object && !IS_MARKED(object))
{
klass = GET_VTABLE(object)->klass; // Get the VTable
// Ensure that the class isn't f***ed up. Read: https://en.wikipedia.org/wiki/Hexspeak
if(klass == NULL || klass == 0xBAADF00D || klass == 0xFEEEFEEE)
{
return;
}
has_references = klass->has_references;
if(has_references || should_process_value(object,state) != LIVENESS_DONT_PROCESS)
{
if (array_is_full(state->all_objects))
array_safe_grow(state, state->all_objects);
array_push_back(state->all_objects, object);
MARK_OBJ(object);
}
// Check if klass has further references - if not skip adding
if (has_references)
{
if(array_is_full(state->process_array))
array_safe_grow(state, state->process_array);
array_push_back(state->process_array, object);
}
}
}
The above code will ensure that the processed class isn't faulty, or points somewhere else where It shouldn't.
Let's create our domain handlers.
Make sure to create this under the mono project.
My header file was named as mono_rustbuster.h, and contained:
static MonoDomain* rustBusterDomain;
GHashTable* pluginHashTable;
void mono_method_info_object();
MonoReflectionAssembly* ves_icall_mono_rb_load_plugin(void* objectPtr, char* data, guint32 dataLen) MONO_INTERNAL;
Then we created mono_rustbuster.c, and wrote the following:
#include "metadata\metadata-internals.h"
#include "metadata\image.h"
#include "metadata\assembly.h"
#include "metadata\debug-helpers.h"
#include "metadata\class-internals.h"
#include "metadata\object-internals.h"
static MonoDomain* rustBusterDomain;
GHashTable* pluginHashTable;
void mono_method_info_object()
{
}
int mono_rb_create_domain()
{
pluginHashTable = g_hash_table_new(g_str_hash, g_str_equal);
rustBusterDomain = mono_domain_create_appdomain("PluginDomain", NULL);
return 0x01;
}
int mono_rb_unload_domain()
{
mono_domain_unload(rustBusterDomain);
return 0x01;
}
MonoReflectionAssembly* ves_icall_mono_rb_load_plugin(void* objectPtr, char* data, guint32 dataLen)
{
MonoAssembly* ass;
MonoImage* img;
MonoImageOpenStatus status;
MonoDomain* current;
char *assNameBuf;
current = mono_domain_get();
mono_domain_set(rustBusterDomain, FALSE);
img = mono_image_open_from_data_full(data, dataLen, TRUE, NULL, FALSE);
ass = mono_assembly_load_from_full(img, "", &status, FALSE);
assNameBuf = (char*)malloc(256);
sprintf(assNameBuf, "%s", ass->aname.name);
g_hash_table_insert(pluginHashTable, (gpointer)assNameBuf, (gpointer)ass);
mono_domain_set(current, FALSE);
return mono_assembly_get_object(rustBusterDomain, ass);
}
After this setup your loaded DLLs might whine about missing references. This mostly happens when you load a new DLL in all by yourself using these functions. We added some layers into mono/metadata/assembly.c for this fix.
Find mono_assembly_load_reference This method works with the assembly's references, find where It is calling the reference variable and add:
if(reference == NULL && strstr(image->name, "data-"))
{
reference = (MonoAssembly*)g_hash_table_lookup(pluginHashTable, aname.name);
}
Hint: Mono add's data- to all of the dll's, so that way we can use that memory pointer to find the references. Basically fixes the unresolved referencess.
Head over to mono/metadata/class.c and find: mono_class_is_assignable_from
Before function would return data using the last function check if the class names are equal.
The last return looks something like this:
return mono_class_has_parent (oklass, klass);
Add:
if(!mono_class_has_parent (oklass, klass))
{
if(strstr(klass->image->name, "data-"))
{
if(!strcmp((oklass)->supertypes [(klass)->idepth - 1]->name, klass->name))
{
//OutputDebugStringA("mono_class_is_assignable_from(): Class names are equal so true is returned");
return TRUE;
}
}
}
The above code will add fixes to ScriptableObject cast exceptions.
You are sort of done. You may encounter additional issues. Here is how it works in C#:
[DllImport("mono.dll")]
internal static extern int mono_rb_create_domain();
[DllImport("mono.dll")]
internal static extern int mono_rb_unload_domain();
Handle loading of a new dll:
var icalls = new Icalls();
int domaincreation = RustBuster.mono_rb_create_domain();
byte[] bytes = getyourdllsbytearraysomehow;
IntPtr pluginMem = Marshal.AllocHGlobal(bytes.Length);
for (int i = 0; i < bytes.Length; i++)
{
Marshal.WriteByte(pluginMem, i, bytes[i]);
}
assembly = icalls.mono_rb_load_plugin(pluginMem, (uint) bytes.Length);
Unload:
int domaincheck2 = RustBuster.mono_rb_unload_domain();
I have a set of C functions that I need to use on an ARM target, in C++ and in C#. I can successfully wrap up the C into a C++ DLL and then into a C# DLL and use all the C functions I've bound successfully. However, I have a debug function that I want to be able to print to the C# GUI and the delegate it uses is being garbage collected rather than left in place for the duration.
Managed Debugging Assistant 'CallbackOnCollectedDelegate' has detected a
problem in 'C:\utm\pc\utm_win32_app\bin\Debug\utm_win32_app.vshost.exe'.
Additional Information: A callback was made on a garbage collected delegate of
type
'utm_dll_wrapper_cs!MessageCodec.MessageCodec_dll+guiPrintToConsoleCallback::
Invoke'. This may cause application crashes, corruption and data loss. When
passing delegates to unmanaged code, they must be kept alive by the managed
application until it is guaranteed that they will never be called.
Here's the snippet of C code that uses and sets up the callback mp_guiPrintToConsole:
#ifdef WIN32
static void (* mp_guiPrintToConsole) (const char*) = NULL;
void logMsg (const char * pFormat, ...)
{
char buffer[MAX_DEBUG_MESSAGE_LEN];
va_list args;
va_start (args, pFormat);
vsnprintf (buffer, sizeof (buffer), pFormat, args);
va_end (args);
#ifdef WIN32
if (mp_guiPrintToConsole)
{
(*mp_guiPrintToConsole) (buffer);
}
#else
// Must be on ARM
printf (buffer);
#endif
}
void initDll (void (*guiPrintToConsole) (const char *))
{
#ifdef WIN32
mp_guiPrintToConsole = guiPrintToConsole;
// This is the signal to the GUI that we're done with initialisation
logMsg ("ready.\r\n");
#endif
}
Here's the C++ code, built into a DLL along with the C code, that can be called from C# and passes in the function pointer printToConsole:
void msInitDll (void (*printToConsole) (const char *))
{
initDll (printToConsole);
}
Here's the snippet code from the C# DLL that calls msInitDll(), passing in guiPrintToConsole(), and defines the delegate onConsoleTrace, which I guess is the thing that is disappearing:
[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
public delegate void _msInitDll([MarshalAs (UnmanagedType.FunctionPtr)] guiPrintToConsoleCallback callbackPointer);
public _msInitDll msInitDll;
public delegate void ConsoleTrace(string data);
public event ConsoleTrace onConsoleTrace;
public void guiPrintToConsole(StringBuilder data)
{
if (onConsoleTrace != null)
{
onConsoleTrace (data.ToString ());
}
}
public void bindDll(string dllLocation)
{
IntPtr ptrDll = LoadLibrary (dllLocation);
if (ptrDll == IntPtr.Zero) throw new Exception (String.Format ("Cannot find {0}", dllLocation));
//...
// All the other DLL function bindings are here
//...
msInitDll = (_msInitDll)bindItem(ptrDll, "msInitDll", typeof(_msInitDll));
msInitDll(guiPrintToConsole);
}
I've looked at the various answers here and the most promising seemed to be to create a static variable in the C# code:
static GCHandle gch;
...and then use that to reference onConsoleTrace in the C# bindDll() function:
gch = GCHandle.Alloc(onConsoleTrace);
However, that doesn't do me any good. I've tried a few other attempts at declaring things static but nothing seems to get me where I want to be. Can anyone suggest another approach to fixing the problem? I have a bug that I need to fix and the lack of any debug is proving quite annoying.
Rob
The following line uses some syntactic sugar:
msInitDll(guiPrintToConsole);
The full syntax is:
msInitDll(new guiPrintToConsoleCallback(guiPrintToConsole));
Hopefully now you see why the delegate can get garbage-collected.
One simple workaround:
var callback = new guiPrintToConsoleCallback(guiPrintToConsole);
msInitDll(callback);
// ... some other code
GC.KeepAlive(callback);
Now the delegate is guaranteed to be alive up to the GC.KeepAlive call.
But you most probably need the delegate to stay alive for longer. As the error message says, simply keep a reference to it. If you need it for the full C# app lifetime duration, turn the callback local to a static field in your class. Static fields are treated as GC roots as their values are always reachable.
And the answer was, in the C# DLL code, add the static variable:
public static guiPrintToConsoleCallback debugCallback;
...and then, in C# bindDLL(), change:
msInitDll(guiPrintToConsole);
...to
debugCallback = new guiPrintToConsoleCallback(guiPrintToConsole);
msInitDll(debugCallback);
Simple when you know how.
The following code compile without errors. Basically, the C#2005 Console application calls VC++2005 class library which in turn calls native VC++6 code. I get the following error when I run the C#2005 application:
"Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
What is the cause of this error? And how to go about correcting it?
Edit1: It crashes at the line StdStringWrapper ssw = w.GetNext();
Edit2: I followed the advice of Naveen and used an integer index instead of iterators and there is no more errors now. A big thanks to all who commented as well!
Code Written in C#2005 as Console Application:
class Program
{
static void Main(string[] args)
{
Class1 test= new Class1();
test.PerformAction();
test.PerformAction();
test.PerformAction();
test.PerformAction();
}
}
Code Written in VC++2005 as Class Library:
public ref class Class1
{
public:
void PerformAction();
};
void Class1::PerformAction()
{
DoSomethingClass d;
StdStringContainer w;
d.PerformAction(w);
for(int i=0; i<w.GetSize(); i++)
{
StdStringWrapper ssw = w.GetNext();
std::cout << ssw.CStr() << std::endl;
}
}
Code Written in VC++6 as Dynamic Link Library:
#ifdef NATIVECODE_EXPORTS
#define NATIVECODE_API __declspec(dllexport)
#else
#define NATIVECODE_API __declspec(dllimport)
#endif
class NATIVECODE_API StdStringWrapper
{
private:
std::string _s;
public:
StdStringWrapper();
StdStringWrapper(const char *s);
void Append(const char *s);
const char* CStr() const;
};
StdStringWrapper::StdStringWrapper()
{
}
StdStringWrapper::StdStringWrapper(const char *s)
{
_s.append(s);
}
void StdStringWrapper::Append(const char *s)
{
_s.append(s);
}
const char* StdStringWrapper::CStr() const
{
return _s.c_str();
}
//
class NATIVECODE_API StdStringContainer
{
private:
std::vector<StdStringWrapper> _items;
std::vector<StdStringWrapper>::iterator _it;
public:
void Add(const StdStringWrapper& item);
int GetSize() const;
StdStringWrapper& GetNext();
};
void StdStringContainer::Add(const StdStringWrapper &item)
{
_items.insert(_items.end(),item);
}
int StdStringContainer::GetSize() const
{
return _items.size();
}
StdStringWrapper& StdStringContainer::GetNext()
{
std::vector<StdStringWrapper>::iterator it = _it;
_it++;
return *it;
}
//
class NATIVECODE_API DoSomethingClass
{
public:
void PerformAction(StdStringContainer &s);
};
void DoSomethingClass::PerformAction(StdStringContainer &s)
{
StdStringWrapper w1;
w1.Append("This is string one");
s.Add(w1);
StdStringWrapper w2;
w2.Append("This is string two");
s.Add(w2);
}
The member _it in StdStringContainer is never initialized to point into the _items vector. This means it's an invalid iterator. When you assign _it to it in GetNext(), you've given it the invalid, uninitialized value that existed in _it. You then increment the uninitialized _it via _it++, which is what's triggering your fault.
As Stroustrup says in 19.2, an uninitialized iterator is an invalid iterator. This means that your uninitialized _it is invalid and that operations performed with it are undefined, and likely to cause dramatic failure.
Your problem is deeper, however. Iterators have a fundamentally different lifetime from the containers that they enumerate. There aren't really any "good" ways to do what you're trying to do with a single iterator member like this unless the container is immutable and initialized in the constructor.
If you can't expose the std:: namespace names, have you considered aliasing them via typedef's, e.g.? What about your organization or project makes it impossible to expose the template classes?
The main problem from my point of view is you are storing an iterator to a vector in your stdStringContainer class. Remember that whenever vector resizes all the existing iterators are invalidated. So whenever you do insert operation into the vector it may be possible that it resizes and your existing iterator becomes invalid. If you try to to dereference it in GetNext() then it will access invalid memory location. For checking whether this really the case try to reserve the initial vector size to some relatively big number so that the resizing doesn't happen. You can reserve the size using reserve() method, in which case it is guaranteed that the capacity() of the vector is greater than or equal to the reserved value.
Sounds like you have a memory leak. I would suggest looking anywhere where there is pointer arithmetic, writing to memory, or array usage. Check for the bounds conditions in the array accessing.
Another issue: The leak many not even be in your code. If this is the case you'll have to exclude the library from your project.
My guess is, that you have the crash because std::string and std::vector in the interface between two C++ modules were compiled with different compilers and runtime libraries.
The memory layout of vector and string maybe changed between VC6 and 2005.
When the 2005 DLL allocates objects of type StdStringContainer and StdStringWrapper, it does so based on the declarations of string and vector in the 2005 headers.
When member functions are called on these objects (which have been compiled with the VC6 compiler and libraries), they assume a different memory layout and fail with access violations.