-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathMeasure.cmd
More file actions
440 lines (413 loc) · 22 KB
/
Copy pathMeasure.cmd
File metadata and controls
440 lines (413 loc) · 22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
/*&cls&@echo off&Title SumatraPDF+ Measure Tool
cd /d "%~dp0" & echo Compiling "%~dpn0.exe"
set "CSC=%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\csc.exe"
if not exist "%CSC%" echo Compiler not found & pause & exit /b
::Prepare the Icon/BMP/ICO/PNG graphics as a 24 px X 24 px RAW PNG.Base64
>icon.b64 echo iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAGvSURBVEhLY/x/kv8/AwXArnE3mD5U7wqm0QETlCYLwAzP85gHprEBsi1ANjzEfDmYjQ2QZQGxhoMAyRYgG75U8DGD/CFuMB8XIMkCZMOD2I0Y1n7zZwhm0mEIvGUDFscGiLYA3XAY6OEJYbjz9DLDtL9tUBFUQFQyxWU4DJx4eJMhWHApw3O771ARBCDoA0KGg4CFvDqDGaMcVl/gtYAYw2Gg/J0tQ+eRTigPAXBagG44c3oamA+ikTEoeEA0yBd8jKwYvsBqAS7DYeDvzFlwbN3WC6ZBama+D2C48PICVBUEYFiAzXCQAcgAJAaz9GhVMZgNokG+2PdgH1gcBlBSET7D0S0CBQ3M9ciA500+wzcXNigPyQe4IjRj2SIG+75OBiddLbir8YEMDluUeAD7AJfhyADmA2RL0F0PAiCfVQnsZThgfx/MZ7T1OgUOInyGkwKwWsD0DxJS/5j+gWlqApz5gFoAHkTYqjxY3OCqDvHJg+QObzNmYDq8zRQqhAlAQQcLPmyAkDwogGgaRPYeZxkYGRj+/7f1Oo0zGPABh/q9YPpAozOYxgZoHMkMDACWhMtEKtc7WwAAAABJRU5ErkJggg==
::Convert first into an App.ico and keep base64 for internal conversion
>makeico.cs echo using System; using System.IO; class M { static void Main() {
>>makeico.cs echo var p = Convert.FromBase64String(File.ReadAllText("icon.b64")); using (var f = File.Create("app.ico")) { f.Write(new byte[]{0,0,1,0,1,0,24,24,0,0,1,0,32,0},0,14); W(f,p.Length); W(f,22); f.Write(p,0,p.Length); } } static void W(Stream s,int v){s.WriteByte((byte)v);s.WriteByte((byte)(v^>^>8));s.WriteByte((byte)(v^>^>16));s.WriteByte((byte)(v^>^>24)); } }
"%CSC%" /nologo makeico.cs && makeico.exe && del makeico.cs makeico.exe
:: The app.ico AND Title icon.b64 can now be used by main compilation
"%CSC%" /nologo /target:winexe /win32icon:app.ico /resource:icon.b64 /platform:x86 /out:"%~dpn0.exe" "%~dpnx0"
:: It should now be safe to delete the temporary graphics
del app.ico icon.b64
REM IMPORTANT we must pause and exit here before NOTES
pause & exit /b
NOTES:
This CMD file is a working demonstration of SumatraPDF DDE [GetMousePos] which returns points.
On running the cmd in Windows 7+ it compiles an exe that can be used to measure page data.
Simply bind the exe to a shortcut in SumatraPDF settings. Like this:
ExternalViewers [
[
CommandLine = C:\path to your version\measure.exe
Name = &Measure
Key = m
ToolbarSvgIcon = <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="1" fill="#fd1" stroke-linecap="round" stroke-linejoin="round"><rect x="0" y="0" width="24" height="24" stroke="none"/><path d="M0 18h24 M0 18L17 1 M0.5 18v6 M8 18v3 M16 18v6 M23.5 18v3" stroke="blue"/><path d="M0 18 L21 18 A21 21 0 0 0 15 3 Z" fill="red" fill-opacity="0.4" stroke="green"/><text fill="black" stroke-width="0.25" font-size="8" font-family="sans-serif" x="7" y="16">45°</text></svg>
]
]
You can edit this file in MS Notepad and re-run with changes, but not while the current one is active !
For example rather than cartographic degrees orientation, if you wanted mariners bearings you can edit.
"\r\nDist: " + dist_u.ToFixed(3) + " " + unit + " Deg.: " + angleDeg.ToFixed(3) + "°" +
to
"\r\nDist: " + dist_u.ToFixed(3) + " " + unit + " Deg.: " + bearing.ToFixed(3) + "°" +
*/
using System; using System.Collections.Generic; using System.Drawing; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; using System.IO; using System.Reflection;
class MeasureForm : Form
{
// window dragging
[DllImport("user32.dll")] private static extern bool ReleaseCapture();
[DllImport("user32.dll")] private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
private const int WM_NCLBUTTONDOWN = 0xA1; private const int HTCAPTION = 0x2;
// DDE
private delegate IntPtr DdeCallback( int uType, int uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, IntPtr dwData1, IntPtr dwData2);
private static readonly DdeCallback ddeCallback = DdeCallbackProc;
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "DdeInitializeW")]
private static extern int DdeInitializeW(out IntPtr pidInst, DdeCallback pfnCallback, int afCmd, int ulRes);
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "DdeCreateStringHandleW")]
private static extern IntPtr DdeCreateStringHandleW(IntPtr idInst, string psz, int iCodePage);
[DllImport("user32.dll")] private static extern IntPtr DdeConnect(IntPtr idInst, IntPtr hszService, IntPtr hszTopic, IntPtr pCC);
[DllImport("user32.dll")]
private static extern IntPtr DdeClientTransaction( byte[] pData, int cbData, IntPtr hConv, IntPtr hszItem, int wFmt, int wType, int dwTimeout, out int pdwResult);
[DllImport("user32.dll")] private static extern int DdeGetData(IntPtr hData, byte[] pDst, int cbMax, int cbOff);
[DllImport("user32.dll")] private static extern bool DdeDisconnect(IntPtr hConv);
[DllImport("user32.dll")] private static extern bool DdeFreeStringHandle(IntPtr idInst, IntPtr hsz);
[DllImport("user32.dll")] private static extern bool DdeUninitialize(IntPtr idInst);
private static IntPtr DdeCallbackProc( int uType, int uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, IntPtr dwData1, IntPtr dwData2) { return IntPtr.Zero; }
// mouse hook
private const int WH_MOUSE_LL = 14; private const int WM_LBUTTONDOWN = 0x0201;
private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")] private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll")] private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll")] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")] private static extern IntPtr GetModuleHandle(string lpModuleName);
private IntPtr hookId = IntPtr.Zero; private LowLevelMouseProc hookCallback;
// UX
private TextBox ratioBox; private TextBox unitBox; private TextBox outputLabel;
private Button calibrateButton; private Button ptButton, pxButton, mmButton, cmButton, inButton; private Button armButton;
private CheckBox ypdfCheck;
// states
private bool calibrationMode = false; private bool armed = false; private int clickIndex = 0;
private struct Pt { public double x; public double y; }
private List<Pt> points = new List<Pt>();
public MeasureForm()
{
// READ the external icon.b64 convert and embed it replacing any internal methods
var asm = Assembly.GetExecutingAssembly(); Image icon; using (var s = asm.GetManifestResourceStream("icon.b64"))
using (var r = new StreamReader(s)) { byte[] png = Convert.FromBase64String(r.ReadToEnd()); using (var ms = new MemoryStream(png)) icon = Image.FromStream(ms); }
using (var bmp = new Bitmap(icon)) this.Icon = Icon.FromHandle(bmp.GetHicon());
this.FormBorderStyle = FormBorderStyle.None; this.TopMost = true;
this.BackColor = Color.FromArgb(34, 34, 34); this.ForeColor = Color.White; // this.Opacity = 0.70;
this.Size = new Size(256, 260); this.StartPosition = FormStartPosition.CenterScreen;
this.Font = new Font("Segoe UI", 12, FontStyle.Regular);
// top bar
Panel bar = new Panel { Height = 28, Dock = DockStyle.Top, BackColor = Color.FromArgb(50, 50, 50) };
Controls.Add(bar);
PictureBox pic = new PictureBox { Image = icon, Size = new Size(28, 28), Location = new Point(0, 0), BackColor = Color.FromArgb(255, 201, 15), SizeMode = PictureBoxSizeMode.CenterImage };
pic.MouseDown += (s, e) => {
if (e.Button == MouseButtons.Left) { ReleaseCapture(); SendMessage(this.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0); } };
bar.Controls.Add(pic);
Label title = new Label { Text = "Chart-o-graphic Measure", Left = 30, Top = 4, ForeColor = Color.White }; title.Width = 196;
title.MouseDown += (s, e) => {
if (e.Button == MouseButtons.Left) { ReleaseCapture(); SendMessage(this.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0); } };
// CLOSE BUTTON
Label close = new Label { Text = "X", Width = 28, Height = 28, Dock = DockStyle.Right, TextAlign = ContentAlignment.MiddleCenter, ForeColor = Color.White };
close.MouseEnter += (s, e) => close.BackColor = Color.FromArgb(200, 50, 50); close.MouseLeave += (s, e) => close.BackColor = Color.FromArgb(50, 50, 50);
close.Click += (s, e) => Close();
bar.Controls.Add(close);
int margin = 6;
int offsetY = 30;
// ratio row
Label ratioLabel = new Label();
ratioLabel.Text = "Ratio:";
ratioLabel.Location = new Point(10, margin + offsetY);
ratioLabel.AutoSize = true;
this.Controls.Add(ratioLabel);
ratioBox = new TextBox();
ratioBox.Text = "72";
ratioBox.Location = new Point(60, margin + offsetY);
ratioBox.Width = 95;
this.Controls.Add(ratioBox);
calibrateButton = new Button();
calibrateButton.Text = "Calibrate";
calibrateButton.Location = new Point(160, margin + offsetY - 1);
calibrateButton.Width = 85;
calibrateButton.Height = 30;
calibrateButton.Click += CalibrateClick;
this.Controls.Add(calibrateButton);
// unit row
Label unitLabel = new Label();
unitLabel.Text = "Unit:";
unitLabel.Location = new Point(10, 42 + offsetY);
unitLabel.AutoSize = true;
this.Controls.Add(unitLabel);
unitBox = new TextBox();
unitBox.Text = "pt";
unitBox.Location = new Point(60, 40 + offsetY);
unitBox.Width = 52;
this.Controls.Add(unitBox);
inButton = MakeUnitButton("in", 115, 40 + offsetY);
inButton.Height = 30;
ptButton = MakeUnitButton("pt", 160, 40 + offsetY);
ptButton.Height = 30;
mmButton = MakeUnitButton("mm", 200, 40 + offsetY);
mmButton.Font = new Font("Segoe UI", 11f);
mmButton.Width = 45;
mmButton.Height = 30;
// get points row
armButton = new Button();
armButton.Text = "Get point(s)";
armButton.Location = new Point(8, 74 + offsetY);
armButton.Width = 100;
armButton.Height = 30;
armButton.Click += ArmGetPos;
this.Controls.Add(armButton);
ypdfCheck = new CheckBox();
ypdfCheck.Text = "Y^";
ypdfCheck.Checked = true;
ypdfCheck.ForeColor = Color.White;
ypdfCheck.BackColor = Color.FromArgb(34, 34, 34);
ypdfCheck.Location = new Point(112, 78 + offsetY);
ypdfCheck.AutoSize = true;
this.Controls.Add(ypdfCheck);
pxButton = MakeUnitButton("px", 160, 74 + offsetY);
pxButton.Height = 30;
cmButton = MakeUnitButton("cm", 200, 74 + offsetY);
cmButton.Width = 45;
cmButton.Height = 30;
// output
outputLabel = new TextBox();
outputLabel.Location = new Point(10, 110 + offsetY);
outputLabel.Width = 235;
outputLabel.Height = 110;
outputLabel.Multiline = true;
outputLabel.ShortcutsEnabled = true;
outputLabel.TabStop = true;
outputLabel.Cursor = Cursors.IBeam;
outputLabel.BorderStyle = BorderStyle.FixedSingle;
outputLabel.BackColor = Color.FromArgb(20, 20, 20);
outputLabel.ForeColor = Color.White;
outputLabel.Font = new Font("Segoe UI", 12f);
this.Controls.Add(outputLabel);
outputLabel.Click += (s, e) =>
{
if (calibrationMode && !armed) { armed = true; outputLabel.Text = "Click 1st calibration point."; }
};
SetUnit("pt"); // default for user but in theory all ratios are based on 1:1 inches
ShowStartupText(); // default to the text used for get points
}
//end of form builder
// add button helper.. Hmm should it default different x,y,w,h may be better
private Button MakeUnitButton(string text, int x, int y)
{
Button b = new Button(); b.Text = text; b.Location = new Point(x, y); b.Width = 40; b.Height = 26;
b.Click += (s, e) => SetUnit(text); this.Controls.Add(b); return b;
}
private void ShowStartupText()
{
// Check DDE probe
string reply = DdeRequest("[GetFileState()]");
if (reply == null)
{
MessageBox.Show(
"SumatraPDF may be active but currently\n there is no DDE reply channel.\n" +
"This simply means current state of SumatraPDF\n" +
"does not provide a DDE context yet. You may be able to use\nDDE after making changes.",
"No DDE Channel", MessageBoxButtons.OK, MessageBoxIcon.Information
);
// Continue NOT exit
}
outputLabel.Text =
"To start use \"Get point(s)\" THEN\r\nClick document for 1st location" +
"\r\n To change units or ratio enter" +
"\r\n values above or click a units" +
"\r\n button, & Get point(s) again.";
}
// unit + ratio
private void SetUnit(string u)
{
unitBox.Text = u;
if (u == "px") ratioBox.Text = "96";
if (u == "pt") ratioBox.Text = "72";
if (u == "mm") ratioBox.Text = "25.4";
if (u == "cm") ratioBox.Text = "2.54";
if (u == "in") ratioBox.Text = "1";
}
private double ConvertPt(double pt)
{
double ratio;
if (!double.TryParse(ratioBox.Text, out ratio) || ratio == 0.000) ratio = 72.000;
return pt * (ratio / 72.000);
}
private void ArmGetPos(object sender, EventArgs e)
{
calibrationMode = false;
armed = true;
clickIndex = 0;
points.Clear();
ShowStartupText();
if (hookId != IntPtr.Zero) { UnhookWindowsHookEx(hookId); hookId = IntPtr.Zero; }
hookCallback = MouseHookCallback;
hookId = SetWindowsHookEx(WH_MOUSE_LL, hookCallback, GetModuleHandle(null), 0);
}
private void CalibrateClick(object sender, EventArgs e)
{
calibrationMode = true;
armed = false;
clickIndex = 0;
points.Clear();
outputLabel.Text =
"Calibrate: Step 1\r\n" +
"Change Unit: to known units.\r\n" +
"Enter known length in ratio.\r\n" +
"When done, click HERE to begin.";
if (hookId != IntPtr.Zero) { UnhookWindowsHookEx(hookId); hookId = IntPtr.Zero; }
hookCallback = MouseHookCallback;
hookId = SetWindowsHookEx(WH_MOUSE_LL, hookCallback, GetModuleHandle(null), 0);
}
private IntPtr MouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_LBUTTONDOWN)
{
if (!armed) return CallNextHookEx(hookId, nCode, wParam, lParam);
armed = false;
string reply = DdeRequest("[GetMousePos]");
if (string.IsNullOrEmpty(reply)) return CallNextHookEx(hookId, nCode, wParam, lParam);
string[] lines = reply.Replace("\0", "").Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
if (lines.Length < 3) return CallNextHookEx(hookId, nCode, wParam, lParam);
double x = 0.000; double y = 0.000; double ypdf = 0.000;
foreach (string line in lines)
{
string t = line.Trim();
if (t.StartsWith("x:")) double.TryParse(t.Substring(2).Trim(), out x);
else if (t.StartsWith("y:")) double.TryParse(t.Substring(2).Trim(), out y);
else if (t.StartsWith("ypdf:")) double.TryParse(t.Substring(5).Trim(), out ypdf);
}
double yUse = ypdfCheck.Checked ? ypdf : y;
Pt p = new Pt { x = x, y = yUse };
points.Add(p);
string unit = unitBox.Text;
// calibration mode
if (calibrationMode)
{
if (clickIndex == 0)
{
string ratioText = ratioBox.Text;
outputLabel.Text =
"Now click 2nd" +
"\r\npoint at " + ratioText + " " + unit + " from" +
"\r\nthe first point. To reset click" +
"\r\ncalibrate button again.";
clickIndex = 1;
armed = true;
return CallNextHookEx(hookId, nCode, wParam, lParam);
}
if (clickIndex == 1 && points.Count >= 2)
{
double dx = points[1].x - points[0].x;
double dy = points[1].y - points[0].y;
double raw = Math.Sqrt(dx * dx + dy * dy);
double guess;
if (double.TryParse(ratioBox.Text, out guess) && guess > 0)
{
double newRatio = (guess * 72.000) / raw;
ratioBox.Text = newRatio.ToFixed(6);
outputLabel.Text =
"Calibration complete.\r\n" +
"Measured: " + raw.ToFixed(4) + " units\r\n" +
"User says: " + guess.ToFixed(4) + " " + unit + "\r\n" +
"Scale: " + newRatio.ToFixed(6) + " " + unit + " per inch";
}
else
{
outputLabel.Text =
"No valid number entered.\r\nCalibration cancelled.";
}
calibrationMode = false;
clickIndex = 0;
points.Clear();
return CallNextHookEx(hookId, nCode, wParam, lParam);
}
}
// normal mode
if (clickIndex == 0)
{
double x1u = ConvertPt(p.x);
double y1u = ConvertPt(p.y);
outputLabel.Text =
"x1: " + x1u.ToFixed(4) + " y1: " + y1u.ToFixed(4) +
"\r\n\r\n For distance, angle or area\r\n click on document at\r\n second point.";
clickIndex = 1;
armed = true;
}
else if (clickIndex == 1 && points.Count >= 2)
{
Pt p1 = points[0]; Pt p2 = points[1];
double x1u = ConvertPt(p1.x); double y1u = ConvertPt(p1.y);
double x2u = ConvertPt(p2.x); double y2u = ConvertPt(p2.y);
double dx = p2.x - p1.x; double dy = p2.y - p1.y;
double dist = Math.Sqrt(dx * dx + dy * dy);
double dx_u = ConvertPt(dx); double dy_u = ConvertPt(dy);
double dist_u = ConvertPt(dist);
double angleRad = Math.Atan2(dy, dx);
double angleDeg = angleRad * (180.0 / Math.PI); if (angleDeg < 0) angleDeg += 360.0;
double bearing = 90.0 - angleDeg; if (bearing < 0) bearing += 360.0;
double area_u = Math.Abs(dx_u * dy_u);
outputLabel.Text =
"x1: " + x1u.ToFixed(4) + " y1: " + y1u.ToFixed(4) +
"\r\nx2: " + x2u.ToFixed(4) + " y2: " + y2u.ToFixed(4) +
"\r\ndx: " + dx_u.ToFixed(4) + " dy: " + dy_u.ToFixed(4) +
"\r\nDist: " + dist_u.ToFixed(3) + " " + unit + " Deg.: " + angleDeg.ToFixed(3) + "°" +
"\r\nArea: " + area_u.ToFixed(4) + " " + unit + "²";
clickIndex = 0; points.Clear();
}
}
return CallNextHookEx(hookId, nCode, wParam, lParam);
}
// DDE
private string DdeRequest(string item)
{
const int APPCLASS_STANDARD = 0x00000000;
const int APPCMD_CLIENTONLY = 0x00000010;
const int XTYP_REQUEST = 0x20B0;
const int CF_UNICODETEXT = 13;
const int TIMEOUT = 5000;
IntPtr inst;
int ret = DdeInitializeW(out inst, ddeCallback, APPCLASS_STANDARD | APPCMD_CLIENTONLY, 0);
if (ret != 0) return null;
IntPtr hszService = DdeCreateStringHandleW(inst, "SUMATRA", 1200);
IntPtr hszTopic = DdeCreateStringHandleW(inst, "control", 1200);
IntPtr hszItem = DdeCreateStringHandleW(inst, item, 1200);
IntPtr hConv = DdeConnect(inst, hszService, hszTopic, IntPtr.Zero);
if (hConv == IntPtr.Zero)
{
DdeFreeStringHandle(inst, hszService);
DdeFreeStringHandle(inst, hszTopic);
DdeFreeStringHandle(inst, hszItem);
DdeUninitialize(inst);
return null;
}
int result;
IntPtr hData = DdeClientTransaction(
null, 0, hConv, hszItem,
CF_UNICODETEXT, XTYP_REQUEST,
TIMEOUT, out result);
if (hData == IntPtr.Zero)
{
DdeDisconnect(hConv);
DdeFreeStringHandle(inst, hszService);
DdeFreeStringHandle(inst, hszTopic);
DdeFreeStringHandle(inst, hszItem);
DdeUninitialize(inst);
return null;
}
int size = DdeGetData(hData, null, 0, 0);
byte[] buffer = new byte[size];
DdeGetData(hData, buffer, size, 0);
string reply = Encoding.Unicode.GetString(buffer);
reply = reply.TrimEnd('\0');
DdeDisconnect(hConv);
DdeFreeStringHandle(inst, hszService);
DdeFreeStringHandle(inst, hszTopic);
DdeFreeStringHandle(inst, hszItem);
DdeUninitialize(inst);
return reply;
}
}
static class DoubleFormatExtensions
{
public static string ToFixed(this double value, int decimals)
{
return value.ToString("F" + decimals);
}
}
class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MeasureForm());
}
}