I have this class KernelHelper which is written in C# .NET Framework 2.0. What I want to do is call its static functions in a C program.
namespace Kernel.Client {
public class KernelHelper {
public static int testc(string msg) {
// Removing the message box does not change anything
System.Windows.Forms.MessageBox.Show(msg);
return 0;
}
// ...
}
}
which compiles and does not seem to make any problems so far. But calling ICLRRuntimeHost_ExecuteInDefaultAppDomain() returns 0x80131513 which is according to this the fact that I didn't follow the correct signature convention. But this can not be the problem.
#if defined(_WIN32)
# include <Windows.h>
# define COBJMACROS
# define CINTERFACE
# include <mscoree.h>
#endif
// ...
HRESULT status;
ICLRRuntimeHost *Host;
BOOL Started;
DWORD Result;
Host = NULL;
Started = FALSE;
status = CorBindToRuntimeEx(
NULL,
NULL,
0,
&CLSID_CLRRuntimeHost,
&IID_ICLRRuntimeHost,
(PVOID *)&Host
);
if (FAILED(status)) {
printf("failed 1\n");
}
status = ICLRRuntimeHost_Start(Host);
if (FAILED(status)) {
printf("failed 2\n");
}
Started = TRUE;
status = ICLRRuntimeHost_ExecuteInDefaultAppDomain(
Host,
L"C:\\svn\\Server\\Kernel\\interface\\bin\\Kernel.Client.dll",
L"Kernel.Client.KernelHelper",
L"testc",
L"My message",
&Result
);
if (FAILED(status)) {
printf("failed 3\n");
}
Could anybody help me here?
Edit:
I tried it also without the message box and let the function just return 0 but it didn't change a thing.
Wow, I did not expect to find the solution so fast and on my own. If you experiance the same problem try this. It seems not to work for everyone.
All I needed to do is to change the Platform Target from Any CPU to x86 inside my project properties.
Edit: If anyone can show me another solution I'd still be glad since I can not tell if this might not be a problem anyway changing this setting.
Related
I've been struggling to get PowerShell to control SetWindowCompositionAttribute via Pinvoke (to add Windows 10 Acrylic Blur effect to the Windows taskbar and other windows via PowerShell).
I am comfortable sending simple parameters like HWND and respective values (as shown in two of my examples below). However, I’m not sure how to enhance the PowerShell code below so it can also handle sending parameters packed in a struct. See SetWindowCompositionAttribute.
I've seen code examples in Delphi and AutoIt (instead of PowerShell) that do this. Unfortunately, I couldn't figure them out.
Anyway, below is my working PowerShell code and usage examples that demonstrate basic interaction with the Windows API. I'm hoping someone could help me enhance this code (with a couple of examples) to also control various features offered by SetWindowCompositionAttribute.
Ultimately, I'd like to be able to specify hWnd, Windows class name, and/or Window title name of the component I wish to add blur too; and, specify the amount of blur/transparency if possible.
Working Functions and example Usage:
$script:nativeMethods = #();
function Register-NativeMethod([string]$dll, [string]$methodSignature) {
$script:nativeMethods += [PSCustomObject]#{ Dll = $dll; Signature = $methodSignature; }
}
function Add-NativeMethods() {
$nativeMethodsCode = $script:nativeMethods | % { "
[DllImport(`"$($_.Dll)`")]
public static extern $($_.Signature);
" }
Add-Type #"
using System;
using System.Runtime.InteropServices;
public class NativeMethods {
$nativeMethodsCode
}
"#
}
#Build class and registers them:
Add-NativeMethods
#Example 1:
Register-NativeMethod "user32.dll" "bool SetForegroundWindow(IntPtr hWnd)"
[NativeMethods]::SetForegroundWindow((Get-Process -name notepad).MainWindowHandle)
#Example 2:
Register-NativeMethod "user32.dll" "bool ShowWindow(IntPtr hWnd, int nCmdShow)"
[NativeMethods]::ShowWindow((Get-Process -name notepad).MainWindowHandle, 0)
EDIT: Added an option for "acrylic" blur with color tinting. It seems to be a bit slow though when moving the windows about.
Is this what you're after?
Window before running function:
Window after running function (Set-WindowBlur -MainWindowHandle 853952 -Enable):
Main code:
$SetWindowComposition = #'
[DllImport("user32.dll")]
public static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data);
[StructLayout(LayoutKind.Sequential)]
public struct WindowCompositionAttributeData {
public WindowCompositionAttribute Attribute;
public IntPtr Data;
public int SizeOfData;
}
public enum WindowCompositionAttribute {
WCA_ACCENT_POLICY = 19
}
public enum AccentState {
ACCENT_DISABLED = 0,
ACCENT_ENABLE_BLURBEHIND = 3,
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4
}
[StructLayout(LayoutKind.Sequential)]
public struct AccentPolicy {
public AccentState AccentState;
public int AccentFlags;
public int GradientColor;
public int AnimationId;
}
'#
Add-Type -MemberDefinition $SetWindowComposition -Namespace 'WindowStyle' -Name 'Blur'
function Set-WindowBlur {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[int]
$MainWindowHandle,
[Parameter(ParameterSetName='Enable',Mandatory)]
[switch]
$Enable,
[Parameter(ParameterSetName='Acrylic',Mandatory)]
[switch]
$Acrylic,
# Color in BGR hex format (for ease, will just be used as an integer), eg. for red use 0x0000FF
[Parameter(ParameterSetName='Acrylic')]
[ValidateRange(0x000000, 0xFFFFFF)]
[int]
$Color= 0x000000,
# Transparency 0-255, 0 full transparency and 255 is a solid $Color
[Parameter(ParameterSetName='Acrylic')]
[ValidateRange(0, 255)]
[int]
$Transparency = 80,
[Parameter(ParameterSetName='Disable',Mandatory)]
[switch]
$Disable
)
$Accent = [WindowStyle.Blur+AccentPolicy]::new()
switch ($PSCmdlet.ParameterSetName) {
'Enable' {
$Accent.AccentState = [WindowStyle.Blur+AccentState]::ACCENT_ENABLE_BLURBEHIND
}
'Acrylic' {
$Accent.AccentState = [WindowStyle.Blur+AccentState]::ACCENT_ENABLE_ACRYLICBLURBEHIND
$Accent.GradientColor = $Transparency -shl 24 -bor ($Color -band 0xFFFFFF)
}
'Disable' {
$Accent.AccentState = [WindowStyle.Blur+AccentState]::ACCENT_DISABLED
}
}
$AccentStructSize = [System.Runtime.InteropServices.Marshal]::SizeOf($Accent)
$AccentPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($AccentStructSize)
[System.Runtime.InteropServices.Marshal]::StructureToPtr($Accent,$AccentPtr,$false)
$Data = [WindowStyle.Blur+WindowCompositionAttributeData]::new()
$Data.Attribute = [WindowStyle.Blur+WindowCompositionAttribute]::WCA_ACCENT_POLICY
$Data.SizeOfData = $AccentStructSize
$Data.Data = $AccentPtr
$Result = [WindowStyle.Blur]::SetWindowCompositionAttribute($MainWindowHandle,[ref]$Data)
if ($Result -eq 1) {
Write-Verbose "Successfully set Window Blur status."
}
else {
Write-Verbose "Warning, couldn't set Window Blur status."
}
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($AccentPtr)
}
To use call with something like:
Set-WindowBlur -MainWindowHandle 1114716 -Acrylic -Color 0xFF0000 -Transparency 50
Set-WindowBlur -MainWindowHandle 1114716 -Disable
Set-WindowBlur -MainWindowHandle 1114716 -Enable
Adapted from: https://gist.github.com/riverar/fd6525579d6bbafc6e48
and from:
https://github.com/riverar/sample-win32-acrylicblur/blob/master/MainWindow.xaml.cs
I can see you invoke these Windows API functions by embedding/building C# source code inside your Powershell script. It's probably the way to go, although I think I wouldn't have bothered with trying to 'add' methods and construct the C# source code piece by piece (because declaring SetWindowCompositionAttribute using your system won't be that easy).
First, because SetWindowCompositionAttribute accepts complex arguments you'll have to declare the underlying structures in you C# code, then you can expose C# methods that simplify the use of SetWindowCompositionAttribute for consumption by the Powershell side.
By googling a bit (simply typed TheMethodINeedToWrap C#), I quickly found a few resources. I've mixed these findings into a C# class that exposes a (simple enough that it is Powershell-callable) method to blur a window. The results of my tests were not ideal as I ended up with a blurred window border but weird looking interior and controls (must have something to do with winforms and/or the window creation parameters), but at least I obtained something.
Because it's a bit long, I've saved this class as a gist here, but as I said it's made of a bunch of interop structures and methods definitions, then a public method simplifying access to SetWindowCompositionAttribute.
PS: If SetWindowCompositionAttribute proves too difficult to wrap, DwmSetWindowAttribute may be a viable aleternative. You'll also find that, although it sometimes needs tweaking, http://pinvoke.net/ proves a viable source for interop definitions.
I'm trying to build a PostScript to PDF Converter using Ghostscript.Net.
The Args that GetArgs return, are the ones I usually use to call gswin32c.exe and they work fine.
But every time i call Process, i get an error Saying "An error occured when call to 'gsapi_init_with_args' is made: -100". Googling that error didn't bring anything up so I thought I might ask here.
Are there differnet arguments to consider when calling the .dll directly with Ghostscript.net? Or did I made a mistake somewhere else?
Here's my class:
public class PdfConverter
{
#region Private Fields
private List<GhostscriptVersionInfo> _Versions = GhostscriptVersionInfo.GetInstalledVersions(GhostscriptLicense.GPL | GhostscriptLicense.AFPL | GhostscriptLicense.Artifex);
#endregion
#region Private Properties
private GhostscriptVersionInfo Version { get; set; }
#endregion
#region Construction
public PdfConverter()
{
Version = GhostscriptVersionInfo.GetLastInstalledVersion();
}
#endregion
#region Public Members
public bool ConvertToPdf(DirectoryInfo dir)
{
var d = dir;
if(!d.Exists)
return false;
var postScriptFiles = d.GetFiles("*.ps");
var pdfFiles = postScriptFiles.Select(psf => new FileInfo(Path.ChangeExtension(psf.FullName, ".pdf")));
foreach(var file in postScriptFiles) {
//ThreadPool.QueueUserWorkItem(new WaitCallback((o) => {
Process(file, new FileInfo(Path.ChangeExtension(file.FullName, ".pdf")));
//}));
}
pdfFiles.ForEach(pdf => pdf?.Refresh());
return pdfFiles.All(pdf => pdf.Exists);
}
#endregion
#region Private Helpers
private void Process(FileInfo inputFile, FileInfo outputFile)
{
Console.WriteLine($"Converting {inputFile} to {outputFile}");
var proc = new GhostscriptProcessor(Version, true);
proc.Process(GetArgs(inputFile, outputFile).ToArray(), new ConsoleStdIO(true, true, true));
}
private IEnumerable<string> GetArgs(FileInfo inputFile, FileInfo outputFile)
{
return new [] {
$"-q ",
$"-sDEVICE=pdfwrite",
$"-dSAFER",
$"-dNOPAUSE",
$"-dBATCH",
$"-sPAPERSIZE=a4",
$"-dEmbedAllFonts=true",
$"-dAutoRotatePages=/None",
$"-sOutputFile=\"{outputFile.FullName}\"",
$"-dCompatibilityLevel#1.4",
$"-c .setpdfwrite",
$"-f \"{inputFile.FullName}\""
};
}
#endregion
}
Edit:
I forgot to mention: To implement it i had to make my own GhostsdcriptStdIO class. I admit that I'm not entirely sure if I did this right. Although it does get instanciated without exceptions, and override StdOut(...) get's called, and the output is written to the console as expected. override void StdError(...) get's called as well. And also written to the console as expeted.
The Output of the error btw is:
"**** Could not open the file "c:\temp\test.pdf""
"**** Unable to open the initial device, quitting."
Here's my ConsoleStdIO class:
public class ConsoleStdIO : Ghostscript.NET.GhostscriptStdIO
{
#region Construction
public ConsoleStdIO(bool handleStdIn, bool handleStdOut, bool handleStdError) : base(handleStdIn, handleStdOut, handleStdError) { }
#endregion
#region Overrides
public override void StdError(string error)
{
var foo = Encoding.Default.GetBytes(error);
var lenght = foo.Length;
using (var err = Console.OpenStandardError()) {
if(err.CanWrite)
err.Write(foo, 0, lenght);
}
}
public override void StdIn(out string input, int count)
{
byte[] bytes = new byte[0];
using(var stdInput = Console.OpenStandardInput()) {
stdInput.Read(bytes, 0, count);
}
input = Encoding.Default.GetString(bytes);
}
public override void StdOut(string output)
{
var foo = Encoding.Default.GetBytes(output);
var lenght = foo.Length;
using (var err = Console.OpenStandardError()) {
if(err.CanWrite)
err.Write(foo, 0, lenght);
}
}
#endregion
}
Again: doing the same operation with the exact same files and arguments using gswin32c.exe works fine.
Happy Hacking
Error -100 is gs_error_Fatal, which means 'something catastrophic went wrong'. Its an indication that the program failed to start up properly and we can't tell why. The back channel may contain more information.
And indeed, the back channel tells you what's wrong:
**** Could not open the file "c:\temp\test.pdf
**** Unable to open the initial device, quitting.
Ghostscript is unable to open the output file, which means it can't open the pdfwrite device (because that requires an output file) so it aborts the operation.
There could be a number of reasons why Ghostscript can't open the output file. The first thing I'd do is trim down the number of arguments;
You don't want -q (quiet) when you are trying to debug a problem, you want all the information you can get.
I'd remove -dSAFER at least to start with, because that prevents Ghostscript accessing directories outside the current working directory and certain 'special' ones. It may well prevent you accessing the temp directory.
You don't need to set EmbedAllFonts when its the same value as the default.
You could drop the CompatibilityLevel (and note that you've used a # there instead of an =) switch, and the AutoRotatePages while getting this to work.
The "-c .setpdfwrite -f" string has been pointless for years but people still keep using it. All that does these days is slow down the start of processing, ditch it.
Finally you can try changing the backslash ('\') characters to forward slash ('/') in case your string handling is messing that up, or use double backslashes (I'd use the forward slash myself).
You should also check that c:\test\temp.pdf doesn't exist, or if it does exist is not read-only or already open in a different application.
So I solved the problem...
After taking KenS' advice I could run the application without Ghostscript (not Ghostscript.NET) giving me any errors. But it did not produce an actual PDF File.
So KenS's answer did not quite solve the problem, but since 'less is more' and since he took the time to talk to me on IRC to verify that my args in itself were correct, I'll give the answer points nonetheless.
What actually solved my was the following:
Here my original GetArgs(...)
private IEnumerable<string> GetArgs(FileInfo inputFile, FileInfo outputFile)
{
return new [] {
$"-sDEVICE=pdfwrite",
$"-dNOPAUSE",
$"-dBATCH",
$"-sPAPERSIZE=a4",
#"-sFONTPATH=" + System.Environment.GetFolderPath(System.Environment.SpecialFolder.Fonts),
$"-sOutputFile={outputFile.FullName}",
$"{inputFile.FullName}",
};
}
Someone in #csharp pointed out to me, that in c, the first argument is always the name of the command. So he suggested to just put "gs" as the first argument (as a dummy) and try... And that's what actually solved my problem.
So this is how the working GetArgs(...) looks:
private IEnumerable<string> GetArgs(FileInfo inputFile, FileInfo outputFile)
{
return new [] {
$"gs",
$"-sDEVICE=pdfwrite",
$"-dNOPAUSE",
$"-dBATCH",
$"-sPAPERSIZE=a4",
#"-sFONTPATH=" + System.Environment.GetFolderPath(System.Environment.SpecialFolder.Fonts),
$"-sOutputFile={outputFile.FullName}",
$"{inputFile.FullName}",
};
}
Currently I'm stuck making a C#-WPF-OpenGL application. I followed this http://www.codeproject.com/Articles/265903/Using-OpenGL-in-a-WPF-Application tutorial and was able to run the sample project. However, the first call to a function that's not part of OpenGL 1.1.0 Fails, i.e my code Looks like this
private void OpenGLControl_OpenGLInitialized(object Sender, OpenGLEventArgs args)
{
OpenGL gl = args.OpenGL;
GLuint glError;
while ( (glError = gl.GetError()) != OpenGL.GL_NO_ERROR )
{
Console.WriteLine( gl.GetErrorDescription(glError) );
}
Console.WriteLine( gl.GetString(OpenGL.Version) ); //Prints out 1.1.0
GLuint hVertexShader;
hVertexShader = gl.CreateShader(OpenGL.GL_VERTEX_SHADER);//Compiles, but raises an exception, also would gl.CreateProgramm() or other functions
}
Somehow this link https://github.com/dwmkerr/sharpgl/wiki/Specifying-OpenGL-Version can't help me to set the Version to 4.2.
So how can I achieve that?
Solved! I was missing RenderingContext="FBO" in MainWindow.xaml, like so
<sharpGL:OpenGLControl
Name="openGLControl"
OpenGLDraw="OpenGLControl_OpenGLDraw"
OpenGLInitialized="openGLControl_OpenGLInitialized"
Resized="openGLControl_Resized"
KeyDown="OnKeyDownHandler"
Mouse.MouseWheel="MouseWheelEventHandler"
DrawFPS="True" RenderContextType="FBO" FrameRate="20"/>
Ok, I will preface this by saying I did do a look up and read a lot of similar questions and answers on here before posting this.
Background Info
Using Visual Studio Professional 2013
Goal:
This project is for my own amusement and to try to learn as much as I can.
I have a native C++ header file called BinaryTree.h which is just a simple data structure that uses recursive logic to build a binary search tree. It works quite well on its own.
I want to build a GUI in C# that uses this. (Its not really useful or practical, I just choose it, well because I wanted to. Also, while the logic inside the binary tree class is complex(ish), I only need to call 2 methods, a addNode method, and a toString method which return the max depth and number of nodes).
I choose using a c++/cli wrapper to accomplish this. Everything seemed to go well, the build was successful and a .dll file was created in the debug directory of my project.
Now, I started in on the C# side of things. I added the .dll file to references. However, when I typed in " using filename.dll;" I got an error saying "Type or namespace not found...".
To reiterate I did some researching. I found (it seemed in VS2010) that different target frameworks could cause this error. I checked mine, targets for both were net4.5, so that is not the problem.
Here is the code from my c++/cli wrapper. Perhaps it has something to do with using templates? Any help is appreciated.
#pragma once
#include "D:\Schoolwork 2015\Test Projects\CPPtoC#WrapperTest\CPPLogic\CPPLogic\BinaryTree.h"
using namespace System;
namespace BinaryTreeWrapper {
template<class Data>
public ref class BinaryTreeWrapperClass
{
public:
BinaryTreeWrapperClass(){ tree = new BinaryTree(); }
~BinaryTreeWrapperClass(){ this->!BinaryTreeWrapperClass(); }
!BinaryTreeWrapperClass(){ delete tree; }
//methods
void wrapperAddNode(Data)
{
tree->addNode(Data);
}
std::string wrapperToString()
{
return tree->toString();
}
private:
BinaryTree* tree;
};
}
Screenshot of the error:
EDIT
Ok, so here is a weird thing... my original file built just fine with the new code and produced a .dll file. However, I decided to try a fresh project since the namespace was still not being found. Upon moving the code over and trying to build, I've run into 4 errors:
Error 1 error C2955: 'BinaryTree' : use of class template requires template argument list
Error 2 error C2512: 'BinaryTree' : no appropriate default constructor available
Error 3 error C2662: 'void BinaryTree::addNode(Data)' : cannot convert 'this' pointer from 'BinaryTree' to 'BinaryTree &'
Error 4 error C2662: 'std::string BinaryTree::toString(void) const' : cannot convert 'this' pointer from 'BinaryTree' to 'const BinaryTree &'
I copied the code exactly, only changing the namespace to "TreeWrapper' and the class name to 'TreeWrapperClass'.
To help, I've include a snippet from my BinaryTree.h file. There is a bunch more that defines the 'NODE' class, but I didn't want to clutter it up more than i needed.
After further investigation, it appears the problem lies with using 'generic'. If I switch it all to 'template' it builds just fine, but then it can't be used as a reference in C# (getting the namespace error). I built a test project using very simple methods (no templates) and was able to use the .dll wrapper I made in C#. So the problem lies with templates and generics.
Last Edit
I've found if I change the code to initiate the template as 'int' it works just fine, and I can use it in C#. For example:
...
BinaryTreeWrapperClass(){ tree = new BinaryTree<int>(); }
....
private:
BinaryTree<int>* tree;
BinaryTree.h
template<class Data>
class BinaryTree
{
private:
Node<Data>* root;
unsigned int nNodes;
unsigned int maxDepth;
unsigned int currentDepth;
void traverse(Node<Data>*& node, Data data);
public:
BinaryTree();
~BinaryTree();
void addNode(Data);
std::string toString() const
{
std::stringstream sstrm;
sstrm << "\n\t"
<< "Max Depth: " << maxDepth << "\n"
<< "Number of Nodes: " << nNodes << "\n";
return sstrm.str(); // convert the stringstream to a string
}
};
template<class Data>
BinaryTree<Data>::BinaryTree() //constructor
{
//this->root = NULL;
this->root = new Node<Data>(); //we want root to point to a null node.
maxDepth = 0;
nNodes = 0;
}
template<class Data>
BinaryTree<Data>::~BinaryTree() //destructor
{
}
template<class Data>
void BinaryTree<Data>::addNode(Data data)
{
traverse(root, data); //call traverse to get to the node
//set currentDepth to 0
currentDepth = 0;
}
template<class Data>
void BinaryTree<Data>::traverse(Node<Data>*& node, Data data)
{
//increment current depth
currentDepth++;
if (node == NULL) //adds new node with data
{
node = new Node<Data>(data);
//increment nNode
nNodes++;
//increment maxDepth if current depth is greater
if (maxDepth < currentDepth)
{
maxDepth = currentDepth - 1; //currentDepth counts root as 1, even though its 0;
}
return;
}
else if (node->getData() >= data) //case for left, getData must be bigger. The rule is, if a number is equal to getData or greater, it is added to the left node
{
Node<Data>* temp = node->getLeftNode();
traverse(temp, data); //recursive call, going down left side of tree
node->setLeftNode(temp);
}
else if (node->getData() < data) //case for right, getData must be less
{
Node<Data>* temp = node->getRightNode();
traverse(temp, data);
node->setRightNode(temp);
}
return;
}
You're declaring a template, but are not actually instantiating it. C++/CLI templates are just like C++ templates - if you don't instantiate them, they just don't exist outside of the compilation unit.
You're looking for generics here (yes, C++/CLI has both templates and generics). And here's how you declare a generic in C++/CLI:
generic<class Data>
public ref class BinaryTreeWrapperClass
{
// ...
}
But you'll get stuck at this point for several reasons.
First, I'll include the parts which are OK:
generic<class Data>
public ref class BinaryTreeWrapperClass
{
public:
BinaryTreeWrapperClass(){ tree = new BinaryTree(); }
~BinaryTreeWrapperClass(){ this->!BinaryTreeWrapperClass(); }
!BinaryTreeWrapperClass(){ delete tree; }
private:
BinaryTree* tree;
};
You've got this right.
Next, let's look at:
std::string wrapperToString()
{
return tree->toString();
}
That's no good, since you're returning an std::string - you don't want to use that from C#, so let's return a System::String^ instead (using marshal_as):
#include <msclr/marshal_cppstd.h>
System::String^ wrapperToString()
{
return msclr::interop::marshal_as<System::String^>(tree->toString());
}
Here, that's much better for use in C#.
And finally, there's this:
void wrapperAddNode(Data)
{
tree->addNode(Data);
}
See... here you'll have to do some real interop. You want to pass a managed object to a native one for storage. The GC will get in your way.
The GC is allowed to relocate any managed object (move it to another memory location), but your native code is clueless about this. You'll need to pin the object so that the GC won't move it.
There are several ways to do this, and I don't know what BinaryTree::addNode looks like, but I'll just suppose it's BinaryTree::addNode(void*).
For long-term object pinning, you can use a GCHandle.
The full code looks like this:
generic<class Data>
public ref class BinaryTreeWrapperClass
{
public:
BinaryTreeWrapperClass()
{
tree = new BinaryTree();
nodeHandles = gcnew System::Collections::Generic::List<System::Runtime::InteropServices::GCHandle>();
}
~BinaryTreeWrapperClass(){ this->!BinaryTreeWrapperClass(); }
!BinaryTreeWrapperClass()
{
delete tree;
for each (auto handle in nodeHandles)
handle.Free();
}
void wrapperAddNode(Data data)
{
auto handle = System::Runtime::InteropServices::GCHandle::Alloc(
safe_cast<System::Object^>(data),
System::Runtime::InteropServices::GCHandleType::Pinned);
nodeHandles->Add(handle);
tree->addNode(handle.AddrOfPinnedObject().ToPointer());
}
System::String^ wrapperToString()
{
return msclr::interop::marshal_as<System::String^>(tree->toString());
}
private:
BinaryTree* tree;
System::Collections::Generic::List<System::Runtime::InteropServices::GCHandle>^ nodeHandles;
};
This allocates a GCHandle for each node, and stores it in a list in order to free it later. Freeing a pinned handle releases a reference to the object (so it becomes collectable if nothing else references it) as well as its pinned status.
After digging around I think I found the answer, although not definitively, so if anyone can chime in I would appreciate it. (Apologies in advance for any misuse of vocabulary, but I think I can get the idea across).
It seems the problem lies in the fundamental difference between templates in native C++ and generics. Templates are instantiated at compilation, and are considered a type. They cannot be changed at runtime, whereas generics can. I don't think there is an elegant way to solve that.
At least I accomplished one goal of my project, which was learning as much as I can haha. I was able to get c++/cli wrappers working for things without templates, and if I choose a type for the template before building the .dll (see above)
If anyone else has an idea please let me know.
Suppose I am watching something in VS2008 and I want to search the object I'm watching for an instance of a particular value.
For example, say I'm watching this and I want to search for a control called mycontrol inside it or I want to search for a string that currently has the value of "Hello World".
I know it's kind of a brute force way of doing things, but I find it would be a quick way of identifying where things are going wrong whilst debugging. Warning: I'm about to swear... When I was using PHP, I could see all variables that were currently set by using get_defined_vars(). It was dirty, but it was quick and I'd like to know if there's something similar I can do in VS.
Sorry if I've been a little vague, I'd be happy to elaborate if you have questions.
Cheers
Iain
Edit:
What I'm actually tring to do is interrogate the current state of the application and quickly search for the various classes that I want to debug. What I'm doing is trying to debug where I don't have the source code (I'm writing a control for a CMS). So I know what the value of something should be, but I don't know where in the structure it exists - that's what I want to find.
e.g. An exeption is thrown by the application because foo should be a list of the type bar. I want to find out where foo is defined so I can look around and see what the other variables in the same class are set to.
Sorry again, I'm finding it hard to explain :(
Edit #2:
I find a good tree might help me visualise it better...
Quickwatch
-this
-var1
+var1a
+var1b
-var1c
-base
-foo = "Hello World"
+var1ca
+var2
+var3
In the above, how would I quickly drill down through the structure to find foo?
It sounds like you want a conditional breakpoint:
When the breakpoint location is
reached, the expression is evaluated
and the breakpoint is hit only if the
expression is true or has changed.
Create a new breakpoint, right-click on it, and select "Condition..." Enter the condition you'd like to wait for. It'll be something like:
this.MyString == "Hello World"
EDIT: Ok, I understand now you want to interrogate another, running application. Assuming it was built in a managed language, you may be interested in Hawkeye:
Hawkeye is the only .Net tool that
allows you to view, edit, analyze and
invoke (almost) any object from a .Net
application. Whenever you try to
debug, test, change or understand an
application, Hawkeye can help.
Free. Not been updated in a while.
I wrote this the other day. It did the job well enough (however it is only some utility code for debugging, so use at your own risk --> the design is pretty bad >_< ). Dumps out the fields and iterates downwards. It might not be perfect, but it solved my problem at the time.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
public static class Dumper
{
public class Dump
{
public Dump(bool spacesInsteadOfTab)
{
_spacesIndeadOfTab = spacesInsteadOfTab;
}
private readonly StringBuilder _sb = new StringBuilder();
public string Result
{
get
{
return _sb.ToString();
}
}
private readonly bool _spacesIndeadOfTab;
private int _currentIndent;
public int CurrentIndent
{
get
{
return _currentIndent;
}
set
{
_currentIndent = value > 0 ? value : 0;
}
}
public void IncrementIndent()
{
CurrentIndent += 1;
}
public void DecrementIndent()
{
CurrentIndent -= 1;
}
private void AppendIndent()
{
if (_spacesIndeadOfTab)
_sb.Append(' ', _currentIndent * 4);
else
_sb.Append('\t', _currentIndent);
}
public void Log(string logValue)
{
AppendIndent();
_sb.AppendLine(logValue);
}
public void Log(string logValue, params object[] args)
{
AppendIndent();
_sb.AppendFormat(logValue, args);
_sb.AppendLine();
}
}
public static Dump TakeDump(object objectToDump, int maxDepth)
{
Dump result = new Dump(false);
int currentDepth = 0;
TakeDump(ref result, ref currentDepth, maxDepth, objectToDump);
return result;
}
private static void TakeDump(ref Dump result, ref int currentDepth, int maxDepth, object objectToDump)
{
currentDepth++;
if (currentDepth > maxDepth)
{
result.IncrementIndent();
result.Log("MaxDepth ({0}) Reached.", maxDepth);
result.DecrementIndent();
return;
}
var objectType = objectToDump.GetType();
result.Log("--> {0}", objectType.FullName);
result.IncrementIndent();
var fields = objectType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (fields.Count() == 0)
result.Log("No fields");
foreach (var fieldInfo in fields)
{
var fieldValue = fieldInfo.GetValue(objectToDump);
if (fieldValue == null)
result.Log("{0} is null", fieldValueType.FullName, fieldInfo.Name);
var fieldValueType = fieldValue.GetType();
if (fieldValueType.IsValueType)
result.Log("{2} as {0} (ToString: {1})", fieldValueType.FullName, fieldValue.ToString(), fieldInfo.Name);
else
TakeDump(ref result, ref currentDepth, maxDepth, fieldValue);
}
result.DecrementIndent();
}
}
It sounds like you are envisioning a feature which would descend down the tree presented in the debugger UI looking for a typed in value. This is not a feature of the debugger at this time (although at first glance it sounds handy). It would have problems though in cases where the expression had infinite expansions.
Circular references for instance will cause an infinite expansion. Those are fairly easy to track down but there are more nefarious tricks which can be done to make infinite expansion harder / impossible to track. True we could probably control for depth and such ...
I think your best bet is to write a reflection based searching mechanism with a depth control mechanism. Then call this API from the debugger window.