How to open MFC dialog from WPF application? - wpf

I have created dialog in .dll project. Now I want to open that dialog from WPF application by clicking on button. Following is the code for dialog:
TestDialog.h:
class CTestDialog : public CDialogEx
{
DECLARE_DYNAMIC(CTestDialog)
public:
CTestDialog(CWnd* pParent = NULL); // standard constructor
virtual ~CTestDialog();
// Dialog Data
enum { IDD = 1000 };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
DECLARE_MESSAGE_MAP()
};
TestDialog.cpp:
#include "stdafx.h"
#include "MFCDll.h"
#include "TestDialog.h"
#include "afxdialogex.h"
IMPLEMENT_DYNAMIC(CTestDialog, CDialogEx)
CTestDialog::CTestDialog(CWnd* pParent /*=NULL*/)
: CDialogEx(CTestDialog::IDD, pParent)
{
}
CTestDialog::~CTestDialog()
{
}
void CTestDialog::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CTestDialog, CDialogEx)
END_MESSAGE_MAP()
I have created export function which create object of dialog and open that dialog by calling the DoModel() function.
extern "C" void PASCAL EXPORT ShowDialogFromDLL()
{
CTestDialog dlg;
theApp.m_pMainWnd = &dlg;
dlg.DoModal();
}
After that I am calling this export function from WPF form following is the code for WPF Form.
MainWindow.xaml.vb:
namespace MainApp
{
public partial class MainWindow : Window
{
[DllImport("MFCDll.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern void ShowDialogFromDLL();
public MainWindow()
{
InitializeComponent();
}
private void btnShow_Click(object sender, RoutedEventArgs e)
{
ShowDialogFromDLL();
}
}
}
But now when I call ShowDialogFromDLL(); after clicking button.It will throw me exception as
Microsoft Visual C++ Debug Library
Debug Assertion Failed!
Program: E:\EDR1\Test\MainApp\bin\Debug\MainApp.vshost.exe
File: f:\dd\vctools\vc7libs\ship\atlmfc\include\afxwin1.inl
Line: 24
For information on how your program can cause an assertion failure, see the Visual C++ documentation on asserts.
(Press Retry to debug the application)
Above error is coming when I call dlg.DoModal(); method.

This should work -
extern "C" __declspec(dllexport) void __stdcall ShowDialogFromDLL()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
CTestDialog dlg;
dlg.DoModal();
}
When building a regular DLL that dynamically links to MFC, you need to use the macro AFX_MANAGE_STATE to switch the MFC module state correctly.

Related

Accessing WinForms member properties via delegates

I was somewhat surprised that can't easily access the properties of the WinForms class with functions you add as bublic, years ago with Borland VCL this wasn't an issue. Apparently 'delegates' is said to be the right keyword ... and I've been struggling with this for days now and can't get it to work. Is my approach beyond or is it just that the plug is not plugged in? Via the event handler, the forms property is successfully changed. Now i'm asking for a hint to transfer this capability down to sub1()?
#include "form1.h"
#include <Windows.h>
using namespace System;
using namespace System::Windows::Forms;
void sub1(void);
public delegate void Deleg(String^ st);
[STAThread]
void Main() // array<System::String^> ^args)
{
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
App1::Form1 form;
Deleg^ Deleg1;
Application::Run(% form);
// Deleg1 += gcnew Deleg(form,&App1::Form1::newTxt); //?
}
void App1::Form1::button1_Click(System::Object^ s, System::EventArgs^ e)
{
String^ st = "ABC";
App1::Form1::newTxt(st);
Refresh();
sub1();
}
void sub1(void)
{
String^ s = "DEF";
// Deleg1(s);
}
I tried to copy some examples I found on the net, but apparently I don't hit the core.

Enable only one instance of WPF application

I have WPF allocation and I want to be able to open only one instance of my application.
So i have this 2 classes:
public sealed class SingleInstance
{
public static bool AlreadyRunning()
{
bool running = false;
try
{
// Getting collection of process
Process currentProcess = Process.GetCurrentProcess();
// Check with other process already running
foreach (var p in Process.GetProcesses())
{
if (p.Id != currentProcess.Id) // Check running process
{
if (p.ProcessName.Equals(currentProcess.ProcessName) == true)
{
running = true;
IntPtr hFound = p.MainWindowHandle;
if (User32API.IsIconic(hFound)) // If application is in ICONIC mode then
User32API.ShowWindow(hFound, User32API.SW_RESTORE);
User32API.SetForegroundWindow(hFound); // Activate the window, if process is already running
break;
}
}
}
}
catch { }
return running;
}
}
And:
public class User32API
{
[DllImport("User32.dll")]
public static extern bool IsIconic(IntPtr hWnd);
[DllImport("User32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("User32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public const int SW_RESTORE = 9;
}
App.xaml:
protected override void OnStartup(StartupEventArgs e)
{
if (SingleInstance.AlreadyRunning())
App.Current.Shutdown(); // Just shutdown the current application,if any instance found.
base.OnStartup(e);
}
So with this solution only one instance is allow but in case the user try to open another instance i can see that in the task bar i have new icon of my application, this icon automatically close when the mouse is over but I want to prevent this icon to be show so I remove this from App.xaml:
StartupUri="MainWindow.xaml"
And now my application not started (probably started but i cannot see it).
Any chance to achieve what I want ?
I need to call MainWindow but I don't know from where
UPDATE
So I try this approach:
protected override void OnStartup(StartupEventArgs e)
{
if (SingleInstance.AlreadyRunning())
App.Current.Shutdown(); // Just shutdown the current application,if any instance found.
base.OnStartup(e);
new MainWindow().Show();
}
And still i can see the second (and third and so...) icons when the user try to open another instance

WM_HELP stops being send when vtk is started or spy++ is running

In our software we occasionally use sending WM_HELP via SendMessage api to a control. Normally the "HelpRequested" event is then fired (or up in the parent hierarchy until an event handler is registered).
We included an external complex 3d visualization library called "VTK" and after that, this Message passing does not work anymore. In trying to track down the problem I used Spy++ to see whether the message is shown there and realized that running spy++ is generating the same problem! (Also without any vtk stuff). It can be shown with this little Program:
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace TestHelp
{
public partial class Form1 : Form
{
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
struct HelpInfo
{
public uint cbSize;
public int iContextType;
public int iCtrlID;
public int hItemHandle;
public int dwContextID;
public int MouseX;
public int MouseY;
}
[DllImport("user32.DLL", EntryPoint = "SendMessage", SetLastError = true)]
private static extern int SendHelpMessage(int hWnd, uint Msg, uint wparam, ref HelpInfo helpinfo);
public static void RaiseHelp(Control ctrl)
{
HelpInfo helpInfo = new HelpInfo
{
cbSize = 48,
iContextType = 1,
iCtrlID = 0,
hItemHandle = ctrl.Handle.ToInt32(),
dwContextID = 0,
MouseX = 10,
MouseY = 10,
};
var res = SendHelpMessage(ctrl.Handle.ToInt32(), 0x053, 0, ref helpInfo);
Debug.WriteLine($"SendMessage returns:{res}");
}
public Form1()
{
InitializeComponent();
button1.HelpRequested += (sender, hlpevent) => { Trace.WriteLine("HelpRequested called"); };
timer = new Timer() {Interval = 1000, Enabled = true};
timer.Tick += (sender, args) => RaiseHelp(button1);
}
private Timer timer;
}
}
The form only contains a single button named "button1".
When you start in debugger you see "HelpRequested called" in Output window every second. When you start Spy++, nothing more, just start, it will stop that! When closing spy++ it continues to work again. Does everyone have an explanation for this behaviour? What is Spy++ doing with my application? My hope is that the same mechanism is responsible for the same problem with vtk (there only in-process, though).
Of course, using win32 api SendMessage may seem inappropriate for a WinForms application, but we don't have time now to refactor all that stuff, and I nevertheless want to understand whats happening here!
Btw.: user window message are not affected (WM_USER to 0x7FFF), checked by overriding WndProc. WM_HELP also does not show in WndProc while spy++ is running, btw.
Problem was wrong size for HelpInfo.cbSize. In 64-bit mode it is 40, in 32-bit it is 28. Yes I should use sizeof(), but this is only allowed in "unsafe" mode.
But how the hell spy++ or VTK interfere with this?

Passing command line arguments to a windows form file in C++/CLI

I've been learning C++/CLI for a few months now, but no matter what I try, I can't seem to fix a problem I'm having.
I need to pass either a String^, Array^, or an ArrayList^ data type from main.cpp to Form1.h. I've attempted to make variables global in main.cpp and then call the variable using extern. However, this will not work for String, Array, and ArrayList data types.
How would I go about doing this? Thanks in advance. Here is a paraphrase of my code:
//main.cpp
bool LoadFileFromArgument = false; //A boolean can be declared global
String^ argument; //this will not pass from main.cpp to Form1.h
int main(array<System::String ^> ^args)
{
String ^ argument = args[1]
// Enabling Windows XP visual effects before any controls are created
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
// Create the main window and run it
Application::Run(gcnew Form1());
return 0;
}
//Form1.h
public ref class Form1 : public System::Windows::Forms::Form
{
public:
Form1(void)
{
InitializeComponent();
//
//TODO: Add the constructor code here
//
extern bool LoadFileFromArgument; //is grabbed without error
extern String^ argument; //will not work
Here is the error:
error C3145: 'argument' : global or static variable may not
have managed type 'System::String ^'
may not declare a global or static variable,
or a member of a native type that refers to objects in the gc heap
Can you not create an overloaded constructor for the form. i.e.
public ref class Form1 : public System::Windows::Forms::Form
{
public:
Form1(String^ argument)
{
InitializeComponent();
//
//TODO: Add the constructor code here
//
// Use "argument" parameter as req'd.
}
Form1(void)
{
//....usual constructor here...
//..etc...
then from main
// Create the main window and run it
Application::Run(gcnew Form1(argument));

Where should I put initialization code so it would be executed in before VS initializes my control in design mode

I have a method Translate extension which searches for a translation. Normally translations are loaded in Window constructor (I tried in App.Setup too). No if i run the application all the translations are displayed correctly, but when opening a user control all translations are gone.
So the question is where do I put my initialization code so it would be executed before VS initializes design window
it should be default constructor
Either the class constructor (or code called from it) or some static member initialized by a static constructor.
Option 1:
public partial class MyUserControl : UserControl
{
int thisWillWork = 1;
int thisWillAlsoWork;
public MyUserControl()
{
thisWillAlsoWork = 1;
InitializeComponents();
}
Option 2:
public class SomeOtherClass
{
public static int YouCanUseThis = 1;
public static int AndThisAlso;
static SomeOtherClass()
{
AndThisAlso = 1;
}
}

Resources