Wrong values passed as parameter to C library using SWIG - c#

Following my three previous posts, I can now pass a managed array of struct to my wrapped method. Here is an extract from the files:
// packer.i
typedef struct {
int width; // input
int height; // input
frame_t view; // output
frame_t dest; // output
} image_t;
CSHARP_ARRAYS(image_t, image_t)
%apply image_t INOUT[] { image_t *images }
int pack(image_t *images, int nb_images, parameters_t params);
Which generates a function with this signature:
// packer_cs.cs
public static int pack(image_t[] images, int nb_images, parameters_t arg2)
Which I call like this:
// Program.cs
var files = Directory.GetFiles("./images");
var images = new image_t[files.Length];
for (var f = 0; f < files.Length; f++)
{
using (var imgInfo = Image.FromFile(files[f]))
{
var imgStruct = new image_t()
{
width = imgInfo.Width,
height = imgInfo.Height,
dest = new frame_t(),
view = new frame_t()
};
images[f] = imgStruct;
}
}
var result = packer_cs.pack(images, images.Length, new parameters_t());
All is well and done, but when I run the pack() method, I have a protected memory access problem (System.AccessViolationException). Thankfully I have access to the source code of the C library, and Visual Studio automagically opens it for debugging and stepping through as soon as I enable unmanaged code debugging.
So, if I add a breakpoint at the start of the pack() function, and I use a watch to check images[x], I can see that the width and height values have nothing to do with what is provided (sometimes it's even 0 or negative). What's going on ? If I inspect my managed array on the C# side, the values are correctly stored and retrieved. Why doesn't C get the right values ? The other parameters (nb_images and params) don't have any problem.
Thank you !

Do the following:
Check that images[f].width and height have the values you expect
If yes, then check the SWIG-generated code to verify that those fields are properly copied.
If you can't spot any problem by looking at the code, you should break on packer_cs.pack and use the debugger to look at the wrapper code that copies the C# array to the C++ array, see what is wrong.
It is probably something in the typemaps that is incorrect. Once you know what that is, you can copy the typemaps code from SWIG source (the csharp_array.i file) to a new typemap in your .i and fix as required.

Related

Visual Studio Debugger Extension get user settings

I'm writing a visual studio extension based on the Concord Samples Hello World project. The goal is to let the user filter out stack frames by setting a list of search strings. If any of the search strings are in a stack frame, it is omitted.
I've got the filter working for a hardcoded list. That needs to be in a non-package-based dll project in order for the debugger to pick it up. And I have a vsix project that references that dll with an OptionPageGrid to accept the list of strings. But I can't for the life of me find a way to connect them.
On the debugger side, my code looks something like this:
DkmStackWalkFrame[] IDkmCallStackFilter.FilterNextFrame(DkmStackContext stackContext, DkmStackWalkFrame input)
{
if (input == null) // null input frame indicates the end of the call stack. This sample does nothing on end-of-stack.
return null;
if (input.InstructionAddress == null) // error case
return new[] { input };
DkmWorkList workList = DkmWorkList.Create(null);
DkmLanguage language = input.Process.EngineSettings.GetLanguage(new DkmCompilerId());
DkmInspectionContext inspection = DkmInspectionContext.Create(stackContext.InspectionSession, input.RuntimeInstance, input.Thread, 1000,
DkmEvaluationFlags.None, DkmFuncEvalFlags.None, 10, language, null);
string frameName = "";
inspection.GetFrameName(workList, input, DkmVariableInfoFlags.None, result => GotFrameName(result, out frameName));
workList.Execute();
CallstackCollapserDataItem dataItem = CallstackCollapserDataItem.GetInstance(stackContext);
bool omitFrame = false;
foreach (string filterString in dataItem.FilterStrings)
{
if (frameName.Contains(filterString))
{
omitFrame = true;
}
}
The CallstackCollapserDataItem is where I theoretically need to retrieve the strings from user settings. But I don't have access to any services/packages in order to e.g. ask for WritableSettingsStore, like in You've Been Haacked's Example. Nor can I get my OptionPageGrid, like in the MSDN Options Example.
The other thing I tried was based on this StackOverflow question. I overrode the LoadSettingsFromStorage function of my OptionPageGrid and attempted to set a static variable on a public class in the dll project. But if that code existed in the LoadSettingsFromStorage function at all, the settings failed to load without even entering the function. Which felt like voodoo to me. Comment out the line that sets the variable, the breakpoint hits normally, the settings load normally. Restore it, and the function isn't even entered.
I'm at a loss. I really just want to pass a string into my Concord extension, and I really don't care how.
Ok, apparently all I needed to do was post the question here for me to figure out the last little pieces. In my CallstackCollapserDataItem : DkmDataItem class, I added the following code:
private CallstackCollapserDataItem()
{
string registryRoot = DkmGlobalSettings.RegistryRoot;
string propertyPath = "vsix\\CallstackCollapserOptionPageGrid";
string fullKey = "HKEY_CURRENT_USER\\" + registryRoot + "\\ApplicationPrivateSettings\\" + propertyPath;
string savedStringSetting = (string)Registry.GetValue(fullKey, "SearchStrings", "");
string semicolonSeparatedStrings = "";
// The setting resembles "1*System String*Foo;Bar"
if (savedStringSetting != null && savedStringSetting.Length > 0 && savedStringSetting.Split('*').Length == 3)
{
semicolonSeparatedStrings = savedStringSetting.Split('*')[2];
}
}
vsix is the assembly in which CallstackCollapserOptionPageGrid is a DialogPage, and SearchStrings is its public property that's saved out of the options menu.

Could not load type 'SAS.LanguageServiceCarriageControl' from assembly

In addition to using the integration components of SAS Enterprise Edition, I am using parts of the following project I found on Github to connect with a SAS server. The goal here is to command the server to run programs on a schedule. However, the programs need to be modified each time, which is why I am attempting to trigger them to run in this manner. However, it keeps throwing an error at lang.FlushLogLines.
https://github.com/cjdinger/SasHarness
SAS.Workspace ws = server.Workspace;
List<string> results = new List<string>();
Array CCs;
Array lineTypes;
Array logLines;
int maxLines = 100;
SAS.LanguageService lang = (SAS.LanguageService)ws.LanguageService;
Array linesVar = (Array)new string[] { PROGRAM_TEXT };
lang.SubmitLines(ref linesVar);
//THROWS AN ERROR HERE
lang.FlushLogLines(maxLines, out CCs, out lineTypes, out logLines);
for (int i = 0; i < logLines.Length; i++)
{
results.Add((string)logLines.GetValue(i));
}
After a bit of research I found the following thread where it is recommended to make sure that all the required dlls are referenced in my project. The mystery here is that I do have them referenced, but the error still occurs.
http://blogs.sas.com/content/sasdummy/2013/06/09/sas-client-with-microsoft-dot-net/
Moreover, starting after the very first line, the code is no longer using SASHarness, but is using native SAS integration libraries only. The code above is also based on examples listed in the following documentation from SAS.
https://support.sas.com/documentation/cdl/en/itechwcdg/61500/PDF/default/itechwcdg.pdf (page 27-28)
Has anybody encountered an error similar to this, and if so, how did you correct it?
Strangely, fixing this error required declaring an instance for each of the assemblies that could not be loaded. I have no idea why this fixes the problem, but it works now.
SAS.Workspace ws = server.Workspace;
string wsId = ws.UniqueIdentifier;
List<string> results = new List<string>();
Array CCs;
Array lineTypes;
Array logLines;
int maxLines = 100;
SAS.LanguageService lang = (SAS.LanguageService) server.Workspace.LanguageService;
Array linesVar = (Array) new string[] { PROGRAM_TEXT };
lang.SubmitLines(ref linesVar);
//For some reason, these two declarations need to be here
SAS.LanguageServiceCarriageControl CarriageControl = new SAS.LanguageServiceCarriageControl();
SAS.LanguageServiceLineType LineType = new SAS.LanguageServiceLineType();
lang.FlushLogLines(maxLines, out CCs, out lineTypes, out logLines);
for (int i = 0; i < logLines.Length; i++)
{
results.Add((string) logLines.GetValue(i));
}

Removing features from the Google Earth Plugin in c#

Using the Visual Studio C# Winforms Google Earth plugin, 4 placemarks have been added to the globe as can be seen in the picture below:
My goal is to be able to remove the linestring placemark. The steps would seem to be to get all the placemarks, find the linestring and remove it.
Here is the code being used to create the linestring placemarks (more or less from the API website)
var lineStringPlacemark = ge2.createPlacemark("Line_" + name);
// create the line string geometry
var lineString = ge2.createLineString("");
lineStringPlacemark.setGeometry(lineString);
// add the the points to the line string geometry
double dlat1 = Convert.ToDouble(lat1) / 100000;
double dlon1 = Convert.ToDouble(lon1) / 100000;
double dlat2 = Convert.ToDouble(lat2) / 100000;
double dlon2 = Convert.ToDouble(lon2) / 100000;
lineString.getCoordinates().pushLatLngAlt(dlat1, dlon1, 0);
lineString.getCoordinates().pushLatLngAlt(dlat2, dlon2, 0);
// Create a style and set width and color of line
lineStringPlacemark.setStyleSelector(ge2.createStyle(""));
var lineStyle = lineStringPlacemark.getStyleSelector().getLineStyle();
lineStyle.setWidth(5);
lineStyle.getColor().set("9900ffff"); // aabbggrr format
// Add the feature to Earth
ge2.getFeatures().appendChild(lineStringPlacemark);
And here is the code I ended up using to remove the line. Note that the GEHelpers.RemoveFeatureById(ge2, s); is commented out since it isn't working for me for some reason.
for (int i = 0; i < ge2.getFeatures().getChildNodes().getLength(); i++)
{
var kmlobject = ge2.getFeatures().getChildNodes().item[i];
string s = kmlobject.getId();
if (s.Contains("Line_"))
{
ge2.getFeatures().removeChild(kmlobject);
kmlobject.release();
//GEHelpers.RemoveFeatureById(ge2, s);
}
}
The line you have should work and remove all the currently loaded content.
GEHelpers.RemoveAllFeatures(ge); // removes all loaded features from 'ge'
If you wish to remove a specific placemark, or any other feature, simply specify its ID as the parameter to the RemoveFeatureById method.
GEHelpers.RemoveFeatureById(ge, 'foo'); // remove the feature with the id 'foo'
An ID can be set either when you create the feature via the api or when you define the feature in kml. e.g.
// api
ge.createPlacemark('foo');
//kml id
<Document id="foo">
</Document>
Edit:
You should not have to do anything other than...
for (int i = 0; i < ge2.getFeatures().getChildNodes().getLength(); i++)
{
var kmlobject = ge2.getFeatures().getChildNodes().item[i];
if (kmlobject.getId().Contains("Line_"))
{
ge2.getFeatures().removeChild(kmlobject);
}
}
I think that there is possibly something else going on with your set up, maybe to do with having multiple instances of the plugin running at the same time.

Can i Read code from a file and and let that code run in an application

I am developing an Desktop Application in WPF using C# .
For the sake of simplicity, Assume my Application has functions which draw lines in said direction goleft() , goright() , goforward() , goback() .
when any of these function is called a line of one inch will be drawn on screen.
I want to make application where user will write code in a file in any editor (say notepad) and save that file in some fixed format (say .abc or .xyz)
Imaginary Example :
(section_start)
For(int i = 0 ; i<= 20 ; i++ )
{
if(i<5)
goforward();
else if(i==5)
goleft();
else if(i < 10)
forward();
.......
........
}
(section_End)
Can i make application which should be capable of reading this file and execute code which is written in between of (section_start) and (section_End). and only if possible can check for syntax errors too... (Not compulsory).
Please guide me on this issue :
Disclosure : My actual Application is somewhat else and could not discuss here due to my company's rules.
Thanks to all who replied to my question. Stackoverflow is fantastic site , i have found the roadmap where to go , till today morning i did not have any clue but now i can go ahead , thanks all of you once again
Will ask question again if i get stucked somewhere
You can read the file content using FileInfo and get the code you need to execute.
Then you can execute the code using CSharpCodeProvider like in this post:
using (Microsoft.CSharp.CSharpCodeProvider foo =
new Microsoft.CSharp.CSharpCodeProvider())
{
var res = foo.CompileAssemblyFromSource(
new System.CodeDom.Compiler.CompilerParameters()
{
GenerateInMemory = true
},
"public class FooClass { public string Execute() { return \"output!\";}}"
);
var type = res.CompiledAssembly.GetType("FooClass");
var obj = Activator.CreateInstance(type);
var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
}
You can choose CodeDOM or IL Emit
More help on CodeDOM
More information on IL Generator / Emit
This is called scripting your application if I understand correctly. C# does not support this out of the box. One thing to look into could be the new Roselyn compiler from Microsoft (it is a new take on the C# compiler, which lets you do just this).
For more info on Roselyn check out:
http://blogs.msdn.com/b/csharpfaq/archive/2011/12/02/introduction-to-the-roslyn-scripting-api.aspx
I've only seen a demo of it, but it looks very promissing, and should solve your problem.
It's not clear what kind of code you want compiled but here is a guide on how to compile code code with C#.
You could use IronPython to handle the script.
Here's an example of how to do this:
First you need a navigation object to perform the operations on:
public class NavigationObject
{
public int Offset { get; private set; }
public void GoForwards()
{
Offset++;
}
public void GoBackwards()
{
Offset--;
}
}
Then the code to execute the file:
public void RunNavigationScript(string filePath, NavigationObject navObject)
{
var engine = Python.CreateEngine();
var scope = engine.CreateScope();
scope.SetVariable("navigation", navObject);
var source = engine.CreateScriptSourceFromFile(filePath);
try
{
source.Execute(scope);
}
catch(Exception
{
}
}
The script file can then take the form of something like:
for x in range(0,20):
if x == 5:
navigation.GoBackwards()
else:
navigation.GoForwards()

Embedding Mono in Objective-c project: How to handle returned List<>

I'm embedding Mono in an MacOSX app written in Objective-c.
I'm accessing a C# lib (DDL), which only contains a bunch of static methods returning different types.
So far I can successfully get returned int, double and string, but I'm having trouble retrieving a returned array...
For exemple, here's how I retrieve an int:
MonoDomain *domain = mono_jit_init("TestDomain");
NSBundle* mainBundle = [NSBundle mainBundle];
NSString* dll = [mainBundle pathForResource:#"TestLib86" ofType:#"dll"];
MonoAssembly* assembly = mono_domain_assembly_open(domain, [dll UTF8String]);
MonoImage* image = mono_assembly_get_image(assembly);
// Get INTEGER
// get a method handle to whatever you like
const char* descAsString = "MiniLib86.Show:GetInt()";
MonoMethodDesc* description = mono_method_desc_new(descAsString,TRUE);
MonoMethod* method = mono_method_desc_search_in_image(description, image);
// call it
void* args[0];
MonoObject *result = mono_runtime_invoke(method, NULL, args, NULL);
int int_result = *(int*)mono_object_unbox (result);
// See the result in log
NSLog(#"int result %i", int_result);
The method in C# that returns an List looks like this:
public static List<int> GetListInt()
{
return new System.Collections.Generic.List<int>{1,2,3,4,5};
}
Any help would be really appreciated !
Take a look at mono_runtime_invoke_array.

Categories