I know DDE is old fashion outdated technology. But I am creating a C# Windows Form application to send data to Excel file for research purpose. I use Win32 DDE functions and below is what I have tried so far.
Pinvoke signatures:
[DllImport("user32.dll", EntryPoint = "DdeInitialize")]
public static extern int DdeInitialize(out uint pidInst, IntPtr pfnCallback, IntPtr afCmd, IntPtr ulRes);
[DllImport("user32.dll", EntryPoint = "DdeConnect")]
private static extern IntPtr DdeConnect(uint idInst, IntPtr hszService, IntPtr hszTopic, IntPtr pCC);
[DllImport("user32.dll", EntryPoint = "DdeCreateStringHandle")]
private static extern IntPtr DdeCreateStringHandle(uint idInst, string psz, int iCodePage);
public Form1()
{
InitializeComponent();
Process application = new Process();
application.StartInfo.FileName = #"C:\Users\xxx\Desktop\DDE_Client.xlsx";
application.Start();
uint instId;
DdeInitialize(out instId, application.MainWindowHandle, IntPtr.Zero, IntPtr.Zero);
IntPtr sPtr = DdeCreateStringHandle(instId, "Excel", 1004);
IntPtr tPtr = DdeCreateStringHandle(instId, "Sheet1", 1004);
IntPtr channel = DdeConnect(instId, sPtr, tPtr, (IntPtr)null);
}
DdeConnect returns non-zero value which indicates the connection is successful.
My question:
How to send a string value to the excel sheet? For example, I want to send "Name" to excel cell A1. Which DDE function should I use for the task?
You are trying to use DDEML and it is indeed hard to find decent DDEML examples because many people who actually needed to use DDE had done so directly using raw Windows Messages.
You will have to first start client transaction using DdeClientTransaction and then corresponding api like DdeGetData or DdeAddData, DdeAccessData etc to access or get the data.
You can find more details about DDEML in the MS Documentation here.
But since you are already using C# I highly recommend that it would be much better to just add a reference to the Microsoft Excel or Office Object Library in COM Assemblies section and use Microsoft.Office.Interop.Excel;
using Microsoft.Office.Interop.Excel;
Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
Related
I have an input web form that will take in 45 user entered data, and then once the user has entered all data he/she can click a button that takes the 45 inputs, does a calculation via a DLL file, and then places all the output information on a different web form page. My question is how do I collect all the 45 input data, and place them into an array which both the DLL can access and the output form? I believe the back end of the dll is written in fortran and the original developer passed the visual basic array into the dll by using "ByRef ArrayInput As Double".
Since the original was Fortran you need to use standard calling convention. Try code like below :
class Program
{
[DllImport("MyDll.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
private static extern uint CallWithDouble(IntPtr data);
static void Main(string[] args)
{
double[,] myDoubleArray = new double[9, 5];
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(myDoubleArray));
Marshal.StructureToPtr(myDoubleArray, ptr, true);
uint results = CallWithDouble(ptr);
}
}
I work in a place where directories have such a looong name and are in such a looong tree.
And I'm having problems with too long path names for folders in an external applicatoin (I can't change this external application, but I can give it shortened path names).
I know Microsoft operating systems can shorten path names such as transforming C:\TooLongName\TooLongSubDirectory in something like C:\TooLon~1\TooLon~1.
But how can I do this in C# and still keep the nave valid and usable?
PS: I'm not using the standard FileInfo and DirectoryInfo classes, I'm using just strings that will be sent to an external application that I cannot change in any way.
If you are unable to use the long path support build into Windows 10 you are able to use the Win32 command GetShortPathName . In order to generate a suitable path.
class Program
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern uint GetShortPathName(
[MarshalAs(UnmanagedType.LPTStr)]
string lpszLongPath,
[MarshalAs(UnmanagedType.LPTStr)]
StringBuilder lpszShortPath,
uint cchBuffer);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern uint GetShortPathName(string lpszLongPath, char[] lpszShortPath, int cchBuffer);
static void Main(string[] args)
{
StringBuilder builder = new StringBuilder(260);
var shortPath = GetShortPathName(#"C:\Projects\Databases\ReallllllllllllllyLOOOOOOOOOOOOOOOOOOOOOONGPATHHHHHHHHHHH\StillllllllllllllllllGOoooooooooooooooooooooooing", builder, (uint)builder.Capacity);
Console.WriteLine(builder.ToString());
Console.ReadKey();
}
}
Produces C:\Projects\DATABA~1\REALLL~1\STILLL~1
When I was searching on Google I found a useful class which let us change the icon of any .exe file using the following line of code :
WindowsFormsApplication1.IconInjector.InjectIcon("myfile.exe", "myicon.ico", 200, 1);
Where 200 and 1 are respectively icon GroupID and icon BaseID which I can determine using Resource Hacker. In this case the file's icon changes successfully without corrupting the file.
So i planned to use this class on my program which is a SFX / Software protector, the output file always hasn't an icon, all what I can see on Resource hacker is the below :
i can't see icon group id nor the base id, anyway, (I don't know what to put instead of 200 and 1 in this case) So I tried to change the icon using the same line of code mentioned above, I used the following line of code (same as above):
WindowsFormsApplication1.IconInjector.InjectIcon("myfile.exe", "myicon.ico", 200, 1);
The file icon was successfully changed but the file doesn't work anymore!
When I tried to reopen the file using ResourceHacker, I found the below:
It seems that the icon resources were successfully added, but i can't figure out why the file doesn't work anymore, it seems that is corrupted.
Any help would be appreciated.
Note : I tried using this class with unprotected file and it works like a charm!
The class I am using is the below:
// IconInjector.cs
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace WindowsFormsApplication1
{
/// <summary>
/// IconInjectorクラスの定義
/// </summary>
public class IconInjector
{
[DllImport("kernel32.dll", SetLastError = true)]
//static extern bool UpdateResource(IntPtr hUpdate, string lpType, string lpName, ushort wLanguage, IntPtr lpData, uint cbData);
static extern int UpdateResource(IntPtr hUpdate, uint lpType, uint lpName, ushort wLanguage, byte[] lpData, uint cbData);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr BeginUpdateResource(string pFileName,
[MarshalAs(UnmanagedType.Bool)]bool bDeleteExistingResources);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard);
/// <summary>
/// アプリケーションのメイン エントリ ポイントです。
/// </summary>
[STAThread]
public static void InjectIcon(string execFileName, string iconFileName, uint iconGroupID, uint iconBaseID)
{
const uint RT_ICON = 3;
const uint RT_GROUP_ICON = 14;
// アイコンファイルの読み込み
IconFile iconFile = new IconFile();
iconFile.Load(iconFileName);
// リソースの更新開始
IntPtr hUpdate = BeginUpdateResource(execFileName, false);
Debug.Assert(hUpdate != IntPtr.Zero);
// RT_GROUP_ICON 書き込み
byte[] data = iconFile.CreateIconGroupData(iconBaseID);
UpdateResource(hUpdate, RT_GROUP_ICON, iconGroupID, 0, data, (uint)data.Length);
// RT_ICON書き込み
for (int i = 0; i < iconFile.GetImageCount(); i++)
{
byte[] image = iconFile.GetImageData(i);
UpdateResource(hUpdate, RT_ICON, (uint)(iconBaseID + i), 0, image, (uint)image.Length);
}
// リソースの更新終了
EndUpdateResource(hUpdate, false);
}
}
}
Any help or suggestion on adding the icon to the protected file without corrupting it?
It sounds like the protection application is verifying that the contents of the file haven't been tampered with. Injecting an icon is definitely a form of tampering, and unless the protection software is updated to ignore it, it will always fail. Alternatively if you own the protection software you could update it to not strip the icons.
I just experienced the same issue with a 7zip Self-Extractor exe.
Updating the icon of the 7zS.sfx (instead of the exe) before creating the Self-Extractor exe does the trick and the exe is not corrupted.
Your application's icon can be added to this executable with a tool like Resource Hacker.
and visit http://georezo.net/jparis/MI_Enviro/Icons/adding_w_RH.htm
When I click a button on a Windows Forms form, I would like to open a Notepad window containing the text from a TextBox control on the form.
How can I do that?
You don't need to create file with this string. You can use P/Invoke to solve your problem.
Usage of NotepadHelper class:
NotepadHelper.ShowMessage("My message...", "My Title");
NotepadHelper class code:
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace Notepad
{
public static class NotepadHelper
{
[DllImport("user32.dll", EntryPoint = "SetWindowText")]
private static extern int SetWindowText(IntPtr hWnd, string text);
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
public static void ShowMessage(string message = null, string title = null)
{
Process notepad = Process.Start(new ProcessStartInfo("notepad.exe"));
if (notepad != null)
{
notepad.WaitForInputIdle();
if (!string.IsNullOrEmpty(title))
SetWindowText(notepad.MainWindowHandle, title);
if (!string.IsNullOrEmpty(message))
{
IntPtr child = FindWindowEx(notepad.MainWindowHandle, new IntPtr(0), "Edit", null);
SendMessage(child, 0x000C, 0, message);
}
}
}
}
}
References (pinvoke.net and msdn.microsoft.com):
SetWindowText: pinvoke | msdn
FindWindowEx: pinvoke | msdn
SendMessage: pinvoke | msdn
Try this out:
System.IO.File.WriteAllText(#"C:\test.txt", textBox.Text);
System.Diagnostics.Process.Start(#"C:\test.txt");
Save the file to disk using File.WriteAllText:
File.WriteAllText("path to text file", myTextBox.Text);
Then use Process.Start to open it in notepad:
Process.Start("path to notepad.exe", "path to text file");
For non ASCII user.
[DllImport("User32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
private static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
Based on #Peter Mortensen answer
Add CharSet = CharSet.Unicode to the attribute for supporting Unicode characters
I was using the NotepadHelper solution until I discovered it doesn't work on Windows 11. Writing the file to disk and starting with the default text editor seems to be the best solution. This has already been posted, but I discovered you need to pass UseShellExecute=true.
System.IO.File.WriteAllText(path, value);
System.Diagnostics.ProcessStartInfo psi = new() { FileName = path, UseShellExecute = true };
System.Diagnostics.Process.Start(psi);
I write to the System.IO.Path.GetTempPath() folder and run a cleanup when the application exits - searching for a unique prefix pattern for file names used by my app. Something like this:
string pattern = TempFilePrefix + "*.txt";
foreach (string f in Directory.EnumerateFiles(Path.GetTempPath(), pattern))
{
File.Delete(f);
}
I am making use of an external unmanaged dll using PInvoke and the DllImport attribute. eg.
[DllImport("mcs_apiD.dll", CharSet = CharSet.Auto)]
private static extern byte start_api(byte pid, byte stat, byte dbg, byte ka);
I am wondering if it is possible to alter the dll file details (mcs_apiD.dll in this example) dynmically in some manner, if for instance I wanted to build against another dll version
Yes this is possible, you'll have to do part of the job that the P/Invoke marshaller does. Loading the DLL and finding the entry point of the exported function. Start by declaring a delegate whose signature matches the exported function:
private delegate byte start_api(byte pid, byte stat, byte dbg, byte ka);
Then use code like this:
using System.ComponentModel;
using System.Runtime.InteropServices;
...
static IntPtr dllHandle;
...
if (dllHandle == IntPtr.Zero) {
dllHandle = LoadLibrary("mcs_apiD.dll");
if (dllHandle == IntPtr.Zero) throw new Win32Exception();
}
IntPtr addr = GetProcAddress(dllHandle, "_start_api#16");
if (addr == IntPtr.Zero) throw new Win32Exception();
var func = (start_api)Marshal.GetDelegateForFunctionPointer(addr, typeof(start_api));
var retval = func(1, 2, 3, 4);
...
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadLibrary(string name);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string name);
Lots of ways to get this wrong of course. Do note that you have to use the actual exported name from the DLL, you no longer get the help from the P/Invoke marshaller to help with name decoration. Use dumpbin.exe /exports on the DLL if you are not sure what the export name looks like.
you can't change the name of the dll but you can alter the path of the library being loaded (like by reading it from the registry or a configuration file) and load it manually with LoadLibrary kernel32's function: see my answer there.